<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
前言:
淺複製和深複製可以說是面試中很常見的一道題了,本文就來聊一聊JavaScript中的淺複製和深複製。
不知道會不會有人會和我一樣,會覺得淺複製就是通過=
操作符將一個變數賦值給另外一個變數。但實際上,淺複製和變數賦值之間是存在區別的。所以,我們先來了解一下變數賦值。
原始值(primitive value):最簡單的資料,即:Undefined、Null、Boolean、Number、String、BigInt、Symbol這其中型別的值。儲存原始值的變數是按值存取的,我們操作的就是儲存在變數中的實際值。
參照值(reference value):有多個值構成的物件,即 Object 型別。參照值是儲存在記憶體中的物件,但是 JavaScript 不允許直接存取記憶體位置,所以不能直接操作物件所在的記憶體空間。在操作物件時,實際操作的是該物件的參照(reference) 而非實際的物件本身。儲存參照值得變數實際上儲存得是物件得參照,是按參照存取得。
首先說明這裡說的賦值,不是直接把參照值(例如:{})或者原始值(例如:false、1、"str"等)直接賦值給一個變數。而是通過變數把一個值賦值給另一個變數。
原始值賦值:儲存原始值的變數是按值存取的,所以通過變數把一個原始值賦值給另一個變數時,原始值會被複制到新變數的位置。
let num1 = 5; let num2 = num1; console.log(num1, num2); // 5 5 num2 = 4; console.log(num1, num2); // 5 4
可以看出 num2
通過 num1
被賦值為5,儲存的是同一個原始值。而且兩個變數相互獨立,互不干擾。
具體賦值過程如下:
參照值賦值:儲存參照值的變數是按參照存取的,通過變數把一個參照賦值給另一個變數時,儲存在變數中的值也會被複制到新變數的位置。但是,這裡複製的實際上是一個指向儲存在堆記憶體中物件的指標。賦值後,兩個變數實際上指向同一個物件。所以兩個變數通過參照對物件的操作會互相影響。
let obj1 = {}; let obj2 = obj1; console.log(obj1); // {} console.log(obj2); // {} obj1.name = 'haha'; console.log(obj1); // { name: 'haha' } console.log(obj2); // { name: 'haha' } obj1.age = 24; console.log(obj1); // { name: 'haha', age: 24 } console.log(obj2); // { name: 'haha', age: 24 }
如上程式碼,通過 obj1
將指向物件的參照賦值給 obj2
後, obj1
和 obj2
儲存了指向同一物件的參照,所以操作的是同一物件。
具體可見下圖:
注:如上兩圖來自《JavaScript 高階程式設計(第四版)》
接下來要說的淺複製和深複製就是針對參照值而言的。
我們先來看一篇部落格中對於淺複製的定義:
An object is said to be shallow copied when the source top-level properties are copied without any reference and there exist a source property whose value is an object and is copied as a reference. If the source value is a reference to an object, it only copies that reference value to the target object.
對此,個人的理解淺複製就是複製該物件的的每個屬性,如果該屬性值是原始值,則複製該原始值,如果屬性值是一個物件,那麼就複製該物件的參照。
即:淺複製將複製頂層屬性,但巢狀物件在原始(源)和拷貝(目標)之間共用
Object.assign()
方法將所有可列舉(Object.propertyIsEnumerable()
返回 true)和自有(Object.hasOwnProperty()
返回 true)屬性從一個或多個源物件複製(淺複製) 到目標物件,返回修改後的物件。
如下程式碼可以看出,淺複製和變數賦值不同,修改物件的屬性值互不影響。
const source = { a: 1, b: 2 }; const objCopied = Object.assign({}, source); console.log(objCopied); // { a: 1, b: 2 } source.a = 3; console.log(source); // { a: 3, b: 2 } console.log(objCopied); // { a: 1, b: 2 } objCopied.a = 4; console.log(source); // { a: 3, b: 2 } console.log(objCopied); // { a: 4, b: 2 }
物件內的巢狀物件在源物件和拷貝物件之間還是共用的,如上程式碼,修改物件內物件的屬性時會相互影響。
const source = { a : {b : 1} }; const objCopied = Object.assign({}, source); console.log(objCopied); // { a: { b: 1 } } source.a.b = 3; console.log(source); // { a: { b: 3 } } console.log(objCopied); // { a: { b: 3 } }
但是注意如下程式碼中,source.a = {};
修改的是源物件中屬性的值,這個並不共用。
const source = { a : {b : 1} }; const objCopied = Object.assign({}, source); console.log(objCopied); // { a: { b: 1 } } source.a = {}; console.log(source); // { a: {} } console.log(objCopied); // { a: { b: 1 } }
展開運運算元(...):展開語法(Spread syntax), 可以在函數呼叫/陣列構造時, 將陣列表示式或者string在語法層面展開;還可以在構造字面量物件時, 將物件表示式按key-value的方式展開。
const source = { a : {b : 1}, c: 2 }; const objCopied = {...source} console.log(objCopied); // { a: { b: 1 }, c: 2 } source.c = 3; console.log(source); // { a: { b: 1 }, c: 3 } console.log(objCopied); // { a: { b: 1 }, c: 2 } source.a.b = 3; console.log(source); // { a: { b: 3 }, c: 3 } console.log(objCopied); // { a: { b: 3 }, c: 2 }
function shallowClone(source) { // 如果是原始值,直接返回 if (typeof source !== 'object') { return source; } // 拷貝後的物件 const copied = Array.isArray(source) ? [] : {}; // 遍歷物件的key for(let key in source) { // 如果key是物件的自有屬性 if(source.hasOwnProperty(key)) { // 複製屬性 copied[key] = source[key] } } // 返回拷貝後的物件 return copied; }
首先來看深複製的定義:
A deep copy will duplicate every object it encounters. The copy and the original object will not share anything, so it will be a copy of the original.
與淺複製不同時,當源物件屬性的值為物件時,賦值的是該物件,而不是物件的參照。所以深複製中,源物件和拷貝物件之間不存在任何共用的內容。
JavaScript 中最常見的深複製的方法就是JSON.parse(JSON.stringify(object))
如下程式碼所示,深複製中源物件和拷貝物件不共用任何內容,即使是巢狀物件。
let obj = { a: 1, b: { c: 2, }, } let newObj = JSON.parse(JSON.stringify(obj)); obj.b.c = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } }
function deepClone(source) { // 如果是原始值,直接返回 if (typeof source !== 'object') { return source; } // 拷貝後的物件 const copied = Array.isArray(source) ? [] : {}; // 遍歷物件的key for(let key in source) { // 如果key是物件的自有屬性 if(source.hasOwnProperty(key)) { // 深複製 copied[key] = deepClone(source[key]); } } return copied; }
有關淺複製和深複製的手動實現,這裡只是簡單實現了一下。其中還有很多細節未實現,具體的實現大家可以參見 lodash 中的實現。
到此這篇關於JavaScript 賦值,淺複製和深複製的區別的文章就介紹到這了,更多相關JS 賦值內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45