今天话不多说,直接来手写一个深拷贝吧。
逻辑分析
首先我们来梳理一下这个深拷贝的逻辑:
-
深拷贝存在的意义是由于引用数据类型无法直接进行拷贝,是基于基本数据类型和引用数据类型的内存地址不同而引申出来的一个方法
-
所以我们首先判断传入对象的 数据类型
-
如果是基本数据类型,或者是基本数据类型的null,则返回数据本身
-
如果是object,则只考虑两种情况,一个是[],另一个是{}
-
遍历
-
key如果是基本数据类型,直接拷贝
-
如果是引用数据类型,则继续递归,直到能够直接拷贝,遍历到底层其实都是基本数据类型
代码分析
function deepClone(source) {
//基本数据类型的拷贝
if (typeof source !== 'object' || source == null) {
return source;
}
//引用数据类型的拷贝,需要递归遍历
const target = Array.isArray(source) ? [] : {};
for (const key in source) { //这里用这种循环是为了考虑到{}格式的数据
if (Object.prototype.hasOwnProperty.call(source, key)) { //如果source上找得到key,则往下执行
console.log('this',this)//在浏览器中,这个this指向window,不管在哪里,这里的this指向全局
if (typeof source[key] === 'object' && source[key] !== null) { //如果遍历到的属性值还是引用数据类型或者基本数据类型中的null
target[key] = deepClone(source[key]); //就继续递归遍历
} else { //直到基本数据类型或者null,就直接拷贝
target[key] = source[key];
}
}
}
return target;
}
然后我们来分析这一行代码Object.prototype.hasOwnProperty.call(source, key)
call函数可以改变this指向,他的第一个参数就代表了它将要指向的合并对象,也就是说,这行代码的执行逻辑是,它将会指向source,然后从source上去找key
Object.prototype.hasOwnProperty()
这个方法用来判断对象本身上是否存在对应的属性(而不是继承自原型链),返回一个布尔值
比如:
const object1 = {};
object1.property1 = 42;
console.log(object1.hasOwnProperty('property1'));
// expected output: true
而且我还注意到一个问题是,只要在这个函数中,无论console.log('this',this)放在哪里,都是指向全局,如果放在浏览器环境中去执行,那就是指向window
这是为什么呢,感觉面试官也很喜欢考察这个,这里面的this指向
我记得,大家对this指向的描述认同度比较高的是这个
this会指向函数运行时的执行上下文,deepClone是在全局window下调用的,那么自然而然会指向window
最近面试面出点经验来了,感觉人也面麻了。
很多现在面试的套路是,给你一段代码,或者让你自己手写一个东西,然后问你这段代码里的细节,特别是this指向问题,是面试官很喜欢考察的点,大家可以多多思考,也欢迎在评论区提出你自己的想法和理解!