JavaScript 核心觀念(29)-物件-Call by Reference 還是 Call by Sharing
前言
接下來談談什麼是 Call by Reference 還是 Call by Sharing
Evaluation strategy (求值策略)
Evaluation strategy 簡單來講是程式語言的運作方式,在維基百科中可以看到以下名詞
- 嚴格求值(Strict evaluation)
- 應用次序(Applicative order)
- 傳值呼叫(Call by value)
- 傳參照呼叫(Call by reference)
- 傳共享物件呼叫(Call by sharing)
- 傳復件-恢復呼叫(Call by copy-restore)
- 部分求值(Partial evaluation)
- 非嚴格求值(Non-strict evaluation)
- 正常次序(Normal order)
- 傳名呼叫(Call by name)
- 傳需求呼叫(Call by need)
- 傳巨集展開呼叫(Call by macro expansion)
- 非確定性策略(Nondeterministic strategies)
- 完全β歸約(Full β-reduction)
- 傳預期呼叫(Call by future)
- 最佳求值(Optimistic evaluation)
但是在這邊主要是認識 傳值呼叫(Call by value)、傳參照呼叫(Call by reference)以及傳共享物件呼叫(Call by sharing)這三個
首先先講講 Call by value,Call by value 又稱之為 pass by value,在這邊我們先看一下下面的範例程式碼
1 | function fn (a, b) { |
上面的程式碼這邊簡單說明一下,我建立了兩個全域變數分別是 x
與 y
以及一個函式叫做 fn
並且可以傳入兩個參數,接下來我呼叫了 fn
並傳入 x
跟 y
。
接下來在 fn 中我們做了一些行為,首先建立了一個區域變數 temp
並且賦予值是 100
。
然後我們將傳入的參數 a
跟 b
分別做了修改,當我們修改完畢之後,請問 a
跟 b
的結果會如何?
1 | function fn (a, b) { |
在這邊我們函式的參數概念有點類似宣告一個新的變數,所以 a
跟 b
分別都會是不同的記憶體位置,上面這個概念就是 Call by value 概念,簡單來講就是傳入假的變數,實際上是建立另一個記憶體空間存放,更白話一點的說法就是,你不會因為修改另一個值而影響到原始的值。
接下來講講 Call by reference,Call by reference 與 Call by value 是相反的概念,在這邊傳入的值,如果修改是會影響到原有的變數,例如以下程式碼
1 | function fn (a) { |
看起來是非常符合 Call by reference 概念,那如果我們基於這個概念將 a 賦予一個新物件會發生何事呢?外層的 obj 也會跟著變嗎?
1 | function fn (a) { |
在這邊預期結果應該是要變成 { b: 100, c: 50 }
畢竟前面有講到 Call by reference 的概念簡單來講就是整個原始路徑參考過去,但這邊卻還是保持 { x: 10, y: 20, }
,這邊原因是為什麼呢?其實 JavaScript 在維基百科中是有說明它是屬於 Call by sharing,在這邊直接擷取維基百科的說法
如果要達成傳參照呼叫(Call by reference)的效果就需要傳一個共享物件,一旦被呼叫者修改了物件,呼叫者就可以看到變化(因為物件是共享的,沒有拷貝)。
這一段的意思如果我們套用到上面的程式碼來解釋的話,大概就是我們可以傳入一個共享物件到 fn
函式中並修改,並且我們也可以修改傳入的這個共享物件,將他修改成另一個物件,因此以後只要看到新的 { }
就代表這是一個新的 reference 唷。