美文网首页
深浅拷贝详解

深浅拷贝详解

作者: 池鱼_故渊 | 来源:发表于2020-12-24 17:34 被阅读0次

深浅拷贝

浅拷贝

浅拷贝是指创建一个新的对象,把这个对象的原始属性精确拷贝一份,如果是基本类型就拷贝基本类似的值,如果是引用类型,拷贝的就是内存地址,如果其中一个引用类型改变了值,那么就会影响另一个对象

深拷贝

深拷贝是将一个对象从内存中完成拷贝出来,在内存中开辟一个新的区域存放对象,并且两者不会相互影响

看图理解

1608802429(1).jpg
1608802449(1).jpg

赋值和浅拷贝的区别

在这篇文章之前,我一直认为,赋值就是浅拷贝,现在发现真的是大错特错,
赋值对于基本类型来说可以认为是浅拷贝,(基本类型没有深浅拷贝),下面我们来看一段代码

const obj = {
  name: "12",
  message: ["帅气",['丑'] "英俊"],
};
const obj2 = obj;
obj2.name = "14";
obj2.message[1] = ['真丑']
console.log(obj); //{ name: '14', message: [ '帅气',['真丑'], '英俊' ] }
console.log(obj2); // { name: '14', message: [ '帅气',['真丑'], '英俊' ] }

从代码中可以看出 name 的修改会相互影响。下面我们来看浅拷贝有哪些方法,以及使用浅拷贝之后 name 应该是什么样子的

浅拷贝方法

  • Object.assign() 对象浅拷贝
const obj = {
  name: "12",
  message: ["帅气", "英俊"],
};
let obj3 = Object.assign({}, obj);
obj3.name = "15";
console.log(obj, obj3);
// 可以看到浅拷贝修改之后name的变更并不会相互影响
 { name: '14', message: [ '帅气', '英俊' ] } { name: '15', message: [ '帅气', '英俊' ] }
  • ... 展开运算符 对象数组浅拷贝
const obj = {
  name: "12",
  message: ["帅气", "英俊"],
};
let obj4 = { ...obj };
obj4.name = "obj4";
console.log(obj, obj4);

// { name: '14', message: [ '帅气', '英俊' ] } { name: 'obj4', message: [ '帅气', '英俊' ] }
  • concat 数组浅拷贝
let arr = [1, 2, 3];
let newArr = arr.concat();
newArr[1] = 100;
console.log(arr); //[ 1, 2, 3 ]
console.log(newArr); // [ 1, 100, 3 ]
  • slice 数组浅拷贝
let arr = [1, 2, { val: 4 }];
let newArr = arr.slice();
newArr[2].val = 1000;
newArr[1] = 10;
console.log(arr); //[ 1, 2, { val: 1000 } ]
console.log(newArr); // [ 1, 10, { val: 1000 } ]
  • 手动实现浅拷贝
function clone(source) {
  if (typeof source === "object") {
    let target = Array.isArray(source) ? [] : {};
    for (let i in source) {
      if (source.hasOwnProperty(i)) {
        // source.hasOwnProperty(i)意思是_proto_上面的属性我们不拷贝
        target[i] = source[i];
      }
    }
    return target;
  } else {
    return target;
  }
}

以上就是一些浅拷贝的方法

深拷贝

  • 简单的方法 JSON.parse(JSON.stringify())
    只适用于固定的数据结构
    这种方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则
const obj = [{ name: "1" }, { name: "2" }];
const obj2 = JSON.parse(JSON.stringify(obj));
obj2[0].name = 3;
console.log(obj, obj2);
// [ { name: '1' }, { name: '2' } ] [ { name: 3 }, { name: '2' } ]
  • lodash 库的_.cloneDeep
    封装好的库可以直接使用,简单方便
let _ = require("lodash");
let obj1 = {
  a: 1,
  b: { f: { g: 1 } },
  c: [1, 2, 3],
};
let obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f); // false
  • 手写 递归方法
    深拷贝,递归+浅拷贝就是深拷贝

看一段代码

function copy1(source) {
  let target = {};
  for (let i in source) {
    if (source.hasOwnProperty(i)) {
      if (typeof source[i] === "object") {
        target[i] = copy1(source[i]);
      } else {
        target[i] = source[i];
      }
    }
  }
  return target;
}
const a = {
  name: "1",
  message: {
    name: "2",
  },
  age: null,
};

const b = copy1(a);
b.message.name = "3";
console.log(a, b);
// { name: '1', message: { name: '2' }, age: null } { name: '1', message: { name: '3' }, age: {} }

在浅拷贝的基础上添加了一个判断是否是对象,如果是对象进入递归,实现了简单的深拷贝

再看一段代码

