拷贝

Object.assign

Object.assign(obj1,obj2,obj3,…)

作用:实现多个对象之间的合并/替换

  • 用 obj2 中的每一项替换 obj1「obj1被更改」
  • 再用 obj3 中的每一项替换被更改后的 obj1 「obj1有被修改了」
  • 最后修改的是obj1,assign 返回的也是 obj1 的内存地址

    替换规则:

    obj2替换obj1

  • 1和2都有的,以2为主

  • 1有2没有的,保留1的内容
  • 1没有2有的,给1中新设置这个成员

    1. let obj1 = {
    2. x: 10,
    3. y: 20
    4. }
    5. let obj2 = {
    6. x: 100,
    7. m: 30
    8. }
    9. let obj3 = {
    10. x: 1000,
    11. n: 40
    12. }
    13. let obj = Object.assign(obj1, obj2, obj3)
    14. console.log(obj) //{ x: 1000, y: 20, m: 30, n: 40 }
    15. console.log(obj1) //{ x: 1000, y: 20, m: 30, n: 40 }
    16. console.log(obj1 === obj) //true
    17. console.log(obj2, obj3) //没有更改过
    1. Object.assign 采用的是浅比较/浅合并
    2. let obj1 = {
    3. x: 10,
    4. y: {
    5. n: 20
    6. }
    7. }
    8. let obj2 = {
    9. z: 30,
    10. y: {
    11. m: 1000
    12. }
    13. }
    14. Object.assign(obj1, obj2)
    15. console.log(obj1) // { x: 10, y: { m: 1000 }, z: 30 }
    1. 实现 obj2 替换 obj1,但是不想修改 obj1 的内容「想实现对象的合并,但是不想改变任意一个对象,想返回一个新对象即可」
    2. let obj1 = {
    3. x: 10,
    4. y: 20
    5. }
    6. let obj2 = {
    7. x: 100,
    8. m: 30
    9. }
    10. let obj = Object.assign({}, obj1, obj2)
    11. console.log(obj, obj1, obj2)

    数组/对象的拷贝(克隆)

    数组的浅拷贝

  • arr.slice()

  • […arr]
  • arr.concat()
  • 迭代赋值
  • Object.assign([],arr)
  • … ```javascript let arr1 = [10, [20, 30, [40, 50]]]

let arr2 = arr1.slice() let arr2 = […arr1] let arr2 = arr1.concat() let arr2 = [] arr1.forEach(item => { arr2.push(item) })

let arr2 = Object.assign([], arr1) console.log(arr2 === arr1) //fasle console.log(arr2[1] === arr1[1]) //true

  1. <a name="d7AjV"></a>
  2. #### 对象的浅拷贝
  3. - { ...obj }
  4. - Object.assign({}, obj)
  5. - 迭代对象每一项,赋值给新对象
  6. - ...
  7. ```javascript
  8. let obj1 = {
  9. x: 10,
  10. y: {
  11. z: 20
  12. }
  13. }
  14. let obj2 = { ...obj1 }
  15. let obj2 = Object.assign({}, obj1)
  16. let obj2 = {}
  17. Reflect.ownKeys(obj1).forEach(key => {
  18. obj2[key] = obj1[key]
  19. })
  20. console.log(obj1, obj2)
  21. console.log(obj2 === obj1) //fasle
  22. console.log(obj2.y === obj1.y) //true

浅拷贝的特点:

原始对象(A)和拷贝后的新对象(B),首先是不同的堆内存,只是第一级的内容相同,但是A和B会共用第二级及更深层级的内存,没有实现完全断开联系…
![7~GL~NU}_]%1O)E0$QRVY3_tmb.jpg
但是真实项目中,往往需要的不是浅拷贝,而是深拷贝,让拷贝后的对象和原来的对象彻底断开联系

浅拷贝和深拷贝的区别

  • 浅拷贝:只拷贝第一级
  • 深拷贝:所有级别都需要拷贝,拷贝成全新的内存空间(只不过内容一样)

