浅拷贝和深拷贝

本文参考:

  • JavaScript中的浅拷贝和深拷贝
  • js 深拷贝 vs 浅拷贝
  • 深入剖析 JavaScript 的深复制
  • javascript中的深拷贝和浅拷贝?

浅拷贝

最简单的浅拷贝就是赋值操作

var obj1 = ['lee', {age: 22}];
var obj2 = obj1;

obj2[0] = 'json';
console.log(obj1[0]); // json

obj2[1].age = 20;
console.log(obj1[1].age); // 20
  • 对于基本数据类型,两个变量不会相互影响
  • 对于引用数据类型,两个变量共用同一块堆内存,会相互影响
赋值内存分析

浅拷贝是只复制第一层得到的新实例

var obj1 = ['lee', {age: 22}];
var obj2 = obj1.slice();

obj2[0] = 'json';
console.log(obj1[0]); // lee

obj2[1].age = 20;
console.log(obj1[1].age); // 20
  • 对于基本数据类型,会在堆中重新开辟一块内存,修改后不会相互影响
  • 对于引用数据类型,因为只拷贝了一层,所以指针还是指向同一块堆内存,会相互影响
浅拷贝内存分析

深拷贝

深拷贝是递归复制了所有层级得到的新实例

var obj1 = ['lee', {age: 22}];
var obj2 = JSON.parse(JSON.stringify(obj1));

obj2[0] = 'json';
console.log(obj1[0]); // lee

obj2[1].age = 20;
console.log(obj1[1].age); // 22
  • 由于对整个对象及其子对象进行了递归复制,所以无论是基本数据类型还是引用数据类型,内存上都会重新创建,两个对象不会相互影响
深拷贝内存分析

实现方法

数组浅拷贝

  1. Array.prototype.slice()
var obj1 = ['lee', {age: 22}];
var obj2 = obj1.slice();
  1. Array.prototype.concat()
var obj1 = ['lee', {age: 22}];
var obj2 = obj1.concat();
  1. Array.from(obj)
var obj1 = ['lee', {age: 22}];
var obj2 = Array.from(obj1);
  1. Array.prototype.map(callback)
var obj1 = ['lee', {age: 22}];
var obj2 = obj1.map(item => item);

浅拷贝

  1. Object.assign([], obj)Object.assign({}, obj)
var obj1 = ['lee', {age: 22}];
var obj2 = Object.assign([], obj1);

var obj1 = { a: 0, b: { c: 0 } };
var obj2 = Object.assign({}, obj1);
  1. [...obj]{...obj}
var obj1 = ['lee', {age: 22}];
var obj2 = [...obj1];

var obj1 = { a: 0, b: { c: 0 } };
var obj2 = {...obj1}
  1. 自己动手实现
function shallowClone (obj) {
  if (!obj || typeof obj !== 'object') {
    throw new Error('arguments error');
  }
  var targetObj = Array.isArray(obj) ? [] : {};
  for (var key in obj) {
    if(obj.hasOwnProperty(key)) {
      targetObj[key] = obj[key];
    }
  }
  return targetObj;
}
  1. jQuery.extend([], obj)jQuery.extend({}, obj)
var obj1 = ['lee', {age: 22}];
var obj2 = $.extend([], obj1);

var obj1 = { a: 0, b: { c: 0 } };
var obj2 = $.extend({}, obj1);
  1. lodash.clone(obj)
var obj1 = ['lee', {age: 22}];
var obj2 = _.clone(obj1);

var obj1 = { a: 0, b: { c: 0 } };
var obj2 = _.clone(obj1);

深拷贝

  1. JSON.parse(JSON.stringify(obj))
var obj1 = ['lee', {age: 22}];
var obj2 = JSON.parse(JSON.stringify(obj1));

缺点:由于JSON.stringify()方法在序列化JS对象时,会忽略所有函数和原型成员,导致拷贝丢失。
这种实现方法能够处理JSON格式所能表示的所有数据类型,但是对RegexpFunction等类型无法进行深拷贝,同时也无法正确处理对象内部循环应用的问题。

  1. 自己动手实现
function deepClone(obj) {
  if (!obj || typeof obj !== 'object') {
    throw new Error('arguments error');
  }
  var targetObj = Array.isArray(obj) ? [] : {};
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      if (obj[key] && typeof obj[key] === 'object') {
        targetObj[key] = Array.isArray(obj[key]) ? [] : {};
        targetObj[key] = deepClone(obj[key]);
      } else {
        targetObj[key] = obj[key];
      }
    }
  }
  return targetObj;
}

缺点:与jQuery中的extend方法实现思路一样,不能处理对象内部循环引用的问题,对DateFunction类型的值没有实现真正的深拷贝,但这些类型的值重新定义时,一般都是直接覆盖,不会影响原对象,所以从一定程度上来讲也算是实现了深拷贝。

  1. jQuery.extend(true, [], obj)jQuery.extend(true, {}, obj)
var obj1 = ['lee', {age: 22}];
var obj2 = $.extend(true, [], obj1);

var obj1 = { a: 0, b: { c: 0 } };
var obj2 = $.extend(true, {}, obj1);

jQuery.extend 源码实现
缺点:无法处理对象内部循环引用的问题

  1. lodash.cloneDeep(obj)
var obj1 = { a: 0, b: { c: 0 } };
var obj2 = _.cloneDeep(obj1)

lodash.cloneDeep 源码实现

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