js深拷贝和浅拷贝

目录

一、概念

1、值传递和引用传递

2、深拷贝和浅拷贝

二、浅拷贝

1、‘=’ 赋值

2、Object.assign()

3、解构赋值

4、concat()

5、slice()

三、深拷贝

1、JSON.stringify 和 JSON.parse

四、手写深拷贝-深度遍历


一、概念

1、值传递和引用传递

值传递的是基本数据类型(Number,String,Boolean,Null,Undefined),直接用 = 传递,一般存放于内存中的栈区,存取速度快,存放量小;数据大小确定。

引用传递的是引用类型(Object,Array,Function,Symbol),一般存放与内存中的堆区,存取速度慢,存放量大,其引用指针存于栈区,并指向引用本身。

2、深拷贝和浅拷贝

深拷贝和浅拷贝是相对于引用类型而言的,因为基本类型赋值都是深拷贝。

浅拷贝: 对于对象来说,浅拷贝是对对象地址的复制, 也就是拷贝的结果是两个对象指向同一个地址指两指两个js 对象指向同一个内存地址,其中一个改变会影响另一个.

深拷贝: 深拷贝开辟一个新的栈,两个对象对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。指复制后的新对象重新指向一个新的内存地址,两个对象改变互不影响。

二、浅拷贝

浅拷贝是对对象地址的复制, 也就是拷贝的结果是两个对象指向同一个内存地址。

1、‘=’ 赋值

var a = {
  name: "a",
  book: [1, 2, 3],
};
var b = a; // 指向同一个地址
b.name = "b";
b.book[0] = 100;
console.log(a); // { name: 'b', book: [ 100, 2, 3 ] }

2、Object.assign()

Object.assign()  方法将所有可枚举Object.propertyIsEnumerable() 返回 true)和自有Object.hasOwnProperty() 返回 true)属性从一个或多个源对象复制到目标对象,返回修改后的对象。

Object.assign() 进行的是浅拷贝,但是第一层处理的是深拷贝。

var target = { a: { a: "aaaa", b: 21 }, name: "a" };
var source = { a: { a: "bbbb", b: 21 }, name: "b" };
var initalObj = Object.assign(target, source);
console.log(initalObj);   // { a: { a: 'bbbb', b: 21 }, name: 'b' }
source.a.a = "changed";
source.name = "c";
console.log(initalObj.a === source.a);  // true
console.log(source); // { a: { a: 'changed', b: 21 }, name: 'c' }
console.log(initalObj); // { a: { a: 'changed', b: 21 }, name: 'b' } name没被修改

3、解构赋值

对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。

解构赋值的拷贝是浅拷贝

var source = { x: 1, y: 2, a: 3, b: 4 };
var { x, y, ...z } = source;
console.log(z); // { a: 3, b: 4 }

var obj = { a: { b: 1 } };
var { ...o } = obj;
obj.a.b = 2;
console.log(o); // { a: { b: 2 } }

4、concat()

不修改原数组,拷贝原数组生成一个新数组(堆里面内存开辟一个新空间),数组内部浅拷贝。

var arr1 = [10, 20, { a: 1 }];
var arr2 = [30, 40, { b: 2 }];
var newArr = arr1.concat(arr2);
console.log(newArr);  // [ 10, 20, { a: 1 }, 30, 40, { b: 2 } ]
console.log(arr1);  // [ 10, 20, { a: 1 } ]   不影响原来的数组
arr1[2].a = 3;
arr2[2].b = 4;
console.log(newArr);  // [ 10, 20, { a: 3 }, 30, 40, { b: 4 } ]

5、slice()

不修改原数组,返回新数组对象,由 begin 和 end 决定的原数组的浅拷贝。

var arr1 = [10, 20, { a: 1 }, 30, 40, 50, 60];
var newArr = arr1.slice(1, 4);
console.log(newArr);  // [ 20, { a: 1 }, 30 ]
console.log(arr1);  // [ 10, 20, { a: 1 }, 30, 40, 50, 60 ]
arr1[2].a = 3;
console.log(newArr);   // [ 20, { a: 3 }, 30 ]

三、深拷贝

1、JSON.stringify 和 JSON.parse

用 JSON.stringify 把对象转换成字符串,再用 JSON.parse 把字符串转换成新的对象。

var obj = {
  name: "aaa",
  value: "JS",
};
var newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj === obj);    // false
newObj.name = "bbb";
console.log(newObj);    // { name: 'bbb', value: 'JS' } 深拷贝了一份obj
console.log(obj);     // { name: 'aaa', value: 'JS' }

缺点:

  • 如果obj里面有时间对象Date,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是时间对象。
  • 如果obj里有function,Symbol类型,undefined,则序列化的结果会把函数、Symbol类型、undefined丢失
  • 如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
  • 如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象
var oldObj = {
  a: "1",
  b: undefined,
  c: null,
  d: {
    child: "child",
  },
  e: Symbol(),
  f: NaN,
  g: Infinity,
  h: -Infinity,
  i: /runoob/i,
  date: new Date(),
  fn: function () {
    console.log("ok");
  },
};
const newObj = JSON.parse(JSON.stringify(oldObj));
console.log(oldObj);
console.log(JSON.stringify(oldObj));
console.log(newObj);

// 输出:
{
  a: '1',
  b: undefined,
  c: null,
  d: { child: 'child' },
  e: Symbol(),
  f: NaN,
  g: Infinity,
  h: -Infinity,
  i: /runoob/i,
  date: 2022-07-21T03:17:09.820Z,
  fn: [Function: fn]
}
{"a":"1","c":null,"d":{"child":"child"},"f":null,"g":null,"h":null,"i":{},
"date":"2022-07-21T03:17:09.820Z"}
{
  a: '1',
  c: null,
  d: { child: 'child' },
  f: null,
  g: null,
  h: null,
  i: {},
  date: '2022-07-21T03:17:09.820Z'
}

四、手写深拷贝-深度遍历

function deepClone(obj) {
  if (obj && typeof obj === "object") {
    let result = obj instanceof Array ? [] : {};

    for (var i in obj) {
      if (obj.hasOwnProperty(i)) {
        if (obj[i] instanceof Date) {
          // Date循环没有内容,走deepClone(obj[i])值会变为{}
          result[i] = obj[i];
        } else if (obj[i] instanceof RegExp) {
          result[i] = new RegExp(obj[i]);
        } else {
          result[i] = obj[i] instanceof Object ? deepClone(obj[i]) : obj[i];
        }
      }
    }
    return result;
  } else {
    return obj;
  }
}

var oldObj = {
  a: "1",
  b: undefined,
  c: null,
  d: {
    child: "child",
  },
  e: Symbol(),
  f: NaN,
  g: Infinity,
  h: -Infinity,
  i: /runoob/i,
  date: new Date(),
  fn: function () {
    console.log("ok");
  },
};
console.log(oldObj);
console.log(deepClone(oldObj));
{
  a: '1',
  b: undefined,
  c: null,
  d: { child: 'child' },
  e: Symbol(),
  f: NaN,
  g: Infinity,
  h: -Infinity,
  i: /runoob/i,
  date: 2022-07-21T03:24:33.689Z,
  fn: [Function: fn]
}
{
  a: '1',
  b: undefined,
  c: null,
  d: { child: 'child' },
  e: Symbol(),
  f: NaN,
  g: Infinity,
  h: -Infinity,
  i: /runoob/i,
  date: 2022-07-21T04:00:52.884Z,
  fn: [Function: fn]
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值