本文目录:
- 1.手写reduce
- 2.手写map
- 3.手写filter
- 4.手写防抖
- 5.手写节流
- 6.实现flat
- 7.手写深拷贝
- 8.手写Promise
1.手写reduce
reduce 接收两个参数:
第一个参数是在每一项上调用的函数
该函数接收 4 个参数:
1.前一个值 prev
2.当前值 cur
3.项的索引 index
4.数组对象 array
第二个可选参数是作为归并基础的初始值
reduce 方法返回一个最终的值
Array.prototype.myReduce = function (fn, initialValue) {
console.log(this)
let acc = initialValue || this[0];
const startIndex = initialValue ? 0 : 1;
for (let i = startIndex; i < this.length; i++) {
acc = fn(acc, this[i], i, this);
}
return acc;
};
2.手写map
map 迭代方法接收两个参数:
- 对每一项执行的函数
- 该函数接收三个参数:
- 数组项的值
- 数组项的下标
- 数组对象本身
- 该函数接收三个参数:
- 指定 this 的作用域对象
map 方法返回每次函数调用结果组成的数组。
代码表示:
arr.map(function(item, index, arr) {}, this);
代码实现:
Array.prototype.myMap = function (fn, context) {
const array = this
context = Object(context) || global // 严格模式下
const resultArr = []
for (let i = 0; i < array.length; i++) {
let item = array[i]
result = fn.call(context, item, i, array)
resultArr.push(result)
}
return resultArr
};
注: 严格模式下,context 为 null 或 undefined 时 Object(context) 返回空对象,不会被赋值为global
3.手写filter
filter 方法接收两个参数:
- 对每一项执行的函数
- 该函数接收三个参数:
- 数组项的值
- 数组项的下标
- 数组对象本身
- 该函数接收三个参数:
- 指定 this 的作用域对象
filter 方法返回 执行结果为true的项组成的数组。
代码表示:
arr.filter(function(item, index, arr){}, context)
代码实现:
Array.prototype.fakeFilter = function fakeFilter(fn, context) {
if (typeof fn !== "function") {
throw new TypeError(`${fn} is not a function`);
}
let arr = this;
let temp = [];
for (let i = 0; i < arr.length; i++) {
let result = fn.call(context, arr[i], i, arr);
if (result) temp.push(arr[i]);
}
return temp;
};
4.手写防抖
函数防抖 是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。
通俗点讲就是=>你点的再快都没用(新的点击会清除上一次的点击效果),要特定时间之后才会触发
代码示例
let time2;
document.getElementById('防抖').onclick =
function () {
clearTimeout(time2);
time2=setTimeout(function () {
//do something***
},2000);
};
5.手写节流
函数节流 是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。
通俗点讲就是=>点击就可以触发事件,但是特定时间内只会触发一次,(技能冷却)
代码示例
let bool=true;
document.getElementById('节流').onclick = function () {
if(bool){
//do something***
bool=false;
setTimeout(()=>{
bool=true
},2000)
}
}
6.实现flat
flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
定义一个数组ary以及将ary转换为字符串str
let ary = [1, [2, [3, [4, 5]]], 6];
let str = JSON.stringify(ary);
直接调用flat方法实现多维数组的扁平化
let ary_flat = ary.flat(Infinity);
如果不用flat,第一种实现方法
ary = str.replace(/(\[\]))/g, '').split(',');
第二种实现方法
str = str.replace(/(\[\]))/g, '');
str = '[' + str + ']';
ary = JSON.parse(str);
第三种实现方法
let result = [];
let myFlat = function (ary) {
for (let i = 0; i < ary.length; i++) {
let item = ary[i];
if (Array.isArray(ary[i])) {
myFlat(item);
} else {
result.push(item);
}
}
return result;
};
第四种实现方法
while (ary.some(Array.isArray)) {
console.log('xxx')
ary = [].concat(...ary);
}
7.手写深拷贝
我们在使用深拷贝的时候,一定要弄清楚我们对深拷贝的要求程度:是仅“深”拷贝第一层级的对象属性或数组元素,还是递归拷贝所有层级的对象属性和数组元素
怎么检验深拷贝成功
改变任意一个新对象/数组中的属性/元素, 都不改变原对象/数组
方法一:深拷贝的最简单直接的方法:
乞丐版: JSON.parse(JSON.stringify(object)),缺点诸多(会忽略undefined、symbol、函数;不能解决循环引用;不能处理正则、new Date())
方法二:手动写递归
基础版(面试够用): 浅拷贝+递归 (只考虑了普通的 object和 array两种数据类型)
var array = [
{ number: 1 },
{ number: 2 },
{ number: 3 }
];
function copy (obj) {
var newobj = obj.constructor === Array ? [] : {};
if(typeof obj !== 'object'){
return;
}
for(var i in obj){
newobj[i] = typeof obj[i] === 'object' ?
copy(obj[i]) : obj[i];
}
return newobj
}
var copyArray = copy(array)
copyArray[0].number = 100;
console.log(array); // [{number: 1}, { number: 2 }, { number: 3 }]
console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]
上面这两个深拷贝的方法最大缺点是都会丢失函数Function、正则表达式等特殊类型。
8.手写Promise
简单版本,理解其原理
function myPromise(executor) {
var _this = this;
this.onFulfilled = []; //成功的回调
this.onRejected = []; //失败的回调
this.state = "PENDING"; //状态
this.value = undefined; //成功结果
this.reason = undefined; //失败原因
function resolve(value) {
if (_this.state === "PENDING") {
_this.state = "FULFILLED";
_this.value = value;
_this.onFulfilled.forEach((fn) => fn(value));
}
}
function reject(reason) {
if (_this.state === "PENDING") {
_this.state = "REJECTED";
_this.reason = reason;
_this.onRejected.forEach((fn) => fn(reason));
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// 定义链式调用的then方法
myPromise.prototype.then = function (onFullfilled, onRejected) {
if (this.state === "FULFILLED") {
typeof onFulfilled === "function" && onFulfilled(this.value);
}
if (this.state === "REJECTED") {
typeof onRejected === "function" && onRejected(this.reason);
}
if (this.state === "PENDING") {
typeof onFulfilled === "function" &&
this.onFulfilled.push(onFulfilled);
typeof onRejected === "function" && this.onRejected.push(onRejected);
}
};
var myP = new myPromise((resolve, reject) => {
console.log("执行");
setTimeout(() => {
reject(3);
}, 1000);
});
myP.then(
(res) => {
console.log(res);
},
(err) => {
console.log(err);
}
);
