JavaScript 中的深拷貝和淺拷貝

JavaScript 內(nèi)存中的堆和棧

棧(stack):堆是 JavaScript 用來存儲(chǔ)靜態(tài)數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)扶关。靜態(tài)數(shù)據(jù)是引擎在編譯時(shí)知道其大小的數(shù)據(jù)佛纫。截止 ES2021, 在 JavaScript 中奏候,這包括 7 種原始值(Primitive values)(string, number, boolean, bull, undefined, bigInt, symbol)和指向?qū)ο蠛秃瘮?shù)的引用哥力。

堆(heap):堆是一個(gè)不同的存儲(chǔ)數(shù)據(jù)的空間锣枝,JavaScript 在這里存儲(chǔ)對(duì)象和函數(shù)迎卤。

定義

將一變量的值賦值給另一個(gè)變量拴鸵,相當(dāng)于在棧內(nèi)存中創(chuàng)建了一個(gè)新的內(nèi)存空間,然后從棧中復(fù)制值蜗搔,存儲(chǔ)到這個(gè)新空間中劲藐。

1. 對(duì)于基本類型,棧中存儲(chǔ)的就是它自身的值樟凄,所以新內(nèi)存空間存儲(chǔ)的也是一個(gè)值聘芜。直接改變新變量的值,不會(huì)影響到舊變量的值缝龄,因?yàn)樗麄冎荡鎯?chǔ)的內(nèi)存空間不同汰现。
因此將基本變量 a 的值復(fù)制給另一個(gè)變量 b 時(shí),改變 b 的值并不會(huì)影響原來的變量 a 本身的值叔壤。

2. 對(duì)于引用變量瞎饲,棧中主要存儲(chǔ)它所引用的對(duì)象的地址。
因此炼绘,將引用變量a的值復(fù)制給另一個(gè)變量b時(shí)嗅战,實(shí)際上a和b都是指向堆中同一個(gè)內(nèi)存地址。因此改變b的值俺亮,相當(dāng)于改變b指向的堆中的值仗哨,因此a也會(huì)隨之改變。

所以我們一般談深拷貝和淺拷貝都是針對(duì)于引用類型而言的铅辞。因?yàn)獒槍?duì)值類型厌漂,它的拷貝結(jié)果都是深拷貝。

淺拷貝(shallow copy):當(dāng)將舊引用變量的值賦給新引用變量時(shí)斟珊,將舊引用變量中存儲(chǔ)的地址復(fù)制到新引用變量中苇倡。這意味著舊的和新的引用變量都指向內(nèi)存中的相同對(duì)象。因此囤踩,如果對(duì)象的狀態(tài)通過任何一個(gè)引用變量發(fā)生變化旨椒,它就會(huì)同時(shí)反映這兩個(gè)變量。

深拷貝(deep copy):深拷貝會(huì)復(fù)制舊對(duì)象的所有成員堵漱,為新對(duì)象分配單獨(dú)的內(nèi)存位置综慎,然后將復(fù)制的成員分配給新對(duì)象。這樣勤庐,兩個(gè)對(duì)象都是相互獨(dú)立的示惊,如果對(duì)其中一個(gè)進(jìn)行任何修改好港,另一個(gè)對(duì)象也不會(huì)受到影響。

簡而言之米罚,如果把對(duì)象 a 復(fù)制給另一個(gè)對(duì)象 b钧汹,如果修改 b,a 也隨之改變了录择,就是淺拷貝拔莱。相反,如果 a 維持原始值隘竭,則是深拷貝塘秦。

淺拷貝實(shí)現(xiàn)

先聲明如下一個(gè)引用類型對(duì)象person

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

`Object.assign(target, source1, source2...)`原本是將所有可枚舉屬性的值從一個(gè)或多個(gè)源對(duì)象分配到目標(biāo)對(duì)象。返回目標(biāo)對(duì)象动看。
通常用法:
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}
這里我們可用用來復(fù)制對(duì)象:
let shallowCopyPerson = Object.assign(person);
1.  遍歷賦值嗤形,這種方式雖然把對(duì)象進(jìn)行了遍歷,但是本質(zhì)還是復(fù)制的是對(duì)象的引用弧圆。

這幾個(gè)的復(fù)制的結(jié)果都是復(fù)制的都是對(duì)指針的復(fù)制赋兵,因此改變shallowCopyPerson的屬性后,原來對(duì)象person的屬性也會(huì)隨之改變搔预,結(jié)果如下:

shallowCopyPerson.name = "lucy"; // 改變復(fù)制后的值
console.log(shallowCopyPerson); // {name: 'lucy', job: 'developer'} 復(fù)制對(duì)象發(fā)生改變霹期。
console.log(person); // {name: 'lucy', job: 'developer'} 注意目標(biāo)對(duì)象自身也會(huì)改變。

