前言
为了巩固提高javascript的水平,我决定手写一些JavaScript中常见的方法,来达到我们熟悉js的目的。
手写深拷贝 ⛹⛹
我们知道,深拷贝其实就是在堆栈中新开辟一个空间,来克隆一个一摸一样的对象。
我们一般实现深拷贝会使用一下方法:
JSON.stringify(JSON.parse(obj))
通过这种方式我们会开辟一个空间,创建一个和obj一摸一样的对象。
那么我们如何手写一个深拷配的方法呢?
// 手写深拷贝
function deepClone(origin, target) {
// 首先创建一个变量默认为传入的target对象或者为空对象
var tar = target || {};
var toStr = Object.prototype.toString;
var arrayType = "[object Array]";
for (const k in origin) {
// 判断origin带有的属性k是继承来的还是创建的
if (origin.hasOwnProperty(k)) {
// 判断当前属性是对象和数组之一吗
if (typeof origin[k] == "object" && origin[k] != null) {
// 具体判断该属性是数组还是对象
tar[k] = toStr.call(origin[k]) == arrayType ? [] : {};
// 进行递归调用,将里面嵌套的对象或者数组也进行拷贝
deepClone(origin[k], tar[k]);
} else {
tar[k] = origin[k];
}
}
}
return tar;
}
我来给大家讲解一些我的实现思路:
- 首先这个函数传入两个参数,一个要被克隆的原始对象(origin),一个克隆之后的对象(target)
- 然后在这个函数中创建变量tar保存target,或者创建一个空对象
- 接着使用for in 方法对传入的origin对象进行遍历,首先判断origin上的属性k是继承来的还是自己创建的。
- 然后就对传入的对象的属性进行判断,首先使用type of进行简单的一个过滤判断(因为typeof只能粗略的判断一下基本本数据类型,不能更细致的区分),这里还要注意一下因为null类型也会配判断为object类型,所以我们还要排除一下null类型。
- 然后我们就要区分是对象还是数组了,在这里就需要考察对JavaScript的掌握程度了,我们知道因为基本类型中的object类型下面又可以细分为function,array,object,null等类型,要想具体的将他们给区分开就需要使用Object.prototype.toString中的call方法。
Object.prototype.toString.call(null); // “[object Null]” Object.prototype.toString.call(undefined); // “[object Undefined]” Object.prototype.toString.call(“abc”);// “[object String]” Object.prototype.toString.call(123);// “[object Number]” Object.prototype.toString.call(true);// “[object Boolean]”
这个可以清楚的分清楚所属的类型
- 所以我们可以通过这行代码来实现针对属性创建一个对象或者数组
tar[k] = toStr.call(origin[k]) == arrayType ? [] : {};
- 然后通过递归调用传入origin[k],tar[k],因为此时我们已经可以判断出来origin[k]是对象还是数组了,所以我们传入originp[k]就相当于是最初传入origin一样,传入tar[k],就像是传入target一样。
- 然后我们就要给递归一个出口:如果origin中的这个属性不是对象或者数组,那么我们就可以直接进行赋值操作,将origin[k]赋值给tar[k]。
- 最后我们将tar给return出去就可以,这样就实现了深拷贝。
手写forEach🛰🛰
废话不多说,先上源代码:
// 手写ForEach方法
Array.prototype.myForEach = function (cb) {
// 这里的this指的是调用这个方法的数组
var _arr = this;
var len = _arr.length;
// 这个obj2代表的是myForEach的第二个参数,这个参数可以改变this指向
var arr2 = arguments[1] || window;
// 根据数组的长度来决定cb这个函数执行的次数
for (var i = 0; i < len; i++) {
// apply可以更改this的指向
cb.apply(arr2, [_arr[i], i, _arr]);
}
};
思路:
首先我们要给Array.prototype绑定一个myForeEach方法,这个函数接收一个参数是一个回调函数, 然后我们在函数内部声明一个变量用来保存this,首先我们要在这里明白的是这里的this指向调用他的数组,然后这个arr2保存的是可能会传递的二个参数,这个参数可以更改this的指向。然后我们就可以实现遍历,其实本质就是一个for循环,然后根据数组的长度来调用回调函数。其中apply方法可以更改this的指向,然后第二个参数传递一个数组,里面的参数就是要传入回调函数的参数。
实例数据:
let arr = [
{
name: "张三",
age: 21,
},
{
name: "李四",
age: 24,
},
{
name: "王五",
age: 31,
},
];
实际使用
arr.myForEach(function (item, index, arr) {
// console.log(this);
console.log(item);
});
手写map方法 🐤🐤
先上源代码:
// 手写map方法
Array.prototype.myMap = function (cb) {
var _arr = this;
var len = _arr.length;
var arr2 = arguments[1] || window;
var newArr = [];
for (var i = 0; i < len; i++) {
// 因为map要返回一个新的数组,所以里面如果有对象这种引用类型,需要进行深拷贝
var _item = deepClone(_arr[i]);
// cb调用apply方法需要在cb中进行return,这也是map方法所需要做的
newArr.push(cb.apply(arr2, [_item, i, _arr]));
}
return newArr;
};
注意这个方法前三行代码和forEach是相同的不再赘述,我们主要说不同的地方,首先我们还是使用for循环但是注意这里我们使用了前面写好的深拷贝函数deepClone克隆数组中的元素,然后创建一个新的空数组,将回调函数中return的元素push到newArr中,最后再将newArr给return出去,这样就完成了myMap方法
实际使用:
let newArr = arr.myMap(function (item, index, arr) {
item.age += 100;
return item;
});
console.log(newArr);
这里的arr和示例数据相同,我们可以查看一下返回值:
手写filter方法 🥇🥇
我们知道过滤在我们实习的很多项目中都会使用到,可以用来筛选有用的数据,所以我们这次就手写一个
废话不多说,先上代码:
// 手写filter方法
Array.prototype.myFilter = function (cb) {
var _arr = this;
var len = _arr.length;
var arr2 = arguments[1] || window;
var newArr = [];
var _item;
for (var i = 0; i < len; i++) {
_item = deepClone(_arr[i]);
cb.apply(arr2, [_item, i, _arr]) ? newArr.push(_item) : "";
}
return newArr;
};
思路:
这个代码实现上面其实和之前map的实现方式有一点像,但略有不同,首先相同的是我们要对数组中的元素进行深拷贝,因为涉及对象这个引用类型,为了防止修改原对象就进行深拷贝,我们知道filter方法返回值其实是符合条件的值,所以可以进行判断如果这个值满足要求,那就将他push到newArr中。
实际使用:
let newArr2 = arr.myFilter(function (item, index, arr) {
return item.age > 25;
});
console.log(newArr2);
总结 🎃🎃
以上就是暂时手写出来的数组方法,后续还会继续更新手写数组方法的内容,通过手写方法可以是我们更加熟悉JavaScript的语法,提升js的基础
原文链接:https://juejin.cn/post/7234058546572607548 作者:前端随想