JS对象的深拷贝和浅拷贝的总结_蓝戒的博客


JS对象的深拷贝和浅拷贝的总结

对象拷贝首推简单有效的方法:JSON.stringfy()JSON.parse()即可搞定。但是这种简单粗暴的方法有其局限性。当值为undefined、function、symbol 会在转换过程中被忽略...所以,对象值有这三种的话用这种方法会导致属性丢失。

利用window.JSON的方法做深拷贝存在2个缺点:

如果你的对象里有函数,函数无法被拷贝下来

无法拷贝copyObj对象原型链上的属性和方法

弄懂深拷贝的相关问题需要的前置知识:

理解JS里的引用类型和值类型的区别,知道Obj存储的只是引用

对原型链有基本了解

基本类型:undefinednullBooleannumberstring。变量直接按指存放在栈区内,可以直接访问,所以我们平时把字符串、数字的值赋值给新变量,相当于把值完全复制过去,新变量的改变不会影响旧变量。

引用类型:存放在堆区的对象,变量在栈区中保存的是一个指针地址。

理解对象引用、深拷贝、浅拷贝的区别

对象引用:

对象引用容易理解,直接赋值,修改复制后的数组,原对象会随之改变。

JS中,一般的=号传递的都是对象/数组的引用。

//对象引用

var boy = {

    age:18

    }

var girl = boy;

console.log(boy === girl);//true

girl.age = 20;

console.log(boy.age);//20

理解:使用“=”进行赋值,girlboy指向了同一内容地址,修改一个,另一个也会修改。

浅拷贝和深拷贝的理解:

只有对象里嵌套对象的情况下,才会根据需求讨论,我们要深拷贝还是浅拷贝。

比如下面这种对象

var obj1 = {

    name: 'ziwei',

    arr : [1,2,3]

}

// 调用objectCopy()拷贝函数后,obj2拷贝obj1所有的属性。但是obj2.arr和obj1.arr是不同的引用,并且指向同一个内存空间。

var obj2 = objectCopy ( obj1 , {})

 console.log( obj1 !== obj2 )  // true    无论哪种拷贝,obj1和obj2一定都是2个不同的对象(内存空间不同)

 console.log( obj2.arr === obj1.arr )   // true   他们2个对象里arr的引用,指向【相同的】内存空间

2个obj经过拷贝后,虽然他们属性相同,也的确是不同的对象,但他们内部的obj都是指向同一个内存空间,这种我们叫浅拷贝

调用deepCopy()拷贝函数后,obj2拷贝obj1所有的属性,而且obj2.arrobj1.arr是指向不同的内存空间。

2个obj2除了拷贝了一样的属性,没有任何其他关联。

var obj2 = deepCopy( obj1 , {})

 console.log( obj1 !== obj2 )     // true    无论哪种拷贝,obj1和obj2一定都是2个不同的对象(内存空间不同)

 console.log( obj2.arr === obj1.arr )     // false   他们2个对象里arr的引用,指向【不同的】内存空间

因此这种 2个obj经过拷贝后,除了拷贝下来相同的属性之外,没有任何其他关联的2个对象,这种我们叫深拷贝

1)对象遍历拷贝(浅拷贝)

var obj = {

  name: '拷贝',

  class: '类别'

}

 

function copy (obj) {

   let newObj = {};

     for (let item in obj ){

       newObj[item] = obj

     }

     return newObj;

}

 

var copyObj = copy(obj);

copyObj.name = '拷贝';

console.log(obj); // {name: "拷贝", job: "类别"}

console.log(copyObj); // {name: "拷贝", job: Object}

no,这不是我想要的。

2ES6Object.assign 对象拷贝(浅拷贝)

var obj = {

  name: '拷贝',

  class: '我是es6方法'

}

var copyObj = Object.assign({}, obj);

copyObj.name = '拷贝';

console.log(obj);   // {name: "拷贝", class: "我是es6方法"}

console.log(copyObj);  // {name: "拷贝", class: "我是es6方法"}

Object.assign:用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),并返回合并后的target

