Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

讲讲深拷贝与浅拷贝,如何实现,有哪些方式? #4

Open
JCHappytime opened this issue Feb 23, 2021 · 0 comments
Open

Comments

@JCHappytime
Copy link
Owner

JCHappytime commented Feb 23, 2021

参考文章

  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。同时如果对象中存在循环引用的情况也无法正确处理。

@JCHappytime JCHappytime changed the title JS中的深浅拷贝如何实现? 讲讲深拷贝与浅拷贝,如何实现,有哪些方式? Feb 23, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant