阅读 31

深拷贝与浅拷贝

1、概要

  • 本文主要讲述数据类型。
  • js中深拷贝与浅拷贝的概念。
  • 介绍深拷贝与浅拷贝的实现方法。

2、数据类型

  • 基本数据类型
  1. 基本的数据类型有:undefinedbooleannumberstringnull,symbol
  2. 基本类型的访问是按值访问的,就是说你可以操作保存在变量中的实际的值。
  3. 任何方法都无法改变一个基本类型的值。
var str = 'abc123';
str.replace('abc', 123); // '123123'
console.log(str);  // 'abc123'
复制代码
  1. 基本类型的比较是值的比较。
  2. 基本类型的值是存放在栈内存中。
  • 引用数据类型
  1. 除基本类型之外就是引用数据类型,也可以说就是对象了(Object,Array,Function,Math,Data,RegExp等)。
  2. 引用类型的值是可变的。
  3. 引用类型的值是同时保存在栈内存和堆内存中的对象。
  4. 引用类型的比较是引用的比较

3、基本概念

  • 简单来说,如果对象B拷贝A,当修改A时,如果B也跟着变了,说明这是浅拷贝;如果B没变,那就是深拷贝。
  • 所有基本数据类型的拷贝都是深拷贝。
// 对基本数据类型的直接赋值是深拷贝
var a = 1;
b = a;
a = 2;
console.log(a); // 2
console.log(b); // 1

// 对引用数据类型的直接赋值是浅拷贝(拷贝的是引用的地址)
var obj1 = { name: 'ZhangSan', age: 21 };
var obj2 = obj1;
obj1.age = 32;
console.log(obj1.age); // 32
console.log(obj2.age); // 32
复制代码
  • 后续本文讲述的深拷贝和浅拷贝都是针对引用数据类型来阐述的

4、如何实现深拷贝

1、数组的拷贝

// 浅拷贝,直接赋值
var a = [1,2,3,4,5];
var b = a;
a[0] = 2;
console.log(a); // [2, 2, 3, 4, 5]
console.log(b); // [2, 2, 3, 4, 5]
复制代码
// cancat,简单数组的深拷贝
var a = [1,2,3,4,5];
var b = [].concat(a);
a[0] = 2;
console.log(a); // [2, 2, 3, 4, 5]
console.log(b); // [1, 2, 3, 4, 5]
复制代码
// slice,简单数组的深拷贝
var a = [1,2,3,4,5];
var b = a.slice();
a[0] = 2;
console.log(a); // [2, 2, 3, 4, 5]
console.log(b); // [1, 2, 3, 4, 5]
复制代码
// ...运算符,简单数组的深拷贝
var a = [1,2,3,4,5];
var b = [...a];
a[0] = 2;
console.log(a); // [2, 2, 3, 4, 5]
console.log(b); // [1, 2, 3, 4, 5]
复制代码
  1. cancat,slice,...运算符的不足
var a = [1,[1,2],3,4,5];
var b = a.slice();
a[0] = 2;
a[1][0] = 2;
console.log(a); // [2, [2,2], 3, 4, 5]
console.log(b); // [1, [2,2], 3, 4, 5]
复制代码
var a = [1,[1,2],3,4,5];
var b = [].concat(a);
a[0] = 2;
a[1][0] = 2;
console.log(a); // [2, [2,2], 3, 4, 5]
console.log(b); // [1, [2,2], 3, 4, 5]
复制代码
var a = [1,[1,2],3,4,5];
var b = [...a];
a[0] = 2;
a[1][0] = 2;
console.log(a); // [2, [2,2], 3, 4, 5]
console.log(b); // [1, [2,2], 3, 4, 5]
复制代码

从上面的结果可以看到,cancat,slice,...运算符只能对数组的一级元素为基本类型的数据实现深拷贝,对于引用类型的元素还是浅拷贝。

2、对象的拷贝

  1. ...运算符
var obj1 = { 
  name: 'ZhangSan', 
  age: 24, 
  team: { 
    name: 'obj1',
    number: 10
  } 
};
var obj2 = {...obj1};
obj1.name = 'LiSi';
obj1.age = 42;
obj1.team.name = 'test';
obj1.team.number = 20;
console.log(obj1); // {name: "LiSi", age: 42, team: {name: "test", number: 20}}
console.log(obj2); // {name: "ZhangSan", age: 24, team: {name: "test", number: 20}}
复制代码
  1. Object.assign()方法
var obj1 = { 
  name: 'ZhangSan', 
  age: 24, 
  team: { 
    name: 'obj1',
    number: 10
  } 
};
var obj2 = Object.assign({}, obj1);
obj1.name = 'LiSi';
obj1.age = 42;
obj1.team.name = 'test';
obj1.team.number = 20;
console.log(obj1); // {name: "LiSi", age: 42, team: {name: "test", number: 20}}
console.log(obj2); // {name: "ZhangSan", age: 24, team: {name: "test", number: 20}}
复制代码

从上面可以看到...运算符Object.assign()方法也只能实现对一级数据为基本数据类型的拷贝,对于一级数据类型为引用类型的数据无法实现深拷贝。

3、 使用JSON.parseJSON.stringify

var a = [1,[1,2],3,4,5];
var b = JSON.parse(JSON.stringify(a));
a[0] = 2;
a[1][0] = 2;
console.log(a); // [2, [2,2], 3, 4, 5]
console.log(b); // [1, [1,2], 3, 4, 5]
复制代码
var obj1 = { 
  name: 'ZhangSan', 
  age: 24, 
  team: { 
    name: 'obj1',
    number: 10
  } 
};
var obj2 = JSON.parse(JSON.stringify(obj1));
obj1.name = 'LiSi';
obj1.age = 42;
obj1.team.name = 'test';
obj1.team.number = 20;
console.log(obj1); // {name: "LiSi", age: 42, team: {name: "test", number: 20}}
console.log(obj2); // {name: "ZhangSan", age: 24, team: {name: "obj1", number: 10}}
复制代码

ps: 并不是所有的数据类型都可以被JSON.stringify

4、递归

// 此方法仅仅提供思路,并非最佳实践
function deepClone(target, type) { // target:目标对象,typetrue/false =>深拷贝/浅拷贝,默认false
  if(!type) return target
  const baseType = ['string','number','boolean','undefined','null','symbol']
  if(!target || baseType.includes(typeof target)) return target
  let cloneData = Array.isArray(target) ? [] : {};
  if(typeof target === 'object') {
    for(key in target) {
      if(target.hasOwnProperty(key)) {
        if(target[key] && typeof target[key] ==="object") {
          cloneData[key] = deepClone(target[key], true);
        } else {
          cloneData[key] = target[key];
        }
      }
    }
  }
  return cloneData
}

var obj1 = { 
  name: 'ZhangSan', 
  age: 24, 
  team: { 
    name: 'obj1',
    number: 10,
    person: {
      name: '二狗子',
      sex: '女'
    }
  } 
};
var obj2 = deepClone(obj1, true);
obj1.name = 'LiSi';
obj1.age = 42;
obj1.team.name = 'test';
obj1.team.number = 20;
obj1.team.person = {
  name: '刘二哈',
  sex: '男'
}
console.log(obj1); //
console.log(obj2); //
复制代码

5.总结

  • 主要讲述了基本数据类型和引用数据类型的概念。
  • 深拷贝和浅拷贝的概念和区别。
  • 介绍了几种实现深拷贝的方法。
  • 递归的方法需要自己根据需求选择去完善。
关注下面的标签,发现更多相似文章
评论