大家好,我是IT修真院成都分院第7期的學員韓建名,一枚正直純潔善良的WEB前端程序員。
1.背景介紹
在使用JavaScript對數組進行操作的時候,我們經常需要將數組進行備份,事實證明如果只是簡單的將它賦予其他變量,那么我們只要更改其中的任何一個,然后其他的也會跟著改變,這就導致了問題的發生。
var arr = ["One","Two","Three"];? ? ? ? ? ? ? ? var arrto = arr;? ? ? ? ? ? ? ? arrto[1] = "test";? ? ? ? ? ? ? ? document.writeln("數組的原始值:" + arr + "
");? ? ? ? ? ? ? ? //Export:數組的原始值:One,test,Three? ? ? ? ? ? ? ? document.writeln("數組的新值:" + arrto + "
");? ? ? ? ? ? ? ? //Export:數組的新值:One,test,Three
像上面的這種直接賦值的方式就是淺拷貝,很多時候,這樣并不是我們想要得到的結果,其實我們想要的是arr的值不變
2.知識剖析
什么是淺拷貝?
什么是深拷貝?
淺拷貝
淺拷貝就是把父對像的屬性,全部拷貝給子對象。
但是,這樣的拷貝有一個問題。那就是,如果父對象的屬性等于數組或另一個對象,那么實際上,子對象獲得的只是一個內存地址,而不是真正拷貝,因此存在父對象被篡改的可能。
只是拷貝了基本類型的數據,我們把這種拷貝叫做“淺拷貝”。
深拷貝
所謂”深拷貝”,就是能夠實現真正意義上的數組和對象的拷貝。它的實現并不難,只要遞歸調用”淺拷貝”就行了。
3.常見問題
如何實現深拷貝?
方法1
js的slice函數
對于array對象的slice函數, 返回一個數組的一段。(仍為數組) arrayObj.slice(start, [end]) 參數 arrayObj 必選項。一個 Array 對象。 start 必選項。 arrayObj 中所指定的部分的開始元素是從零開始計算的下標。 end 可選項。 arrayObj 中所指定的部分的結束元素是從零開始計算的下標。 說明 slice 方法返回一個 Array 對象,其中包含了 arrayObj 的指定部分。 slice 方法一直復制到 end 所指定的元素,但是不包括該元素。如果 start 為負,將它作為 length + start處理,此處 length 為數組的長度。如果 end 為負,就將它作為 length + end 處理,此處 length 為數組的長度。如果省略 end ,那么 slice 方法將一直復制到 arrayObj 的結尾。如果 end 出現在 start 之前,不復制任何元素到新數組中。
var arr = ["One","Two","Three"];? ? ? ? ? ? ? ? var arrtoo = arr.slice(0);? ? ? ? ? ? ? ? arrtoo[1] = "set Map";? ? ? ? ? ? ? ? document.writeln("數組的原始值:" + arr + "
");? ? ? ? ? ? ? ? //Export:數組的原始值:One,Two,Three? ? ? ? ? ? ? ? document.writeln("數組的新值:" + arrtoo + "
");? ? ? ? ? ? ? ? //Export:數組的新值:One,set Map,Three
方法2
js的concat方法
concat() 方法用于連接兩個或多個數組。該方法不會改變現有的數組,而僅僅會返回被連接數組的一個副本。 語法 arrayObject.concat(arrayX,arrayX,......,arrayX) 說明 返回一個新的數組。該數組是通過把所有 arrayX 參數添加到 arrayObject 中生成的。如果要進行 concat() 操作的參數是數組,那么添加的是數組中的元素,而不是數組。
var arrtooo = arr.concat();? ? ? ? ? ? ? ? ? ? arrtooo[1] = "set Map To";? ? ? ? ? ? ? ? ? ? document.writeln("數組的原始值:" + arr + "
");? ? ? ? ? ? ? ? ? ? //Export:數組的原始值:One,Two,Three? ? ? ? ? ? ? ? ? ? document.writeln("數組的新值:" + arrtooo + "
");? ? ? ? ? ? ? ? ? ? //Export:數組的新值:One,set Map To,Three
4.編碼實戰
請對以上示例代碼加以嘗試
5.擴展思考
(1)還有哪些方法可以實現數組的深拷貝
(2)深拷貝和淺拷貝的原理是什么
了解引用類型和基本類型
按照CEMA-262第3版的定義,js變量松散類型的本質,決定了它只是在特定時間用于保存特定值的一個名字而已。由于不存在必須定義變量數據類型的規則,變量的值及其數據類型可以在腳本生命周期內改變。盡管從某種角度看,這可能是一個既有趣又強大,同時又容易出問題的特性,但js變量實際的復雜程度還遠不止如此。 ECMAScript變量可能包含兩種不同數據類型的值:基本類型值和引用類型值。 基本類型值指的是那些保存在棧內存中的簡單數據段,即這種值完全保存在內存中的一個位置。 而引用類型值則是指那些保存在堆內存中的對象,意思是變量中保存的實際上只是一個指針,這個指針指向內存中的另一個位置,該位置保存對象。
在將一個值賦給變量時,解析器必須確定這個值是基本類型值,還是引用類型值。(Undefined、Null、Boolean、Number、String)這五種基本數據類型的值在內存中分別占有固定大小的空間,因此可以把他們的值保存在棧內存中。對于保存基本數據類型的變量,我們可以說他們是按值訪問的,因為我們操作的是它們實際保存的值。 如果賦給變量的是一個引用類型的值,則必須在堆內存中去為這個值分配空間。由于這種值的大小不固定,因此不能把它們保存到棧內存中。但內存地址的大小是固定的,因此可以將內存地址保存在棧內存中。這樣,當查詢引用類型的變量時,就可以首先從棧中讀取內存地址,然后在“順藤摸瓜”找到保存在堆中的值。對于這種查詢方式,我們把它叫做按引用訪問,因為我們操作的不是實際的值,而是被那個值所引用的對象。
6.參考文獻
Javascript 高級程序設計
7問題討論
Q : 王帥:slice方法可以支持倒著操作嗎,如何實現?
A:可以實現,倒著切片需要把參數設置為負數。
Q :常開洋:函數的參數是通過何種方式傳遞?
A :按值傳遞。