一方面,它是這樣的——傳遞給函數內部用的參數僅僅是綁定值(引用地址)的一個名稱,并不會影響外部的對象。
另一方面,如果不深入研究,這些術語真的被認為吃錯誤的,因為很多論壇都在說如何將對象傳遞給JavaScript函數)。
一般理論確實有按值傳遞的說法:但這時候這個值就是我們所說的地址拷貝(副本),因此并沒喲破壞規則。
在Ruby中,這個策略稱為按引用傳遞。再說一下:它不是按照大結構的拷貝來傳遞(例如,不是按值傳遞),而另一方面,我們沒有處理原始對象的引用,并且不能修改它;因此,這個跨術語的概念可能更會造成混亂。
理論里沒有像按值傳遞的特殊case一樣來面試按引用傳遞的特殊case。
但依然有必要了解這些策略在上述提到的技術中(Java, ECMAScript, Python, Ruby, other),實際上——他們用的策略就是按共享傳遞。
按共享與指針
對于С/С+ +,這個策略在思想上和按指針值傳遞是一樣的,但有一個重要的區別——該策略可以取消引用指針以及完全改變對象。但在一般情況下,分配一個值(地址)指針到新的內存塊(即之前引用的內存塊保持不變);通過指針改變對象屬性的話會影響阿東外部對象。
因此,和指針類別,我們可以明顯看到,這是按地址值傳遞。 在這種情況下,按共享傳遞只是“語法糖”,像指針賦值行為一樣(但不能取消引用),或者像引用一樣修改屬性(不需要取消引用操作),有時候,它可以被命名為“安全指針”。
然而,С/С+ +如果在沒有明顯指針的解引用的情況下,引用對象屬性的時候,還具有特殊的語法糖:
obj->x instead of (*obj).x
和C++關系最為緊密的這種意識形態可以從“智能指針”的實現中看到,例如,在 boost :: shared_ptr里,重載了賦值操作符以及拷貝構造函數,而且還使用了對象的引用計數器,通過GC刪除對象。 這種數據類型,甚至有類似的名字- 共享_ptr。
ECMAScript實現
現在我們知道了ECMAScript中將對象作為參數傳遞的策略了——按共享傳遞:修改參數的屬性將會影響到外部,而重新賦值將不會影響到外部對象。但是,正如我們上面提到的,其中的ECMAScript開發人員一般都稱之為是:按值傳遞,只不過該值是引用地址的拷貝。
JavaScript發明人布倫丹·艾希也寫到了:傳遞的是引用的拷貝(地址副本)。所以論壇里大家曾說的按值傳遞,在這種解釋下,也是對的。
更確切地說,這種行為可以理解為簡單的賦值,我們可以看到,內部是完全不同的對象,只不過引用的是相同的值——也就是地址副本。
ECMAScript代碼:
var foo = {x: 10, y: 20};var bar = foo; alert(bar === foo); // true bar.x = 100;bar.y = 200; alert([foo.x, foo.y]); // [100, 200]
復制代碼
即兩個標識符(名稱綁定)綁定到內存中的同一個對象, 共享這個對象:
foo value: addr(0xFF) => {x: 100, y: 200} (address 0xFF) <= bar value: addr(0xFF)
復制代碼
而重新賦值分配,綁定是新的對象標識符(新地址),而不影響已經先前綁定的對象 :
bar = {z: 1, q: 2}; alert([foo.x, foo.y]); // [100, 200] – 沒改變alert([bar.z, bar.q]); // [1, 2] – 但現在引用的是新對象
復制代碼
即現在foo和 bar,有不同的值和不同的地址:
foo value: addr(0xFF) => {x: 100, y: 200} (address 0xFF)bar value: addr(0xFA) => {z: 1, q: 2} (address 0xFA)
復制代碼
再強調一下,這里所說對象的值是地址(address),而不是對象結構本身,將變量賦值給另外一個變量——是賦值值的引用。因此兩個變量引用的是同一個內存地址。下一個賦值卻是新地址,是解析與舊對象的地址綁定,然后綁定到新對象的地址上,這就是和按引用傳遞的最重要區別。
此外,如果只考慮ECMA-262標準所提供的抽象層次,我們在算法里看到的只有“值”這個概念,實現傳遞的“值”(可以是原始值,也可以是對象),但是按照我們上面的定義,也可以完全稱之為“按值傳遞”,因為引用地址也是值。
然而,為了避免誤解(為什么外部對象的屬性可以在函數內部改變),這里依然需要考慮實現層面的細節——我們看到的按共享傳遞,或者換句話講——按安全指針傳遞,而安全指針不可能去解除引用和改變對象的,但可以去修改該對象的屬性值。
術語版本
讓我們來定義ECMAScript中該策略的術語版本。
可以稱之為“按值傳遞”——這里所說的值是一個特殊的case,也就是該值是地址副本(address copy)。從這個層面我們可以說:ECMAScript中除了異常之外的對象都是按值傳遞的,這實際上是ECMAScript抽象的層面。
或針對這種情況下,專門稱之為“按共享傳遞”,通過這個正好可以看到傳統的按值傳遞和按引用傳遞的區別,這種情況,可以分成2個種情況:1:原始值按值傳遞;2:對象按共享傳遞。
“通過引用類型將對象到函數”這句話和ECMAScript無關,而且它是錯誤的。
結論
我希望這篇文章有助于宏觀上了解更多細節,以及在ECMAScript中的實現。一如既往,如果有任何問題,歡迎討論。
原文轉自:http://www.anti-gravitydesign.com