VC)D)U`_VEDKE~MUMTJ5A~S.jpg

深拷贝的方案:

基于 JSON 对象中的方法进行处理
  • 先基于 JSON.stringify 把原来的对象变为“json字符串”
  • 然后再基于 JSON.parse 把“json字符串”变为对象「此时会重新开辟“所有”需要的内存空间」

    1. let obj1 = {
    2. x: 10,
    3. y: [1, 2, { z: 3 }],
    4. m: {
    5. n: 20,
    6. k: {
    7. p: 30
    8. }
    9. }
    10. }
    11. let obj2 = JSON.parse(JSON.stringify(obj1))
    12. console.log(obj2)
    13. console.log(obj2 === obj1) //false
    14. console.log(obj2.m === obj1.m) //false
    15. console.log(obj2.m.k === obj1.m.k) //false
    16. console.log(obj2.y === obj1.y) //false
    17. console.log(obj2.y[2] === obj1.y[2]) //false

    但是 JSON 这种方案存在弊端(在基于 stringify 把对象变为字符串的时候):

  • 无法处理 BigInt 类型的值

  • 属性名是 symbol 类型,或者属性值是 undefined、symbol、function 类型的,都自动消失了
  • 属性值是 正则/Error 的实例,都自动变为 {}
  • 日期对象变为字符串后,再基于 parse 处理的时候,回不到对象格式了
  • 不支持对象的“套娃”操作(这里window就是典型套娃 window.window===window)

RLFJGL8J9NV0AQ@@@PO7RML.jpg
也就是JSON这种方案,只能用于处理:属性名是字符串、属性值是“数字、字符串、布尔、null、普通对象、数组对象”、而且没有套娃操作…这样的情况!!

  1. let obj = {
  2. name: '珠峰',
  3. age: 13,
  4. bool: true,
  5. n: null,
  6. u: undefined,
  7. sym: Symbol('sym'),
  8. big: 10n,
  9. list: [10, 20, { a: 100, b: 200 }],
  10. reg: /\d+/,
  11. time: new Date,
  12. err: new Error('xxx'),
  13. ke: { js: '基础课', web: '高级课', arr: [1000, 2000] },
  14. [Symbol('KEY')]: 100,
  15. fn: function () { }
  16. }
  17. obj.obj = obj
  18. let newObj = _.cloneDeep(obj)
  19. console.log(newObj)
  20. console.log(newObj === obj)
  21. console.log(newObj.ke.arr === obj.ke.arr)

image.png

基于循环和递归,一层层的进行拷贝

先放一个大概意思代码 后面完善~~

  1. function deepCloneArray(arr) {
  2. var clonedArray = [];
  3. for (var i = 0; i < arr.length; i++) {
  4. var item = arr[i];
  5. if (Array.isArray(item)) {
  6. clonedArray.push(deepCloneArray(item)); // 递归处理数组
  7. } else if (typeof item === 'object' && item !== null) {
  8. clonedArray.push(deepCloneObject(item)); // 递归处理对象
  9. } else {
  10. clonedArray.push(item);
  11. }
  12. }
  13. return clonedArray;
  14. }
  15. function deepCloneObject(obj) {
  16. var clonedObj = {};
  17. for (var key in obj) {
  18. var value = obj[key];
  19. if (Array.isArray(value)) {
  20. clonedObj[key] = deepCloneArray(value); // 递归处理数组
  21. } else if (typeof value === 'object' && value !== null) {
  22. clonedObj[key] = deepCloneObject(value); // 递归处理对象
  23. } else {
  24. clonedObj[key] = value;
  25. }
  26. }
  27. return clonedObj;
  28. }

合并

  1. let obj1 = {
  2. name: '张三',
  3. age: 25,
  4. hobby: {
  5. music: 100,
  6. jump: 80
  7. }
  8. }
  9. let obj2 = {
  10. name: '李四',
  11. age: 22,
  12. sex: 0,
  13. hobby: {
  14. read: 100,
  15. music: 90
  16. }
  17. }
  18. let obj3 = {
  19. name: '王五',
  20. age: 20,
  21. height: '158cm',
  22. score: {
  23. math: 100,
  24. chinese: 90
  25. }
  26. }

浅合并

  • let obj = Object.assign({}, obj1, obj2, obj3)

    深合并

  • let obj = _.merge({}, obj1, obj2, obj3) (使用lodash的merge方法)