深拷贝和浅拷贝

230 阅读5分钟

深拷贝和浅拷贝

堆和栈的区别

栈: (stack) 自动分配的空间,由系统自动释放
堆: (heap) 动态分配内存,大小不定, 也不会自动释放

数据类型

基本数据类型主要是:undefinedbooleannumberstringnullsymbol
引用数据类型 对象,数组,function

let a = 1;
let b = a;
b = 15;
console.log(a, b) //1 15

赋值过程如下

栈里面开辟一块内存,放1,之后将a 与1关联起来
   再开辟一块内存,放15 ,把之前的关联取消,在和b关联起来重新赋值 原来的值保持不变

引用类型数据是存放在堆中,当obj1赋值给obj2 执行过程如下图,

let obj1 = {a:1}
let obj2 = obj1;
obj2.a = 100;
console.log(obj1.a)//100 

传值和传址

传值和传地址如下图

传值: 就是给变量赋常量值
传址: 在堆内里取一块区域,放对象值然后有个地址, 栈里有个变量,把一个地址赋值给变量。

深浅拷贝

  1. 原始数据,是没有深浅拷贝的区别

  2. 引用类型数据才有深浅拷贝

使用场景: 复杂对象

浅拷贝

赋值对象 所有的属性 都不是 引用类型的时候,我们可以使用浅拷贝。
遍历并复制,最后返回一个对象。

浅拷贝:只复制一层对象,当对象的属性是引用类型时,实际复制的是其引用。
    引用值指向发生改变时候,也会跟着改变。

使用for  in

// 实现浅拷贝
let shallowCopy = obj => {
  let rst = {};
  //  遍历对象
  for (let key in obj) {
    // 只复制本身拥有的属性(非继承过来的属性)枚举属性
    if (obj.hasOwnProperty(key)) {
      rst[key] = obj[key];
    }
  }
  return rst;
}

let person = {
    name: '张三',
    age: 12,
    eat: function(){},
    car:['自行车', '奔驰'],
    chlid:[{
        name: '李四',
        age:2
    },{
        name:'王五',
        age: 30
    }]
}

let newPerson = shallowCopy(person);
newPerson.name = '罗志祥'
newPerson.age = 50;
newPerson.chlid[0].name = '柴扉'
newPerson.car[1] = '破烂车'

console.log(newPerson, person);

使用扩展运算符

let star = { name: '刘亦菲' };
let newStar = { ...star };
newStar.name = '迪丽热巴';
console.log(star.name)


let girl = {
  name: '死胖子', // 赋值
  info: {
    height: 180,
    weight: 180
  }
}

let newGirl = { ...girl };

newGirl.info.height = 140;
console.log(girl.info.height); // 

扩展运算符它的value是原始数据类型的时候。是深拷贝。
当value是引用类型的时候,是浅拷贝

Object.assign


// Object.assign 可以把 n 个源对象拷贝到目标对象中去
/* 
es6中的方法

Object.assign是深拷贝还是浅拷贝
*/

let person = {
  name: '开心',
  info: {
    gender: '女',
    hobby: '吃饭睡觉打豆豆'
  }
}

// 第一级属性是深拷贝,以后级别浅拷贝
let lincancan = Object.assign({}, person);
console.log(lincancan);

lincancan.name = '林灿灿';
console.log(person.name);  // 开心

lincancan.info.hobby = '吃鸡脚';
console.log(person.info.hobby); // 鸡脚

深拷贝

JSON.stringify

let obj = {
  name'小明',
  dog: ['小花''旺财']
}

let obj1 = JSON.parse(JSON.stringify(obj));
obj1.name = '小华';
obj1.dog[0] = '小白';
// console.log(obj1,obj)

let richGirl = [{
  name:'开心',
  car:['宝马','奔驰','保时捷'],
  deive:function (){},
  age:undefined
}]

let richBoy = JSON.parse(JSON.stringify(richGirl));
console.log(richBoy);

// 纯的JSON数据,不包含循环引用

递归实现

//  递归实现深拷贝
let deepClone = obj => {
  let newObj = Array.isArray(obj) ? [] : {};
  if (obj && typeof obj === 'object') {
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        if (obj[key] && typeof obj[key] === 'object') {
          newObj[key] = deepClone(obj[key]);
        } else {
          // 如果不是对象直接拷贝
          newObj[key] = obj[key];
        }
      }
    }
  }
  return newObj;
}

let richGirl = {
  name: '开心',
  car: ['宝马', '奔驰', '保时捷'],
  deive: function () { },
  age: undefined
}

let richBoy = deepClone(richGirl);
// console.log(richBoy);

richBoy.deive = '渣男开大G';
richBoy.name = '小明';
richBoy.car = ['哈罗单车''膜拜'];
richBoy.age = 20;

console.log(richGirl);
console.log(richBoy);

深拷贝:将 B 对象拷贝到 A 对象中,包括 B 里面的子对象.
浅拷贝:将 B 对象拷贝到 A 对象中,但不包括 B 里面的子对象

-- 和原数据是否指向同一对象 第一层数据为基本数据类型 原数据中包含子对象
赋值 改变会使原数据一同改变 改变会使原数据一同改变
浅拷贝 改变不会使原数据一同改变 改变会使原数据一同改变
深拷贝 改变不会使原数据一同改变 改变不会使原数据一同改变
/**
 * 深拷贝
 *
 * 缺陷: 比如拷贝Symbol、拷贝函数、循环引用
 */
export const deepCopyJson = source => {
  return JSON.parse(JSON.stringify(source));
};

/**
 * 请试着实现一个可以解决 deepCopyJson 中不能拷贝的Symbol、循环引用问题的拷贝函数,
 * 并且保证copy.test.js中的单元测试顺利通过。
 */
export const deepCopy = source => {};
- END -