Today,逛segmentfault的时候,看到有很多大神手写了深拷贝,记得也有大厂把这个当作题目,那我也来学习一下吧~
在此之前,我都是直接JSON.parse(JSON.stringfy())实现深拷贝。有时写业务时,遇到一些错误使用深拷贝就能解决。有一段时间,我竟然认为"...[]"这种就是对数组进行深拷贝了.....总之这块知识掌握得不够。
开始撸码!首先了解下,浅拷贝和深拷贝的含义。
以前的认知 | 现在的认知 |
---|---|
浅拷贝是直接赋值,不仅把值赋了,引用也相同——比如浅拷贝对象a到b,如果修改a里面的某个属性,b里对应的属性也会变更。 | 浅拷贝只能赋值最外一层,比如对象obj1={a:{a:{a:1}}},浅拷贝给obj2,修改obj1.b=2,再修改obj1.b.b=2;输出ob2为{a:{a:{a:1}},b:2}。obj2.b不是对象。 |
可以使用JSON.parse(JSON.stringfy())实现,而且基本够用写业务了呢,或者依赖lodash的_.cloneDeep()实现。 | 手写 |
没遇到转换失败的场景 | JSON.parse(JSON.stringfy())有缺点:1、undefined转换会直接消失;2、RegExp转换后变成{};3、NaN、+-Infinity转换后变成null;4、环引用会报错:TypeError: Converting circular structure to JSON |
我跟着https://segmentfault.com/a/11...这篇文章,一起手动写了下代码。写到最后都挺好,也实现了功能。在此过程中,学到了很多知识——
1、基础类型和引用类型分别有哪些?
基础类型:String Number Boolean Undefined Null
可循环的引用类型:Array Object Map Set
不可循环的引用类型:Symbol RegExp Function
2、判断类型的方法?
Object.prototype.toString.call(x)
3、递归写法?
function digui(sum) {
if(sum==0){
return 0;
}
return sum+digui(sum-1)
}
console.log(digui(6));
4、拷贝Symbol的方法?
Object(Symbol.prototype.valueOf.call(x));
5、for...in 和for...of的区别
for...in | for...of |
---|---|
推荐遍历对象——对象的键名,遍历数组——数组的索引(即键名);枚举属性,包括原型 | 推荐遍历数组——元素值;遍历对象,不包含可枚举属性 |
6、环引用是什么意思?如何解决它的深拷贝?
环引用就是value是变量自己,可以使用Map解决。具体解决见文章里所说。
另外还学习到了如何拷贝函数、Symbol、RegExp,Map、Set。不过,也有不理解的地方:
思考1、76行代码(见图一)是判断引用数据类型,通过输出,看出来返回的temp是origin的相同类型的空值,比如origin是{key:origin},那temp就是{}。这时map已经保存了key为origin,value为{}的值——这不就不对了嘛?环引用就变成了{}(见图三)。后来,我把origin只保留了一个换引用然后debugger一步步走下来,发现递归走了两次,第一次执行了map.set,走到第100行代码(见图四),把换引用作为key又循环了一次,因为第一次的map.set,所以79行代码(见图一)的if判断可以通过,从而设置了环引用的值。
图一
图二
图三
图四
7、Map如何获取值和赋值?
var map= new Map();
map.get('key');
map.set('key','value');
8、Set是什么?Set如何获取值和赋值?
set类似数组,但值唯一
var set = new Set()
set.add(1)
set.delete(2)
set.has(1) ---> true/false
set.size
set.clear()
9、如何拷贝函数?
思路:函数toString,使用正则找到函数体和参数,返回new Function(参数,函数体)
10、如何拷贝RegExp?
这个没看懂,等后面看懂了补充把
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。