深浅拷贝

吐槽君 分类:javascript

前言

为什么会出现深浅拷贝呢?

之所以会出现深浅拷贝的问题,实质上是由于JS对基本类型和引用类型的处理不同。基本类型指的是简单的数据段,而引用类型指的是一个对象,而JS不允许我们直接操作内存中的地址,也就是不能操作对象的内存空间,所以,我们对对象的操作都只是在操作它的引用而已。

在复制时也是一样,如果我们复制一个基本类型的值时,会创建一个新值,并把它保存在新的变量的位置上。而如果我们复制一个引用类型时,同样会把变量中的值复制一份放到新的变量空间里,但此时复制的东西并不是对象本身,而是指向该对象的指针。所以我们复制引用类型后,两个变量其实指向同一个对象,改变其中一个对象,会影响到另外一个。

var num = 10;
var obj = {
    name: 'yanjie'
}

var num2 = num;
var obj2 = obj;

obj.name = 'haoshuai';
obj2.name; // 'haoshuai'
 

CE1DDDDF546312F9E1F7528BCF2D1444.jpg
可以看到我们的obj和obj2都保存了一个指向该对象的指针,所有的操作都是对该引用的操作,所以对对象的修改会影响其他的复制对象。

浅拷贝

只考虑对象类型

如果我们要复制对象的所有属性都不是引用类型时,就可以使用浅拷贝,实现方式就是遍历并复制,最后返回新的对象

先上代码!!

function shallowCopy(obj) {
    if (typeof obj !== 'object') return
    
    let newObj = obj instanceof Array ? [] : {}
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = obj[key]
        }
    }
    return newObj 
}

 
  • 我们先来看一下这句代码 let newObj = obj instanceof Array ? [] : {},先看一下 obj instanceof Array,这句代码的作用是用来检测 Array.prototype 是否存在于参数 object 的原型链上。然后后面接上了三元运算符 ?[]:{}

所以这句代码的意思是Arrqy.prototype是否存在于参数在object原型链上,如果存在,就把[]赋值给newObj,反之,把{}赋值给它。

  • 既然讲到这里,那就把for in 和for of 也讲一下呗(主要是自己也不是太理解,嘿嘿!)

for … of是作为ES6新增的遍历方式,允许遍历一个含有iterator接口的数据结构并且返回各项的值,和ES3中的for … in的区别如下:

  1. for..in适用遍历数/数组对象/字符串/map/set等拥有迭代器对象的集合.但是不能遍历对象,因为没有迭代器对象.与forEach()不同的是,它可以正确响应break、continue和return语句
  2. for-of循环不支持普通对象,但如果你想迭代一个对象的属性,你可以用for-in循环或对象的Object.keys()方法.
  • hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。

这么解释了一下,上面的代码就好看多了吧!

当if条件满足的时候,newObj[key]就会复制 obj[key]引用类型的指针,简单来讲就是当obj[key]的值改变时,newObj[key]的值也会改变,这就是浅拷贝。

深拷贝

先上代码!

function deepClone(obj){
  let objClone = Array.isArray(obj)?[]:{};
  if(obj && typeof obj==="object"){
    for(key in obj){
      if(obj.hasOwnProperty(key)){
         //判断ojb子元素是否为对象,如果是,递归复制
          if(obj[key]&&typeof obj[key] ==="object"){
              objClone[key] = deepClone(obj[key]);
          }else{
              //如果不是,简单复制
              objClone[key] = obj[key];
          }
       }
     }
  }
return objClone;
} 

let a=[1,2,3,4],b=deepClone(a);
a[0]=2;
console.log(a,b);
 

1.jpg

成功实现了!撒花

扩展

  • JS中拷贝Array的slice和concat方法

slice拷贝

var a = [1,2,3];
var b = a.slice(); //slice
console.log(b === a);
a[0] = 4;
console.log(a);
console.log(b);
 

2.png

concat拷贝

var a = [1,2,3];
var b = a.concat();  //concat
console.log(b === a);
a[0] = 4;
console.log(a);
console.log(b);
 

3.png

看到结果,如果你觉得,这两个方法是深拷贝,那就恭喜你跳进了坑里!
来看看有意思的例子吧

var a = [[1,2,3],4,5];
var b = a.slice();
console.log(a === b);
a[0][0] = 6;
console.log(a);
console.log(b);
 

4.png

可以看到slice和contact对于第一层是深拷贝,但对于多层的时候,是复制的引用,所以是浅拷贝

最后的最后,再允许我多啰嗦一句呗:

JSON 对象的 parse 和 stringify都是深拷贝

总结

小伙伴们,这是手写题哦,所以一定要自己敲一下,那样印象才更深刻,加油哦!!!

回复

我来回复
  • 暂无回复内容