2015-11-17 32 views
4

我發現了一個奇怪的JS行爲:奇怪的JavaScript陣列同步技巧?

var myArray = [ [1] , [2] , [3] ]; 

var myArrayCopy = []; 

myArrayCopy.push(myArray[1]); 

alert(myArrayCopy); // 2, as expected. 

myArrayCopy[0][0] = 'foo'; 

alert(myArrayCopy); // 'foo', as expected. 

alert(myArray); // 1, foo, 3 = WTF ? :) 

Demo

請注意,如果我們直接使用值而不是數組,則這不起作用。

對我來說,它看起來像將數組推到數組中,翻譯成某種方式,就好像我們只推送那些數組的引用,而不是副本,(這不是人們所期待的行爲,如果我錯了,請更正) 。

有人可以解釋爲什麼嗎?

+1

數組傳遞給Ref,它不像你編輯一個ref,你正在編輯一個ref。你可以使用.slice()來獲得一個具有相同值的新容器(淺拷貝) – dandavis

+1

@dandavis - 你的評論在概念上是正確的,但不準確。在JavaScript中沒有* byRef *這樣的東西,但是傳遞一個對象(數組* *)會導致你描述的效果。 – Amit

+0

沒有爭論的語義,在js中,它與可變/不可變的數字,字符串,布爾值是不可變的,因此通過值傳遞,而可變值,又名對象,作爲對象引用傳遞。是迂腐的,objs在技術上並沒有通過ref傳遞,它是一個指向ref的指針,通過值傳遞,但這只是令人困惑,其效果是傳遞「byRef」... – dandavis

回答

2

別名。

您在myArrayCopy中引用了一個對myArray中包含的存在對象(數組)的引用。因此,當您修改該數組時,您正在修改從上述數組中引用的實際對象。

請注意,當您在JavaScript中傳遞對象時,它們不會被複制,而是傳遞給實例的引用並在接收器的範圍內修改它將導致修改後的對象位於來電者也是如此。 這條規則有一些明顯的例外(作爲原始類型的示例,它們不是對象,或者是在寫入時實際複製的字符串等等),但讓我們專注於您的問題。

考慮到一個數組是一個對象,它發生在下面的例子一樣,但它是從我的觀點更加清晰:

var o = { "foo": " bar" }; 
myArray[0] = o; 
myArrayCopy[0] = myArray[0]; 
o.foo = "nolongerbar"; 

是什麼返回myArrayCopy[0].foo?什麼myArray[0].foo

在大多數OO語言中稱爲別名,無論如何,這是處理對象引用時的常見錯誤。

編輯

哦,這不是在任何情況下同步的把戲,它通常是惱人的bug背後的原因。 :-)

+0

謝謝你的完整答案。特別是爲了指出「別名」這個術語! – lapin

+0

不客氣。這是一個廣泛使用的術語,儘管在關於JavaScript的討論中發現它並不常見。我不知道爲什麼,也許存在一個具有相同含義的術語,但我發現* aliasing *是最有意義的(至少對我來說當然是)。 – skypjack

2

myArray[1]的值是一個數組([2])。當您將其推入myArrayCopy.push(myArray[1]);中的myArrayCopy時,您將按數組,而不是它的內容([2]而不是2)。

當你稍後改變這個數組(myArrayCopy[0][0] = 'foo';)時,它在使用這個數組的任何地方顯然都有效果。

+0

我知道推送一個數組而不是它的內容,我的問題是,當我更改myArrayCopy [0] [0]值時,它是如何「明顯」,它對第一個數組也有影響!但我認爲其他答案可能已經回答了:) – lapin

+0

@lapin - 這是相同的數組。這就是我在我的回答中試圖解決的問題:*你推動數組,而不是內容*和*當你稍後改變這個數組*。這一直是同一陣列。 – Amit

0

是的,這是有道理的行動,因爲myArray 1和myArrayCopy [0]具有相同的內存地址。您可以檢查以下調試捕捉

enter image description here

通過線9號之後,那些已經在同一時間myArray的改變,myArraycopy的價值,是「富」

-2

這是完全正確的。

由於數組作爲對象,它存儲內存地址 而不是存儲值本身。

var myArray = [ [1] , [2] , [3] ]; 

myArray將存儲創建的數組的地址(比如addr-1)。

由於陣列中的元素本身是一個陣列,它現在將具有對應於陣列[1][2]三個地址,分別[3]addr-2addr-3addr-4

所以最後我們:

addr-1 = [addr-2,addr-3,addr-4] 

var myArrayCopy = []; 

這將存儲一個新的地址,說addr-5 = []

myArrayCopy.push(myArray[1]); 

,因爲你推的myArray的第一個元素myArrayCopy。它將存儲地址addr-3。這樣addr-5 = [addr-3]

`alert(myArrayCopy); // 2, as expected 

所以當然這工作正常爲addr-3 = [2]

myArrayCopy[0][0] = 'foo'; 

這裏要修改的myArrayCopy零個元素的零個元素的值,即你在addr-3改變的第一要素到foo

alert(myArrayCopy); // 'foo', as expected 

所以它的工作原理,以及:

alert(myArray); // 1, foo, 3 

myArray現在有addr-1 = [addr-2, addr-3, addr-4],其中addr-2 = [1]addr-3 = ["foo"]addr-4 = [3]

+1

你的回答看起來像是它的格式嗎? (看看周圍,人羣中***非常奇怪) – Amit

+0

我知道它不是,我第一次接電話,所以我只是在玩耍。 –