关于javascript:js深拷贝和浅拷贝及其实现方式

浅拷贝

var m = { a: 10, b: 20 }
var n = m; 
n.a = 15; // 这时m.a的值是多少

m.a会输入15,因为这是浅拷贝,n和m指向的是同一个堆,对象复制只是复制的对象的援用。

深拷贝

var m = { a: 10, b: 20 } 
var n = {a:m.a,b:m.b}; 
n.a = 15;

深拷贝和下面浅拷贝不同,就是彻底copy一个对象,而不是copy对象的援用。
这次,咱们再来输入m.a ,发现m.a的值还是10,并没有扭转,m对象和n对象是尽管所有的值都是一样的,然而在堆外面,对应的不是同一个了,这个就是深拷贝。

深拷贝和浅拷贝

深拷贝和浅拷贝的示意图大抵如下:

浅拷贝只复制指向某个对象的指针,而不复制对象自身,新旧对象还是共享同一块内存。但深拷贝会另外发明一个截然不同的对象,新对象跟原对象不共享内存,批改新对象不会改到原对象。

浅拷贝的实现形式

1、能够通过简略的赋值实现

function simpleClone(initalObj) { 
    var obj = {}; 
    for ( var i in initalObj) { 
        obj[i] = initalObj[i]; 
    } 
    return obj; 
} 
var obj = { 
    a: "hello", 
    b:{ a: "world", b: 21 }, 
    c:["Bob", "Tom", "Jenny"], 
    d:function() { 
        alert("hello world"); 
    } 
} 
var cloneObj = simpleClone(obj); 
console.log(cloneObj.b); 
console.log(cloneObj.c); 
console.log(cloneObj.d); 
cloneObj.b.a = "changed"; 
cloneObj.c = [1, 2, 3]; 
cloneObj.d = function() { 
    alert("changed"); 
}; 
console.log(obj.b); 
console.log(obj.c); 
console.log(obj.d);

2、Object.assign()实现

Object.assign() 办法能够把任意多个的源对象本身的可枚举属性拷贝给指标对象,而后返回指标对象。然而 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的援用,而不是对象自身。

var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "changed";
console.log(obj.a.a); //  "changed"

留神:当object只有一层的时候,是深拷贝,例如如下:

var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = Object.assign({}, obj1);
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(obj2);
// { a: 10, b: 100, c: 30 }

3、函数库lodash的_.clone办法

该函数库也有提供_.clone用来做 Shallow Copy,前面咱们会再介绍利用这个库实现深拷贝。

var _ = require('lodash');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = _.clone(obj1);
console.log(obj1.b.f === obj2.b.f);// true

4、开展运算符…

开展运算符是一个 es6 / es2015个性,它提供了一种十分不便的形式来执行浅拷贝,这与 Object.assign ()的性能雷同。

let obj1 = { name: 'Kobe', address:{x:100,y:100}}
let obj2= {... obj1}
obj1.address.x = 200;
obj1.name = 'wade'
console.log('obj2',obj2) // obj2 { name: 'Kobe', address: { x: 200, y: 100 } }

5、Array.prototype.concat()

let arr = [1, 3, {
    username: 'kobe'
    }];
let arr2 = arr.concat();    
arr2[2].username = 'wade';
console.log(arr); //[ 1, 3, { username: 'wade' } ]

6、Array.prototype.slice()

let arr = [1, 3, {
    username: ' kobe'
    }];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr); // [ 1, 3, { username: 'wade' } ]

深拷贝的实现形式

1、办法一还是手动复制

和下面的举例一样,手动复制能够实现深拷贝。

2、对象只有一层的话能够应用下面的:Object.assign()函数

3、转成 JSON 再转回来

var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 10 } } <-- 沒被改到
console.log(obj2);
// { body: { a: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// false

用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。

能够封装如下函数

var cloneObj = function(obj){
    var str, newobj = obj.constructor === Array ? [] : {};
    if(typeof obj !== 'object'){
        return;
    } else if(window.JSON){
        str = JSON.stringify(obj), //系列化对象
        newobj = JSON.parse(str); //还原
    } else {
        for(var i in obj){
            newobj[i] = typeof obj[i] === 'object' ? 
            cloneObj(obj[i]) : obj[i]; 
        }
    }
    return newobj;
};

4、函数库lodash的_.cloneDeep办法

该函数库也有提供_.cloneDeep用来做 Deep Copy

var _ = require('lodash');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false

5、递归拷贝

function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    var prop = initalObj[i];        // 防止互相援用对象导致死循环,如initalObj.a = initalObj的状况
    if(prop === obj) {            
      continue;
    }        
    if (typeof prop === 'object') {
      obj[i] = (prop.constructor === Array) ? [] : {};            
      arguments.callee(prop, obj[i]);
    } else {
      obj[i] = prop;
    }
  }    
  return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);

6、应用Object.create()办法

间接应用var newObj = Object.create(oldObj),能够达到深拷贝的成果。

function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    var prop = initalObj[i];        // 防止互相援用对象导致死循环,如initalObj.a = initalObj的状况
    if(prop === obj) {            
      continue;
    }        
    if (typeof prop === 'object') {
      obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
    } else {
      obj[i] = prop;
    }
  }    
  return obj;
}

7、jquery

jquery 有提供一个$.extend能够用来做 Deep Copy。

var $ = require('jquery');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f);
// false

8、lodash

另外一个很热门的函数库lodash,也有提供_.cloneDeep用来做 Deep Copy。

var _ = require('lodash');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理