浅拷贝是指,一个新的对象对原始对象的属性值进行精确地拷贝,如果拷贝的是基本数据类型,拷贝的就是基本数据类型的值;如果拷贝的是引用数据类型,拷贝的就是内存地址。如果其中一个对象的引用内存地址发生改变,另一个对象也会发生变化。,object.assign 是 ES6 中 object 的一个方法,该方法可以用于 JS 对象的合并。我们可以使用它来实现浅拷贝。,该方法的参数 target 指的是目标对象,sources指的是源对象。使用形式如下:,使用示例:,这里通过 Object.assign 将 object2 和 object3 拷贝到了 target 对象中,下面来尝试将 object2 对象中的 b 属性中的d属性由 2 修改为 666:,可以看到,target的b属性值的d属性值变成了666,因为这个b的属性值是一个对象,它保存了该对象的内存地址,当原对象发生变化时,引用他的值也会发生变化。,注意:,如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性;,如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回;,因为null 和 undefined 不能转化为对象,所以第一个参数不能为null或 undefined,否则会报错;,它不会拷贝对象的继承属性,不会拷贝对象的不可枚举的属性,可以拷贝 Symbol 类型的属性。,实际上,Object.assign 会循环遍历原对象的可枚举属性,通过复制的方式将其赋值给目标对象的相应属性。,使用扩展运算符可以在构造字面量对象的时候,进行属性的拷贝。使用形式如下:,使用示例:,扩展运算符 和 object.assign 实现的浅拷贝的功能差不多,如果属性都是基本类型的值,使用扩展运算符进行浅拷贝会更加方便。,(1)Array.prototype.slice(),slice()方法是JavaScript数组方法,该方法可以从已有数组中返回选定的元素,不会改变原始数组。使用方式如下:,该方法有两个参数,两个参数都可选:,如果两个参数都不写,就可以实现一个数组的浅拷贝:,slice 方法不会修改原数组,只会返回一个浅拷贝了原数组中的元素的一个新数组。原数组的元素会按照下述规则拷贝:,如果向两个数组任一中添加了新元素,则另一个不会受到影响。,(2)Array.prototype.concat(),concat() 方法用于合并两个或多个数组,此方法不会更改原始数组,而是返回一个新数组。使用方式如下:,该方法的参数arrayX是一个数组或值,将被合并到arrayObject数组中。如果省略了所有 arrayX 参数,则 concat 会返回调用此方法的现存数组的一个浅拷贝:,concat方法创建一个新的数组,它由被调用的对象中的元素组成,每个参数的顺序依次是该参数的元素(参数是数组)或参数本身(参数不是数组)。它不会递归到嵌套数组参数中。,concat方法不会改变this或任何作为参数提供的数组,而是返回一个浅拷贝,它包含与原始数组相结合的相同元素的副本。原始数组的元素将复制到新数组中,如下所示:,根据以上对浅拷贝的理解,实现浅拷贝的思路:,代码实现:,这里用到了 hasOwnProperty() 方法,该方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性。所有继承了 Object 的对象都会继承到 hasOwnProperty() 方法。这个方法可以用来检测一个对象是否是自身属性。,可以看到,所有的浅拷贝都只能拷贝一层对象。如果存在对象的嵌套,那么浅拷贝就无能为力了。深拷贝就是为了解决这个问题而生的,它能解决多层对象嵌套问题,彻底实现拷贝。,深拷贝是指,对于简单数据类型直接拷贝他的值,对于引用数据类型,在堆内存中开辟一块内存用于存放复制的对象,并把原有的对象类型数据拷贝过来,这两个对象相互独立,属于两个不同的内存地址,修改其中一个,另一个不会发生改变。,JSON.parse(JSON.stringify(obj))是比较常用的深拷贝方法之一,它的原理就是利用JSON.stringify 将JavaScript对象序列化成为JSON字符串),并将对象里面的内容转换成字符串,再使用JSON.parse来反序列化,将字符串生成一个新的JavaScript对象。,这个方法是目前我在公司项目开发中使用最多的深拷贝的方法,也是最简单的方法。,使用示例:,这个方法虽然简单粗暴,但也存在一些问题,在使用该方法时需要注意:,在日常开发中,上述几种情况一般很少出现,所以这种方法基本可以满足日常的开发需求。如果需要拷贝的对象中存在上述情况,还是要考虑使用下面的几种方法。,该函数库也有提供_.cloneDeep用来做深拷贝,可以直接引入并使用:,这里附上lodash中深拷贝的源代码供大家学习:,(1)基础递归实现,实现深拷贝的思路就是,使用for in来遍历传入参数的属性值,如果值是基本类型就直接复制,如果是引用类型就进行递归调用该函数,实现代码如下:,这样虽然实现了深拷贝,但也存在一些问题:,(2)优化递归实现,上面只是实现了一个基础版的深拷贝,对于上面存在的几个问题,可以尝试去解决一下:,代码实现:,可以使用以下数据进行测试:,运行结果如下:,
,可以看到,这样基本就实现了多数数据类型的深拷贝,不过也还存在一些缺陷,比如Map和Set结构在这个方法中无法进行拷贝,可以自己实现一下。
© 版权声明
文章版权归作者所有,未经允许请勿转载。