关于深拷贝和浅拷贝的我的理解

关于深拷贝和浅拷贝的我的理解javascript

1、深拷贝和浅拷贝区别java

如何区分深拷贝与浅拷贝,简单点来讲,就是假设B复制了A,当修改A时,看B是否会发生变化,若是B也跟着变了,说明这是浅拷贝,拿人手短,若是B没变,那就是深拷贝,自食其力。typescript

具体可看这篇文章:https://www.jianshu.com/p/1c142ec2ca45数组

示例:ruby

let obj = {
    name: "hahah",
    age: 18,
    sex: "男",
  }  // 复杂数据类型 赋值至关因而 赋引用地址
  const newObj = obj;  // 修改newObj其中一个值 , 另外一个obj也发生改变
  newObj.name = "hahahahahahah";  console.log(obj, newObj);

输出结果:两个复杂数据类型的 name 值所有改变 ,复杂数据类型 = 赋值只是 引用地址的传递 ,指向的仍是相同的引用。bash

简单的第一层浅拷贝:app

let obj = {
    name: "lili",
    age: 18,
    sex: "男",
    arr: [1, "2", "3"],
    obj: {
      name: "hahaha",
      sex: "女",
    }
  };  // 方法 一 
  // let newObj = Object.assign({}, obj);
  // 方法 二 
  let newObj = { ...obj };  // 再次改变时就是 改变不同的数据
  newObj.name = "hahaha";
  newObj.obj.sex = "男";
  newObj.arr[1] = 10;  console.log(obj, newObj);

输出结果:能够看到第一层的 name 没有互相影响 ,可是更深层次的 arr 数组 和 obj 对象 仍是发生了相同改变,这仍是一个浅拷贝。咱们可使用不少方式在js中复制数据,好比 扩展运算符...,Object.assign, Object.freeze,slice, concat, map,filter, reduce等方式进行复制,ide

这里能够得出一个结论:浅拷贝,就是只拷贝第一层数据,更深层的数据仍是同一个引用。函数

2、简单数据类型(基本数据类型)spa

若是是基本数据类型,名字和值都会储存在栈内存中

简单数据类型能够随意赋值都不会相互影响。

3、复杂数据类型(引用数据类型)

复杂数据类型的存储方法:存储空间分为堆空间 和 栈空间 ,复杂数据类型的值存在于 堆空间中 ,而名(www.taobaoosx.com,obj)则存在于栈空间中 ,堆空间返回一个引用地址给 名 ,名根据引用地址去访问 栈空间中的值。

图示:

当b=a进行拷贝时,其实复制的是a的引用地址,而并不是堆里面的值。

此时 a 和 b 指向的是 堆空间的同一个地址。而当咱们a[0]=1时进行数组修改时,因为a与b指向的是同一个地址,因此天然b也受了影响,这就是所谓的浅拷贝了。

顺着往下想能够获得这么一个结论:在堆内存中也开辟一个新的内存专门为b存放值,就像基本类型那样,岂不就达到深拷贝的效果了 。

4、实现深拷贝的方法

一、for in 循环实现深拷贝

let obj = {      name: "hahaha",      age: 18,      objChild: {        sex: "男",        addres: "深圳",        arr: [1, 2, 3, "10", "12", { aaa: "aaa", bbb: "bbb" }],
      },      func: function () {        console.log("我是一个函数");
      }
    }    function simpleCopy(obj) {      // 判断 obj 为数组仍是对象
      let newObj = Array.isArray(obj) ? [] : {}      // 遍历该对象
      for (let i in obj) {        // 判断 该对象是否 真的包含 i 这个键
        if (obj.hasOwnProperty(i)) {          // 判断 obj[i] 是否为 对象或者数组
          if (obj[i] && (typeof obj[i]) === "object") {            // 是对象递归再次执行
            newObj[i] = simpleCopy(obj[i]);
          } else {            // 不是直接简单赋值
            newObj[i] = obj[i];
          }
        }
      }      return newObj;
    }    let newObj = simpleCopy(obj);

    newObj.name = "ccccc";
    newObj.objChild.sex = "女";
    newObj.objChild.arr[5].aaa = "小帅";
    newObj.func = () => {      console.log("hahahahahahah");
    }
    obj.func();  // 我是一个函数
    newObj.func(); // hahahahahahah
    console.log(obj);    console.log(newObj);    console.log(newObj === obj); // false

