JavaScript

JS中的浅拷贝与深拷贝

2020-04-13  本文已影响0人  西瓜鱼仔

区分

如何区分浅拷贝与深拷贝,简单点来说,就是假设B复制了A,当修改其中一方时,看另一方是否会发生变化:

  • 如果另一方跟着变了,说明这是浅拷贝,荣辱与共
  • 如果另一方没变,那就是深拷贝,自立门户

原理

想真正理解和运用克隆的话,首先要了解JS中的原始值和应用值,以及它们的储存位置及方式。
原始值存储在栈里,而且存储的是变量的实际值。引用值存储在堆里,且存储的是一个指针,该指针指向内存中的某个位置,该位置存储变量的实际值。

  1. 基本数据类型属于原始值,名字和值都会储存在栈内存中:
var a = 1;
b = a; // 栈内存会开辟一个新的内存空间,此时b和a都是相互独立的
b = 2;
console.log(a); // 1

基本数据类型赋值算不上是深拷贝,因为深拷贝本身只针对较为复杂的Object类型数据。

  1. 引用数据类型,名字存在栈内存中,值存在堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值。比如浅拷贝:

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

    a[0] = 1时进行数组修改时,由于a与b指向的是同一个地址,所以自然b也受了影响,这就是所谓的浅拷贝。
    那要是在堆内存中也开辟一个新的内存专门为b存放值,就像基本类型那样,就实现了深拷贝:

实现浅拷贝的方法

  1. 直接用=赋值
let a = [0,1,2,3,4],
    b = a;
console.log(a === b);//true
a[0] = 1;
console.log(a,b);//[1,1,2,3,4] [1,1,2,3,4]
  1. for···in只循环第一层
// 只复制第一层的浅拷贝
function simpleCopy(obj1) {
   var obj2 = Array.isArray(obj1) ? [] : {};
   for (let i in obj1) {
   obj2[i] = obj1[i];
  }
   return obj2;
}
var obj1 = {
   a: 1,
   b: 2,
   c: {
         d: 3
      }
}
var obj2 = simpleCopy(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 4
alert(obj2.c.d); // 4
  1. Object.assign方法
var obj = {
    a: 1,
    b: 2
}
var obj1 = Object.assign({},obj);
boj1.a = 3;
console.log(obj.a) // 3

实现深拷贝的方法

  1. 采用递归去拷贝所有层级属性
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. 通过JSON对象来实现深拷贝
function deepClone2(obj) {
  var _obj = JSON.stringify(obj),
    objClone = JSON.parse(_obj);
  return objClone;
}

缺点: 无法实现对对象中方法的深拷贝,会显示为undefined

  1. 直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果
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;
}

4.使用扩展运算符实现深拷贝

// 当对象中的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米" }

参考自:
https://www.jianshu.com/p/1c142ec2ca45
https://blog.csdn.net/qq_22896159/article/details/81949296

上一篇下一篇

猜你喜欢

热点阅读