JavaScript 中的深拷贝和浅拷贝

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);
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容