关于javascript:JavaScript中的深拷贝和浅拷贝

2次阅读

共计 2301 个字符,预计需要花费 6 分钟才能阅读完成。

JavaScript 内存中的堆和栈

栈(stack):堆是 JavaScript 用来存储静态数据的数据结构。静态数据是引擎在编译时晓得其大小的数据。截止 ES2021, 在 JavaScript 中,这包含 7 种原始值 (Primitive values)(string, number, boolean, bull, undefined, bigInt, symbol) 和指向对象和函数的援用。

堆(heap):堆是一个不同的存储数据的空间,JavaScript 在这里存储对象和函数。

定义

将一变量的值赋值给另一个变量,相当于在栈内存中创立了一个新的内存空间,而后从栈中复制值,存储到这个新空间中。

1. 对于根本类型,栈中存储的就是它本身的值,所以新内存空间存储的也是一个值。间接扭转新变量的值,不会影响到旧变量的值,因为他们值存储的内存空间不同。因而将根本变量 a 的值复制给另一个变量 b 时,扭转 b 的值并不会影响原来的变量 a 自身的值。2. 对于援用变量,栈中次要存储它所援用的对象的地址。因而,将援用变量 a 的值复制给另一个变量 b 时,实际上 a 和 b 都是指向堆中同一个内存地址。因而扭转 b 的值,相当于扭转 b 指向的堆中的值,因而 a 也会随之扭转。

所以咱们个别谈深拷贝和浅拷贝都是针对于援用类型而言的。因为针对值类型,它的拷贝后果都是深拷贝。

浅拷贝(shallow copy):当将旧援用变量的值赋给新援用变量时,将旧援用变量中存储的地址复制到新援用变量中。这意味着旧的和新的援用变量都指向内存中的雷同对象。因而,如果对象的状态通过任何一个援用变量发生变化,它就会同时反映这两个变量。

深拷贝(deep copy):深拷贝会复制旧对象的所有成员,为新对象调配独自的内存地位,而后将复制的成员调配给新对象。这样,两个对象都是互相独立的,如果对其中一个进行任何批改,另一个对象也不会受到影响。

简而言之,如果把对象 a 复制给另一个对象 b,如果批改 b,a 也随之扭转了,就是浅拷贝。相同,如果 a 维持原始值,则是深拷贝。

浅拷贝实现

先申明如下一个援用类型对象person

let person = {name: "Emon", job: "developer"};
1. 间接赋值
let shallowCopyPerson = person;
2. Object.assign(target)

`Object.assign(target, source1, source2...)` 本来是将所有可枚举属性的值从一个或多个源对象调配到指标对象。返回指标对象。通常用法:
const targetObj = {a: 1, b: 2};
const sourceObj = {c: 3, d: 4};
Object.assign(targetObj, sourceObj); // {a: 1, b: 2, c: 3, d: 4}
console.log(targetObj); //{a: 1, b: 2, c: 3, d: 4}
这里咱们可用用来复制对象:
let shallowCopyPerson = Object.assign(person);
1.  遍历赋值,这种形式尽管把对象进行了遍历,然而实质还是复制的是对象的援用。

这几个的复制的后果都是复制的都是对指针的复制,因而扭转 shallowCopyPerson 的属性后,原来对象 person 的属性也会随之扭转,后果如下:

shallowCopyPerson.name = "lucy"; // 扭转复制后的值
console.log(shallowCopyPerson); // {name: 'lucy', job: 'developer'} 复制对象产生扭转。console.log(person); // {name: 'lucy', job: 'developer'} 留神指标对象本身也会扭转。

深拷贝实现

其实有很多办法能够实现深拷贝,这里咱们简略介绍几种比拟不便高效的。

  1. Object.assign({}, source);
    这个办法是和下面的浅拷贝实现稍有区别,他把 target 设置为空对象,在将 source 对象传进去,实现了复制
let deepCopyPerson = Object.assign({}, person);
  1. … 拓展运算符(spread operator)
let deepCopyPerson = {...person};
  1. JSON.parse/stringify
    这个办法的限度比拟多,如果你须要拷贝的对象中没有 functions, undefined, Infinit 或者像 RegExps, Maps, Sets, Blobs, FileLists, ImageDatas 等比较复杂的类型。那么能够用这个办法。
const a = {
  string: "string",
  number: 123,
  bool: false,
  nul: null,
  date: new Date(), // stringified
  undef: undefined, // lost
  inf: Infinity, // forced to 'null'
  re: /.*/, // lost, to {}
  fun: () => "hello world", // lost};
  1. 遍历赋值
    咱们能够通过遍历原有对象,把外面的值一个一个从新赋值给新对象。
let shallowCopyPerson = {};
for (const key in person) {shallowCopyPerson[key] = person[key];
}
  1. structuredClone()办法
    这是 JS 官网定义的办法。不过遗憾的是,目前支流浏览器都不反对这个办法。能够参考 structuredClone 获取最新的浏览器反对后果。
const shallowCopyPerson = structuredClone(person);
正文完
 0