输出结果:能够看到拷贝后的修改并无影响到原先 对象 ,新的对象的引用地址已经改变,与原先对象属于两个不一样对象,此时 console.log(newObj === obj); // false

二、JSON.stringfiy和JSON.parse方法实现深拷贝

let obj = {      name: "hahaha",      age: 18,      objChild: {        sex: "男",        addres: "深圳",        arr: [1, 2, 3, "10", "12", { aaa: "aaa", bbb: "bbb" }],        func: function () {          console.log("我是一个函数");
        }
      },

    }    function simpleCopy(obj) {      let objClone = JSON.parse(JSON.stringify(obj));      return objClone;
    }    let newObj = simpleCopy(obj);

    newObj.name = "ccccc";
    newObj.objChild.sex = "女";
    newObj.objChild.arr[5].aaa = "小帅";
    newObj.func = () => {      console.log("hahahahahahah");
    }    console.log(obj);    console.log(newObj);    console.log(newObj === obj); // false

    obj.objChild.func();  // 我是一个函数
    newObj.objChild.func(); // 报错

输出结果:

**缺点: 没法实现对对象中方法的深拷贝,会显示为www.taobaoosx.com,undefined **

三、经过jQuery的extend方法实现深拷贝

let newObj = $.extend(true, {}, obj); // true为深拷贝,false为浅拷贝

四、lodash函数库实现深拷贝

let result = _.cloneDeep(test)

官网截图:

五、Reflect法(与 for in 遍历相似)

// 代理法function deepClone(obj) {    // 判断是否为一个对象
    if (!isObject(obj)) {        throw new Error('obj 不是一个对象!')
    }    let isArray = Array.isArray(obj)    let cloneObj = isArray ? [...obj] : { ...obj }    // 静态方法 Reflect.ownKeys() 返回一个由目标对象自身的属性键组成的数组。
    Reflect.ownKeys(cloneObj).forEach(key => {
        cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
    })    return cloneObj
}

六、手动实现深拷贝

let obj1 = {
   a: 1,
   b: 2}let obj2 = {
   a: obj1.a,
   b: obj1.b}obj2.a = 3;alert(obj1.a); // 1alert(obj2.a); // 3

七、Object.assign来实现深拷贝

// 只适用于只有一层复杂数据类型var obj = {
    a: 1,
    b: 2}var obj1 = Object.assign({}, obj); // obj赋值给一个空{}obj1.a = 3;console.log(obj.a);// 1

八、数组方法slice实现对数组的深拷贝

// 当数组里面的值是基本数据类型,好比String,Number,Boolean时,属于深拷贝// 当数组里面的值是引用数据类型,好比Object,Array时,属于浅拷贝var arr1 = ["1","2","3"]; 
var arr2 = arr1.slice(0);
arr2[1] = "9";console.log("数组的原始值:" + arr1 );console.log("数组的新值:" + arr2 );

九、数组方法concat实现对数组的深拷贝

// 当数组里面的值是基本数据类型,好比String,Number,Boolean时,属于深拷贝var arr1 = ["1","2","3"];var arr2 = arr1.concat();
arr2[1] = "9";console.log("数组的原始值:" + arr1 );console.log("数组的新值:" + arr2 );// 当数组里面的值是引用数据类型,好比Object,Array时,属于浅拷贝var arr1 = [{a:1},{b:2},{c:3}];var arr2 = arr1.concat();
arr2[0].a = "9";console.log("数组的原始值:" + arr1[0].a ); // 数组的原始值:9console.log("数组的新值:" + arr2[0].a ); // 数组的新值:9

十、使用扩展运算符实现深拷贝

// 当value是基本数据类型,好比String,Number,Boolean时,是可使用拓展运算符进行深拷贝的
// 当value是引用类型的值,好比Object,Array,引用类型进行深拷贝也只是拷贝了引用地址,因此属于浅拷贝
var car = {brand: "BMW", price: "380000", length: "5米"}
var car1 = { ...car, price: "500000" }
console.log(car1); // { brand: "BMW", price: "500000", length: "5米" }
console.log(car); // { brand: "BMW", price: "380000", length: "5米" }

十一、Object.create()实现深拷贝

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;
}
相关文章
相关标签/搜索