// 判断是否是对象
function isObject(obj) {
  return typeof obj === "object" && obj !== null;
}

function copy2(source) {
  if (!isObject(source)) return source;
  let target = Array.isArray(source) ? [] : {};
  for (let i in source) {
    if (source.hasOwnProperty(i)) {
      if (isObject(source[i])) {
        target[i] = copy2(source[i]);
      } else {
        target[i] = source[i];
      }
    }
  }
  return target;
}
const a = {
  name: "1",
  message: {
    name: "2",
  },
  age: null,
  arr: [1, 2, 3, [4, 5]],
};

let b = copy2(a);
b.arr[3][1] = 9;
console.log(a);
// { name: '1',
//   message: { name: '2' },
//   age: null,
//   arr: [ 1, 2, 3, [ 4, 5 ] ] }
console.log(b);
// { name: '1',
//   message: { name: '2' },
//   age: null,
//   arr: [ 1, 2, 3, [ 4, 9 ] ] }

实现了对数组的兼容

解决循环引用,再看一段代码

function copy3(source, hash = new WeakMap()) {
  if (!isObject(source)) return source;
  if (hash.has(source)) return hash.get(source); // 新增代码,查哈希表

  var target = Array.isArray(source) ? [] : {};
  hash.set(source, target); // 新增代码,哈希表设值

  for (var key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      if (isObject(source[key])) {
        target[key] = cloneDeep3(source[key], hash); // 新增代码,传入哈希表
      } else {
        target[key] = source[key];
      }
    }
  }
  return target;
}
// es5版本
function cloneDeep3(source, uniqueList) {
  if (!isObject(source)) return source;
  if (!uniqueList) uniqueList = []; // 新增代码,初始化数组

  var target = Array.isArray(source) ? [] : {};

  // ============= 新增代码
  // 数据已经存在,返回保存的数据
  var uniqueData = find(uniqueList, source);
  if (uniqueData) {
    return uniqueData.target;
  }

  // 数据不存在,保存源数据,以及对应的引用
  uniqueList.push({
    source: source,
    target: target,
  });
  // =============

  for (var key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      if (isObject(source[key])) {
        target[key] = cloneDeep3(source[key], uniqueList); // 新增代码,传入数组
      } else {
        target[key] = source[key];
      }
    }
  }
  return target;
}

// 新增方法,用于查找
function find(arr, item) {
  for (var i = 0; i < arr.length; i++) {
    if (arr[i].source === item) {
      return arr[i];
    }
  }
  return null;
}

主要的思路就是我们存储已经拷贝的对象,当我们检测道这个对象已经拷贝之后,我们就不再递归直接取出改值使用

参考文献
深拷贝 主要参考了 https://muyiy.cn/blog/4/4.3.htm (其中还有关于拷贝 Symbol 递归爆栈的解法)

相关文章

  • 深浅拷贝详解

    深浅拷贝 浅拷贝 浅拷贝是指创建一个新的对象,把这个对象的原始属性精确拷贝一份,如果是基本类型就拷贝基本类似的值,...

  • 详解iOS的深浅拷贝

    上次面试被问到怎么去完全复制?懵逼了啊,之前完全没注意到过这个啊,所以就把问题记下来,好好花了个时间整理了一下,写...

  • iOS 中深浅拷贝详解

    先上个图注意:其他对象NSArray、NSMutableArray 、NSDictionary、NSMutable...

  • 2018-08-29

    深浅拷贝 1.根据拷贝内容的不同,分为深浅拷贝 深拷贝:内容...

  • js的深浅拷贝

    js的深浅拷贝可以分为数组的深浅拷贝和对象的深浅拷贝 一、数组的深浅拷贝如果只是简单的将数组中的元素付给另外一个数...

  • Python—浅拷贝与深拷贝

    浅拷贝 深拷贝 深浅拷贝的作用

  • 深浅拷贝

    先来了解一下这些:在js中变量类型基本分为两种:基本数据类型和引用数据类型;基本类型:Underfied Null...

  • 深浅拷贝

    浅拷贝是对一个对象的顶层拷贝。通俗的理解就是:拷贝了引用,并没有拷贝内容。 深拷贝是对于一个对象所有层次的拷贝(递...

  • 深浅拷贝

    深浅拷贝.copy 浅拷贝 这是一个方法,浅拷贝修改可变类型是会将原数据也一块修改 Copy.deepcopy 深...

  • 深浅拷贝

    1:is 和 ==的区别 is 是比较两个引用是否指向了同一个对象(地址引用比较)。== 是比较两个对象是否相等。...

网友评论

      本文标题:深浅拷贝详解

      本文链接:https://www.haomeiwen.com/subject/pdnmnktx.html