深拷貝實(shí)現(xiàn)

其實(shí)有很多方法可以實(shí)現(xiàn)深拷貝拯田,這里我們簡單介紹幾種比較方便高效的历造。

  1. Object.assign({}, source);
    這個(gè)方法是和上面的淺拷貝實(shí)現(xiàn)稍有區(qū)別,他把 target 設(shè)置為空對(duì)象船庇,在將 source 對(duì)象傳進(jìn)去吭产,實(shí)現(xiàn)了復(fù)制
let deepCopyPerson = Object.assign({}, person);
  1. ...拓展運(yùn)算符(spread operator)
let deepCopyPerson = { ...person };
  1. JSON.parse/stringify
    這個(gè)方法的限制比較多,如果你需要拷貝的對(duì)象中沒有functions, undefined, Infinit或者像RegExps, Maps, Sets, Blobs, FileLists, ImageDatas等比較復(fù)雜的類型鸭轮。那么可以用這個(gè)方法臣淤。
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. 遍歷賦值
    我們可以通過遍歷原有對(duì)象,把里面的值一個(gè)一個(gè)重新賦值給新對(duì)象窃爷。
let shallowCopyPerson = {};
for (const key in person) {
  shallowCopyPerson[key] = person[key];
}
  1. structuredClone()方法
    這是 JS 官方定義的方法邑蒋。不過遺憾的是,目前主流瀏覽器都不支持這個(gè)方法按厘∫降酰可以參考structuredClone獲取最新的瀏覽器支持結(jié)果。
const shallowCopyPerson = structuredClone(person);
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末逮京,一起剝皮案震驚了整個(gè)濱河市卿堂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌懒棉,老刑警劉巖草描,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件览绿,死亡現(xiàn)場離奇詭異,居然都是意外死亡陶珠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門享钞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來揍诽,“玉大人,你說我怎么就攤上這事栗竖∈畲啵” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵狐肢,是天一觀的道長添吗。 經(jīng)常有香客問我,道長份名,這世上最難降的妖魔是什么碟联? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮僵腺,結(jié)果婚禮上鲤孵,老公的妹妹穿的比我還像新娘。我一直安慰自己辰如,他們只是感情好普监,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著琉兜,像睡著了一般凯正。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上豌蟋,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天廊散,我揣著相機(jī)與錄音,去河邊找鬼梧疲。 笑死奸汇,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的往声。 我是一名探鬼主播擂找,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼浩销!你這毒婦竟也來了贯涎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤慢洋,失蹤者是張志新(化名)和其女友劉穎塘雳,沒想到半個(gè)月后陆盘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡败明,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年隘马,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妻顶。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡酸员,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出讳嘱,到底是詐尸還是另有隱情幔嗦,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布沥潭,位于F島的核電站邀泉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏钝鸽。R本人自食惡果不足惜汇恤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拔恰。 院中可真熱鬧屁置,春花似錦、人聲如沸仁连。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饭冬。三九已至使鹅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間昌抠,已是汗流浹背患朱。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留炊苫,地道東北人裁厅。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像侨艾,于是被迫代替她去往敵國和親执虹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 深拷貝和淺拷貝 淺度拷貝:復(fù)制一層對(duì)象的屬性唠梨,并不包括對(duì)象里面的為引用類型的數(shù)據(jù)袋励,當(dāng)改變拷貝的對(duì)象里面的引用類型時(shí)...
    這些年你跑哪去了閱讀 83評(píng)論 0 1
  • 以下內(nèi)容來源于公眾號(hào)前端大全 基本數(shù)據(jù)類型和引用數(shù)據(jù)類型:基本數(shù)據(jù)類型的特點(diǎn):直接存儲(chǔ)在棧(stack)中的數(shù)據(jù)引...
    看見少年和猹閱讀 125評(píng)論 0 0
  • 深拷貝和淺拷貝是針對(duì)復(fù)雜數(shù)據(jù)類型來說的,淺拷貝只拷貝一層,而深拷貝是層層拷貝茬故。 深拷貝 深拷貝復(fù)制變量值盖灸,對(duì)于非基...
    a91afcace9ee閱讀 235評(píng)論 0 0
  • 在了解js中深拷貝及淺拷貝之前先讓我們看一下js的棧內(nèi)存和堆內(nèi)存 棧內(nèi)存的六種數(shù)據(jù)類型:String Number...
    灬小丑閱讀 233評(píng)論 0 0
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險(xiǎn)厭惡者,不喜歡去冒險(xiǎn)磺芭,但是人生放棄了冒險(xiǎn)赁炎,也就放棄了無數(shù)的可能。 ...
    yichen大刀閱讀 6,038評(píng)論 0 4