转载: https://lixuguang.github.io/2020/01/02/FE-guide-new/
作者: 李旭光
拷贝
1 | let a = { |
从上述例子中我们可以发现,如果给一个变量赋值一个对象,那么两者的值会是同一个引用,其中一方改变,另一方也会相应改变。
通常在开发中我们不希望出现这样的问题,我们可以使用浅拷贝来解决这个问题。
浅拷贝
Object.assign
首先可以通过 Object.assign
来解决这个问题。
1 | // js代码 |
展开运算
当然我们也可以通过展开运算符(…)来解决
1 | let a = { |
Array
1 | arr.slice() |
通常浅拷贝就能解决大部分问题了,但是当我们遇到如下情况就需要使用到深拷贝了
1 | // js代码 |
浅拷贝只解决了第一层的问题,如果接下去的值中还有对象的话,那么就又回到刚开始的话题了,两者享有相同的引用。要解决这个问题,我们需要引入深拷贝。
深拷贝
这个问题通常可以通过 JSON.parse(JSON.stringify(object))
来解决,这也是最好用最简单的方法,俗称乞丐版。
乞丐版
1 | // js代码 |
但是该方法也是有局限性的:
- 会忽略 undefined
- 会忽略 symbol
- 不能序列化函数
- 不能解决循环引用的对象
举个栗子:
1 | // js代码 |
如果你有这么一个循环引用对象,你会发现你不能通过该方法深拷贝
在遇到函数、 undefined
或者 symbol
的时候,该对象也不能正常的序列化
1 | // js代码 |
你会发现在上述情况中,该方法会忽略掉函数和 undefined 。
但是在通常情况下,复杂数据都是可以序列化的,所以这个函数可以解决大部分问题,并且该函数是内置函数中处理深拷贝性能最快的。
那么是否可以解决函数和循环引用的问题呢?答案是肯定可以解决,接下来是基础版本的改造
基础版本
1 | function myClone(target){ |
写到这里已经可以帮助你应付一些面试官考察你的递归解决问题的能力。但是显然,这个深拷贝函数还是有一些问题。
这里只考虑了对象,没有考虑数组。
下面我们来做一个强化版的深拷贝,同时考虑对象、数组还有循环引用的问题。
强化版
1 | function myClone(target, map = new WeakMap()) { // WeakMap => 键对象弱引用, 可被垃圾回收 |
当然如果你的数据中含有以上三种情况下,可以使用 lodash
的深拷贝函数。
如果你所需拷贝的对象含有内置类型并且不包含函数,可以使用 MessageChannel
1 | // js代码 |
深拷贝实现方式2,可以深拷贝 function
、symbol
,等等,堪称终极版
终极版
1 | // js代码 |