用法: Object.assign(target, source1, source2);  所以 copyObj = Object.assign({}, obj);  这段代码将会把obj中的一级属性都拷贝到 {}中,然后将其返回赋给copyObj

no,有缺陷。

3ES6扩展运算符(...)(浅拷贝)

var obj = {

    name: '拷贝',

   class: '我是es6方法'

}

var copyObj = { ...obj }

console.log(obj);   // {name: "拷贝", class: "我是es6方法"}

console.log(copyObj);  // {name: "拷贝", class: "我是es6方法"}

扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。

no,不完美。

4JSON.parse()与JSON.stringify()(深拷贝-JSON对象

//纯数据json对象的深度克隆

function deepClone(obj) {

     return JSON.parse(JSON.stringify(obj));

}

理解:通过对象符串化和字符串对象化进行对象的拷贝。此方法只使用与纯JSON对象的深拷贝

5)对象深拷贝函数方法封装(深拷贝)

var clone = function (obj) {

    if(obj === null) return null

    if(typeof obj !== 'object') return obj;

    if(obj.constructor===Date) return new Date(obj);

    if(obj.constructor === RegExp) return new RegExp(obj);

    var newObj = new obj.constructor ();  //保持继承链

    for (var key in obj) {

        if (obj.hasOwnProperty(key)) {   //不遍历其原型链上的属性

            var val = obj[key];

            newObj[key] = typeof val === 'object' ? arguments.callee(val) : val; // 使用arguments.callee解除与函数名的耦合

        }

    } 

    return newObj; 

};

理解深拷贝实现:

1)、用 new
obj.constructor ()
构造函数新建一个空的对象,可以保持原形链的继承;

2)、用obj.hasOwnProperty(key)来判断属性是否来自原型链上,因为for..in..也会遍历其原型链上的可枚举属性。

3)、函数用到递归算法,在函数有名字,而且名字以后也不会变的情况下,这样定义没有问题。但问题是这个函数的执行与函数名 factorial 紧紧耦合在了一起。为了消除这种紧密耦合的现象,需要使用 arguments.callee

深拷贝注意点:

1. 不仅拷贝第一层级,还能够拷贝数组或对象所有层级的各项值

2. 不是单独针对数组或对象,而是能够通用于数组,对象和其他复杂的JSON形式的对象

3. 检验深拷贝成功的方法就是:改变任意一个新对象/数组中的属性/元素,都不改变原对象/数组

深拷贝和浅拷贝图解

存在大量深拷贝需求的代码——immutable提供的解决方案

实际上,即使我们知道了如何在各种情况下进行深拷贝,我们也仍然面临一些问题: 深拷贝实际上是很消耗性能的。所以,当你的项目里有大量深拷贝需求的时候,性能就可能形成了一个制约的瓶颈了。

 

immutable的作用:

通过immutable引入的一套API,实现:

 

1.在改变新的数组(对象)的时候,不改变原数组(对象)

2.在大量深拷贝操作中显著地减少性能消耗

 

先睹为快:

const { Map } = require('immutable')

const map1 = Map({ a: 1, b: 2, c: 3 })

const map2 = map1.set('b', 50)

map1.get('b') // 2

map2.get('b') // 50

深拷贝和浅拷贝的实现方式

浅拷贝的实现:

浅拷贝比较简单,就是用for in 循环赋值。

深拷贝的实现:

深拷贝,就是遍历那个被拷贝的对象

判断对象里每一项的数据类型

如果不是对象类型,就直接赋值,如果是对象类型,就再次调用deepCopy,递归的去赋值。

本文固定链接: http://www.webzsky.com/?p=1361 | 蓝戒的博客

cywcd
该日志由 cywcd 于2019年02月20日发表在 javascript 分类下,
原创文章转载请注明: JS对象的深拷贝和浅拷贝的总结 | 蓝戒的博客
关键字: ,

报歉!评论已关闭.

来自的朋友,欢迎您 点击这里 订阅我的博客 o(∩_∩)o~~~
×