一、克隆(拷贝)

1、浅拷贝

拷贝就是复制,就相当于把一个对象中的所有内容,复制一份给另一个对象(直接复制)。或者说就是把一个对象的地址给了另一个对象,他们指向相同,两个对象之间有共同的属性或者方法,都可以使用

var obj = {
    name: "James",
    age: 123,
    card: ["visa", "master"],
    wife: {
        name: "Wade",
        son: {
            name: "Anthony",
        }
    }
}
var obj1 = {};

function clone(target, origin) {
    for (var prop in origin) {
       target[prop] = origin[prop];
    }
}
clone(obj1,obj);  
console.log(obj);  
console.log(obj1);

打印结果如下:

javascript 对象克隆 js怎么克隆对象_javascript 对象克隆

延续上面代码,现在我们来改变拷贝后的obj1中的引用属性值:

obj1.card.push("银联");
obj1.wife.name = "Rose";

console.log(obj.card);       //  ["visa", "master", "银联"]
console.log(obj.wife.name);  // Rose

console.log(obj1.card);      // ["visa", "master", "银联"]
console.log(obj1.wife.name); // Rose

这里主要区分原始值和引用值的拷贝,原始值的拷贝,拷贝的是整个原始值,而引用值的拷贝,拷贝的是地址,如果是浅拷贝,一旦obj对象中含有引用值。拷贝之后,如果对obj1进行改变,则obj的值也会发生改变。

2、采用 instanceof 深拷贝

把一个对象中所有的属性或者方法,依次找到,并且在另一个对象中开辟相应的空间,依次的存储到另一个对象中,这句话可能不太好理解。简单点说,深拷贝之后,两个对象之间有共同的属性或者方法,但是它们各自指向各自空间(指向不同)。如果是深拷贝,一旦obj对象中含有引用值,拷贝之后,如果对obj1进行改变,则obj的值不会进行改变。

var obj = {
    name: "James",
    age: 123,
    card: ["visa", "master"],
    wife: {
        name: "Wade",
        son: {
            name: "Anthony",
        }
    }
}
var obj1 = {};
// 通过函数实现,把对象origin中的所有的数据深拷贝到对象target中
function clone(target, origin) {
    for (var prop in origin) {
        var item = origin[prop];        // 先获取origin对象中每个属性的值
        if (item instanceof Array) {    // 判断这个属性的值是不是数组
            target[prop] = [];          // 如果是数组,那么在target对象中添加一个新的属性,并且这个属性值也是数组
            clone(target[prop], item);  // 调用这个方法,把origin对象中这个数组的属性的值依次的复制到target对象的这个数组的属性中
        } else if (item instanceof Object) { // 判断这个属性的值是不是对象类型的
            target[prop] = {};          // 如果是对象类型的,那么在target对象中添加一个新的属性,是一个空对象
            clone(target[prop], item);  // 再次调用这个函数,把origin对象中的属性对象的值依次的复制到target对象的这个属性对象中
        } else {
            target[prop] = origin[prop] // 如果值是普通的数据,直接复制到target对象的这个属性中
        }
    }
}
clone(obj1, obj);
console.log(obj);
console.log(obj1);

打印结果如下:

javascript 对象克隆 js怎么克隆对象_javascript 对象克隆_02

延续上面代码,现在我们再来改变拷贝后的obj1中的引用属性值:

obj1.card.push("银联");
obj1.wife.name = "Rose";

console.log(obj.card);      // ["visa", "master"]
console.log(obj.wife.name); // Wade

console.log(obj1.card);     // ["visa", "master", "银联"]
console.log(obj1.wife.name);// Rose

上面深拷贝我们采用了 instanceof 来进行判断是数组还是对象,也可以换一种思考方式如采用Object.prototype.toString的方式:

3、采用 Object.prototype.toString.call() 深拷贝

var obj = {
    name: "James",
    age: 123,
    card: ["visa", "master"],
    wife: {
        name: "Wade",
        son: {
            name: "Anthony",
        }
    }
}
var obj1 = {};

// 思路:首先遍历对象for...in... 1、判断是不是原始值  2、判断是数组还是对象  3、建立相应的数组或对象
function deepClone(origin, target) {
    var target = target || {},
        toStr = Object.prototype.toString,    // 让代码更简介;
        arrStr = "[object Array]";            // 要进行比对的值
    for (var prop in origin) {
      if (origin.hasOwnProperty(prop)) {                                    // 判断是否属于自身上的数据,避免非原型上的数据
          if (origin[prop] !== "null" && typeof(origin[prop]) == "object") { // 判断如果原对象的属性值不等于null,并且类型为引用类型
              if (toStr.call(origin[prop]) == arrStr) {                     // 判断这个属性的值是不是数组
                  target[prop] = [];                                        // 如果是数组,那么在target对象中添加一个新的属性,并且这个属性值也是数组
              } else {
                  target[prop] = {};                                        // 如果不是数组,那么在target对象中添加一个新的属性,是一个空对象
              }
              deepClone(origin[prop], target[prop]);                        // (递归)再次调用这个函数,把origin对象中的属性对象的值依次的复制到target对象的这个属性对象中
          } else {
              target[prop] = origin[prop];                                  // 如果值是普通的数据,直接复制到target对象的这个属性中
          }
      }
    }
  return target;
}
deepClone(obj, obj1);
console.log(obj);
console.log(obj1);

打印结果如下:

javascript 对象克隆 js怎么克隆对象_javascript 对象克隆_03

延续上面代码,现在我们再来验证改变拷贝后的obj1中的引用属性值:

obj1.card.push("银联");
obj1.wife.name = "Rose";

console.log(obj.card);      // ["visa", "master"]
console.log(obj.wife.name); // Wade

console.log(obj1.card);     // ["visa", "master", "银联"]
console.log(obj1.wife.name);// Rose

最后,优化下上面函数代码,让它看起来更加简洁:

function deepClone(origin, target) {
    var target = target || {},
        toStr = Object.prototype.toString,    // 让代码更简介;
        arrStr = "[object Array]";            // 要进行比对的值
    for (var prop in origin) {
      if (origin.hasOwnProperty(prop)) {                                  
          if (origin[prop] !== "null" && typeof(origin[prop]) == "object") { 
               target[prop] = (toStr.call(origin[prop]) == arrStr) ? [] : {};  // 采用三目运算符的方式优化代码
              deepClone(origin[prop], target[prop]);                      
          } else {
              target[prop] = origin[prop];                                  
          }
      }
    }
  return target;
}
deepClone(obj, obj1);

总结:深拷贝思路:

首先遍历对象for...in...

1、判断是不是原始值 typeof Object

2、判断是数组还是对象 instanceof/isPrototypeOf()/Object.prototype.toString.call()/constructor

3、建立相应的数组或对象

for...in...不仅可以遍历对象,也可以遍历数组,因为数组也是对象(数组的属性可以看作是前面的索引)

var arr = [1, 2, 3, 4, 5];
for (var prop in arr) {
    console.log(prop + ":" + arr[prop]);
}

打印结果如下:

javascript 对象克隆 js怎么克隆对象_javascript 对象克隆_04