既然都是要寫JavaScript的資訊,不免俗的要來 水一篇 好好探討一下傳值還是傳址。
我們都知道我們的資料都是被存放在記憶體裡面的,但這時候我就很好奇為什麼JS 引擎是如何為我們保留記憶體位置的?
在進入by value 和 by reference 的觀念前我們先來看一段簡單的程式:
1 | var a = 3; |
答案會是:
1 | a is 2 |
感覺很直觀,一點都不難啊,不要急我們再往下走:
1 | var c = {greet : "Hello!"}; |
這個時候結果會變成:
1 | Object {greet : "你好"} |
疑?為什麼這裡兩個都會變成剛剛更改的?明明第一個案例a不會引響b,但為什麼第二個案例c會引響d呢?
這就是接下來我們要說明的地方。
Call By Value
當我今天創建的變數a的值是 primitive type 時,a會在記憶體中有自己的位置(0x001),當這時我打上b = a時,b會先有一個自己的記憶體位置(0x002),然後在把a的值放在自己的記憶體裡面。
因此a 和 b 雖然值一樣,但他們存在於兩個不同的記憶體位置,因此並不會乎相干擾影響。
這種情況,我們就稱為 Call By Value,而這種情形會發生在 primitive type 的變數。
Call By Reference
當我今天創建的變數a的值是object時,a一樣會在記憶體中有自己的位置(0x001),當這時我打上b = a時,b就會跟之前不一樣,b不會創建一個新的記憶體位置而是指向a的記憶體位置。
因此當 a 的值改變的時候 b 的值也會改變,因為它們實際上是指稱到相同的位置。
這種情形我們就稱為 Call By Reference,這樣情況會發生在 Object 和 Function 這種變數。
一般來說,Primitive type 是 by value,而 Object 和 Function 則是 by reference,但如果只是這樣Call by value & Call by reference就不會引起這麼大的討論跟困惑。
例外情況
今天如果我們是使用 object literal 的方式指定物件的值,就不是by reference 而是會變成 by Value,如下面範例:
1 | var a = { number: 1 }; |
結果如下:
1 | { number: 1 } |
在這裡,如果我們是用 object literal 的方式去定義 a這個變數,在這種情況底下,因為它並不清楚 a 的內容是已經存在的,所以它會建立一個新的記憶體位置來存放 c 物件裡面的內容。
JavaScript是哪一類型?
看完上面的案例後,是不是滿腦困惑,所以既不是 call by value 也不是 call by reference,那這樣應該叫做什麼呢?
看了很多資料,看到一個名詞:call by sharing。
sharing有「共享」的意思,對於Primitive type來說,看起來會很像 by value,對於object來說則會很像 by reference。這也是目前大多數人的結論。
但與其在爭論by value 還是 by reference又或者是 by sharin,我更喜歡huli的結論是:與其由定義來看,不如直接從行為來加以區分,不同種類能夠達成的行為都不一樣。
- 第一個條件用來區分到底是 by value 還是 by reference:「在函式裡對引數重新賦值,外面變數是否會改變?」
- 第二個條件來區分這個 by value 是真by value 還是一個叫做 by sharing 的分支:「能否透過引數,改變外部變數的值」(我們這邊所指的「值」跟地址或引用無關,純粹在講像
{numer:1}
這樣子的值)
有興趣的朋友可以點進下方的參考資料看原作者的文章
參考資料:
https://blog.techbridge.cc/2018/06/23/javascript-call-by-value-or-reference/