Skip to content

Latest commit

 

History

History
73 lines (68 loc) · 4.28 KB

File metadata and controls

73 lines (68 loc) · 4.28 KB

参考文章

  1. js的深拷贝和浅拷贝
  2. js深拷贝 VS 浅拷贝

前言

深浅拷贝实际上针对的是复杂数据类型而言的,浅拷贝只复制对象的一层属性,深拷贝则复制了所有层级的属性。因为Js存储对象是存地址的,所以浅拷贝会导致obj1和obj2指向同一块内存地址,而深拷贝则是为对象开辟一块新的内存,复制对象进来,因此对于深拷贝而言,obj1和obj2的操作互不影响。

浅拷贝实现

指的是将一个对象的属性值复制到另外一个对象,如果有的属性的值为引用类型的话,那么会将这个引用的地址复制给对象,因此两个对象会有同一个引用类型的引用。浅拷贝可以使用Object.assign()和展开运算符来实现(如:const {a, b, c} = {1, 2, {3, 4}}; 所以c = {3, 4})。 如何实现一个对象或者数组的浅拷贝。 想一想,好像很简单,遍历对象,然后把属性和属性值都放在一个新的对象就好了~

function shallowCopy(object) {
  // 只拷贝对象
  if (!object || typeof object !== "object") {
    return;
  }
  // 根据object的类型判断是新建一个数组还是对象
  let newObject = Array.isArray(object) ? [] : {};
  
  // 遍历object,并且判断是object的属性才拷贝
  for (let key in object) {
    if (object.hasOwnProperty(key)) {
      newObject[key] = object[key];
    }
  }
  return newObject;
}

深拷贝实现

相对于浅拷贝而言,如果遇到属性值为引用类型的时候,它新建一个引用类型并将对应的值复制给它,因此对象获得的一个新的引用类型而不是一个原有类型的引用。深拷贝对于一些对象可以使用JSON的两个函数来实现,但是由于JSON的对象格式化比js的对象格式化更加严格,所以如果属性值里面出现函数或者Symbol类型的值时,会转换失败。 那如何实现一个深拷贝呢?说起来也简单,我们在拷贝的时候判断一下属性值的类型,如果是对象,我们递归调用深拷贝函数不就好了~

function deepCopy(object) {
  if (!object || typeof object !== "object")  return;
    let newObject = Array.isArray(object) ? [] : {};
    
    for (let key in object) {
      if (object.hasOwnProperty(key)) {
        newObject[key] = typeof object[key] === "object" ? deepCopy(object[key]) : object[key];
    }
  }
  
  return newObject;
}

思考

  • 【性能问题】尽管使用深拷贝会完全克隆一个新对象,不会产生副作用,但是深拷贝使用了递归,性能会不如浅拷贝,在开发中,还是要根据实际情况选择。
  • 【JSON对象的parse和stringify】JSON对象是ES5中引入的新的类型(支持的浏览器为IE8+),JSON对象parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串, 借助这两个方法,也可以实现对象的深拷贝。

示例

  //例1
  var source = { name:"source", child:{ name:"child" } } 
  var target = JSON.parse(JSON.stringify(source));
  target.name = "target";  //改变target的name属性
  console.log(source.name); //source 
  console.log(target.name); //target
  target.child.name = "target child"; //改变target的child 
  console.log(source.child.name); //child 
  console.log(target.child.name); //target child
  //例2
  var source = { name:function(){console.log(1);}, child:{ name:"child" } } 
  var target = JSON.parse(JSON.stringify(source));
  console.log(target.name); //undefined
  //例3
  var source = { name:function(){console.log(1);}, child:new RegExp("e") }
  var target = JSON.parse(JSON.stringify(source));
  console.log(target.name); //undefined
  console.log(target.child); //Object {}

这种方法使用较为简单,可以满足基本的深拷贝需求,而且能够处理JSON格式能表示的所有数据类型,但是对于正则表达式类型、函数类型等无法进行深拷贝(而且会直接丢失相应的值)。 还有一点不好的地方是它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。同时如果对象中存在循环引用的情况也无法正确处理。