JS 深拷贝与浅拷贝

引言

要介绍深浅拷贝那么咱们先了解一下js的数据类型。

  • 基本数据类型:number,string,boolean,undefined,null
  • 复杂数据类型:object,array,function,regexp,date 等

一、简介

1.浅拷贝

对于基本数据类型,浅拷贝相当于直接赋值
对于复杂数据类型,浅拷贝只拷贝一层,相当于只拷贝其引用,例如对象B是由对象A浅拷贝得到的,即AB还是指向内存中的同一地址,那么改变对象B,对象A也会改变

2.深拷贝

对于复杂数据类型,深拷贝会拷贝多层,是真正意义上的拷贝,例如对象B是由对象A深拷贝得到的,即AB指向不同的内存地址,那么无论如何改变对象B,对象A也不会改变

二、实现方法

上面讲完什么是深浅拷贝,那么接下来就用具体的代码去实现深浅拷贝

1.浅拷贝

方法一:简直粗暴的直接赋值

var obj1 = { name: 'ming', age: 10 };
var obj2 = obj1;
obj2.name = 'lei';
console.log(obj1.name) // lei
console.log(obj2.name) // lei

方法二:Object.assign(target,source) ES6

Object.assign方法实行的是浅拷贝,而不是深拷贝。如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用,如下面的例子

var obj1 = { name: 'father', children: {name:'son',age:10} };
var obj2 = Object.assign({},obj1)
obj2.children.name = 'lei';
console.log(obj1.children.name) // lei
console.log(obj2.children.name) // lei

方法三:for in 循环

和方法二类似,如果源对象的某个属性是对象,那么目标对象拷贝得到的是这个对象的引用

var obj1 = { name: 'father', children: {name:'son',age:10} };
var obj2 = {};
for (var key in obj1){
    obj2[key] = obj1[key];
}
obj2.children.name = 'lei';
console.log(obj1.children.name) // lei
console.log(obj2.children.name) // lei

方法四:Object.create(target,source);

Object.getOwnPropertyDescriptors 为 ES6 新增方法

var obj1 = { name: 'father', children: {name:'son',age:10} };
var obj2 = Object.create({}, Object.getOwnPropertyDescriptors(obj1));
obj2.children.name = 'lei';
console.log(obj1.children.name) // lei
console.log(obj2.children.name) // lei

2.深拷贝

方法一:JSON.parse(JSON.stringify(source))

用JSON.stringify将源对象(source)转为字符串,再用JSON.parse方法将字符串转为目标对象

注: 此方法只能用作可以转为JSON格式对象,否则JSON.stringify方法将会报错,并且不管原来对象的构造函数是什么拷贝之后都会变为Object

var obj1 = { name: 'ming', age: 10 };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.name = 'lei';
console.log(obj1.name) // ming
console.log(obj2.name) // lei

方法二:递归拷贝

function deepClone(obj1, obj2) {
  var obj = obj2 || {};
  for (var i in obj1) {
    var temp = obj1[i]; // 避免相互引用对象导致死循环,如obj1.a = obj1的情况
    if (temp === obj) {
      continue;
    }
    if (typeof temp === 'object') {
      obj[i] = (temp.constructor === Array) ? [] : {};
      arguments.callee(temp, obj[i]);
    } else {
      obj[i] = temp;
    }
  }
  return obj;
}
var obj1 = { name: 'father', children: {name:'son',age:10} };
var obj2 = deepClone(obj1);
obj2.children.name = 'lei';
console.log(obj1.children.name) // son
console.log(obj2.children.name) // lei

方法三:Object.create()

function deepClone(obj1, obj2) {
  var obj = obj2 || {};
  for (var i in obj1) {
    var temp = obj1[i]; // 避免相互引用对象导致死循环,如obj1.a = obj1的情况
    if (temp === obj) {
      continue;
    }
    if (typeof temp === 'object') {
      obj[i] = (temp.constructor === Array) ? [] : Object.create(temp);
    } else {
      obj[i] = temp;
    }
  }
  return obj;
}
var obj1 = { name: 'father', children: {name:'son',age:10} };
var obj2 = deepClone(obj1);
obj2.children.name = 'lei';
console.log(obj1.children.name) // son
console.log(obj2.children.name) // lei

方法四:Object.assign(target,source) ES6

将源对象(source)的所有可枚举属性,复制到目标对象(target)

注: 此方法只能用作对象只有一层的情况

var obj1 = { name: 'ming', age: 10 };
var obj2 = Object.assign({},obj1);
obj2.name = 'lei';
console.log(obj1.name) // ming
console.log(obj2.name) // lei

但是,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用,因此也不能被称为深拷贝

var obj1 = { name: 'father', children: {name:'son',age:10} };
var obj2 = Object.assign({},obj1)
obj2.children.name = 'lei';
console.log(obj1.children.name) // lei
console.log(obj2.children.name) // lei

方法五:for in 循环

注: 此方法只能用作对象只有一层的情况,类似方法三

var obj1 = { name: 'ming',age:10};
var obj2 = {};
for (var key in obj1){
  obj2[key] = obj1[key];
}
obj2.name = 'lei';
console.log(obj1.name) // ming
console.log(obj2.name) // lei

实现浅拷贝的方法还有很多种,但是真正实现深拷贝的方法却很少,所以在拷贝对象时一定要注意

你可能感兴趣的:(JS 深拷贝与浅拷贝)