# JavaScript - 傳值 ( by Value )、傳址 ( by Reference )、傳共享 ( by sharing ) ###### tags: `JavaScript` `六角學院` `JS直播班` ## 開始之前 在學習 JavaScript 路上,免不了要熟悉所謂 **傳值 ( call by value )** 以及 **傳址 ( call by reference )** 的觀念 ~~(當初因為不熟悉這鬼東西放棄JS好幾次)~~ 先感謝 **六角學院** 讓我透過直播班有機會探討這個主題,寫下第一篇公開的技術筆記。 這篇筆記我會以比較實作的方式,探討 **變數 ( Variable )、記憶體位址 ( Memory Address )、值 ( Value )** 之間的~~三角~~關係,讓我們開始吧 ! <br> ## 基本型別 ( Primity type ) 先讓我們暖身一下 : ```javascript= let a = 10; a += 1; console.log(a); // ? ``` 這個答案應該顯而易見 :::spoiler **>> 點我看答案 <<** --- ![](https://i.imgur.com/hh2rvd1.jpg) **a 的值會是 11** --- ::: <br> 那麼在記憶體中,這是怎麼呈現的呢 ? 讓我們來**畫個圖表** ! ==步驟 1.== `let a = 10;` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | 10 | | a | 0x1 | 0x0 | 其實這圖表就是在模擬 **變數** 與 **值** 在 **記憶體** 的存取流程,記憶體位址 0x0、0x1、0x2...,都是代表記憶體的某個空間位址,就像是你家的門牌啦 ! 步驟 1 會先在記憶體的某個空間位址,存放一個 10 的值,然後再將 變數 a 的值,指向這個 10 所在的記憶體位址,所以此時 console.log(a),會印出 number 型別的 10。 ~~りしれしゃさ小~~ 如果覺得太難懂,~~沒關係這不是你的問題~~,我來說個故事… 今天有一間教室 **(記憶體)**,教室裡某個桌上 **( 0x0 )**,放了 10 塊錢 **( number 10 )**,看了一下這桌子是誰的,原來是 a 同學的 **( 變數 a 的值指向 0x0 )**,也就是 a 同學擁有這 10 塊錢 ( **`let a = 10`** )。 --- ==步驟 2.== `a += 1` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | 10 | | a | 0x1 | ==**0x3**== | | | 0x2 | ==**1**== | | | 0x3 | ==**11**== | 此時 a 同學走在走廊上 ( **0x2** ),發現走廊地板有 1 塊錢 ( **0x2 的值為 1** ),於是 a 同學撿起來收進口袋 **( 變數 a 的值與 0x2 的值相加運算 )**,所以 a 同學此時有 11 塊 **( 在新的記憶體空間放進運算後的結果 11 這個值,並將變數 a 的值重新賦值指向至 0x3,求得值為 11 )**。 什麼 !? 你說我這樣說你聽不懂 ? :::spoiler **---** --- 對不起... 〒▽〒 ::: <br> --- **暖身完畢,緊接著我們來延伸上一個例子 :** ```javascript= let a = 10; let b = a; console.log(b); // 10 ``` 這個例子我們可以很直觀講出 console 印出來的結果是 10,因為 b 等於 a 嘛 ! 那我們來多加一行程式碼,將 a 重新賦值看看 : ```javascript= let a = 10; let b = a; a = 20; console.log(b); // ? ``` 過去初嚐 Javascript 的我,很直覺地說出「 那麼簡單,這我知道 ! `b` 會印出 20 啊,因為 `b` 等於 `a`,現在 `a` 重新給他一個 20 的值,所以 `b` 也是 20 對啊 ! 」 然後我默默開啟 devtools 的 console 試試,試完後,我表情面有難色,心裡OS : WTF... :::spoiler **>> 點我看答案 <<** --- ![devtool](https://i.imgur.com/fXXvLZe.jpg) **嗯... b 還是 10 呢** ~~(然後就放棄 JS 了)~~ --- :::   修但幾勒... 先不要這麼快放棄,我們一樣**畫個圖表**來一個一個步驟看 : ==步驟 1.== `let a = 10;` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | 10 | | a | 0x1 | 0x0 | 步驟 1 跟上一個例子的步驟 1 一樣,但我們換個故事來說... 今天有一棟宿舍 **(記憶體)**,在這棟宿舍發現有 10 個比特幣在某房間,看了一下房號,原來是 0x0 號 **(記憶體位址)** 啊 ! 這時來了一個人,他是 a 先生 **( `let a` )**,a 先生表示這房間 **( 0x0 )** 是他租的 **( 變數 a 的值指向 0x0 )**,也就是 a 先生擁有房間裡面這 10 個比特幣 **( `a = 10` )** ~~(羨慕~~ --- ==接下來步驟 2.== `let b = a;` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | 10 | | a | 0x1 | 0x0 | | ==**b**== | 0x2 | ==**0x0**== | 此時跳出一個 b 先生 **( `let b` )**,b 先生說他也租這房間 **( 變數 b 複製變數 a 的值 0x0 )**,也就是 a 先生和 b 先生是室友,他們其實是共同擁有這 10 個比特幣 **( `a === b // true` )** ~~(還是羨慕~~ --- ==最後步驟 3.== `a = 20;` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | 10 | | a | 0x1 | ==**0x3**== | | b | 0x2 | 0x0 | | | 0x3 | ==**20**== | 然後又在這棟宿舍的某個房間發現到 20 個比特幣,看一下房號,是 0x3 號 **(記憶體位址)**,a 看到有人發現這 20 個比特幣,手忙腳亂地衝過來解釋說:「哈哈是我的啦」,原來 a 先生日前跟 b 先生吵架,已經不租那間房間了,還跟 b 先生說:「沒關係那 10 個比特幣送你,我才不稀罕勒」 ~~(拜託送我)~~,反正 a 先生還有藏在 0x3 號房的 20 個比特幣 **( 變數 a 重新賦值指向到 0x3 )** :::info 從上面的故事最後可以得知,a 先生擁有 20 個比特幣 **( 變數 a 的值是 20 )**,b 先生擁有 10 個比特幣 **( 變數 b 的值是 10 )**。 ::: ![devtool](https://i.imgur.com/fXXvLZe.jpg) 當然,值換成 **字串型別 ( strig )** 也是一樣的結論 : ![devtool](https://i.imgur.com/EPSXjZo.jpg) <br> 理解之後,我們來做個稍微複雜的例子 : ```javascript= let a = 'A'; let b = 'A'; let c = 'A'; b = 'B'; c = 'C'; a += b; a += c; console.log(a, b, c); // ? ``` ( 截至 [Alex JS30 Day14 影片片段](https://youtu.be/sxe-oahUARI?t=1430) ) ==步驟1.== 一樣先畫圖表,將程式碼 1~3 行在記憶體的位址畫上去 : | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | 'A' | | a | 0x1 | 0x0 | | b | 0x2 | 0x0 | | c | 0x3 | 0x0 | 當然,為了控制記憶體不要大爆炸,宣告變數並賦予一個 **基本型別 ( Primitive type )** 的值時,會去找記憶體有沒有一模模一樣樣的值,若有一樣的,就 ==**複製**== 那個值的記憶體位址當作自己 ( 變數 ) 的值,就不會再重覆多做一個一樣的值,但若值是 **物件型別 ( Object type )** 的話,代誌丟毋係哩修欸價里甘丹,稍後會來探討。 ----- ==步驟2.== 程式碼第 4 行 `b = 'B';` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | 'A' | | a | 0x1 | 0x0 | | b | 0x2 | ==**0x4**== | | c | 0x3 | 0x0 | | | 0x4 | ==**'B'**== | 變數 b 重新賦值指向到新的值 'B' 所在的位址 0x4 --- ==步驟3.== 程式碼第 5 行 `c = 'C';` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | 'A' | | a | 0x1 | 0x0 | | b | 0x2 | 0x4 | | c | 0x3 | ==**0x5**== | | | 0x4 | 'B' | | | 0x5 | ==**'C'**== | 變數 c 重新賦值指向到新的值 'C' 所在的位址 0x5 --- ==步驟4.== 程式碼第 6 行 `a += b;` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | 'A' | | a | 0x1 | ==**0x6**== | | b | 0x2 | 0x4 | | c | 0x3 | 0x5 | | | 0x4 | 'B' | | | 0x5 | 'C' | | | 0x6 | ==**'AB'**== | 變數 a 原本的值是 'A' **( 0x0 )**,而 b 的值是 'B' **( 0x4 )**,字串相加連接後得到 'AB' 這個值 **( 在記憶體空間 0x6 產生出 'AB' )**,再賦予給 a,此時 a 的值是 'AB' **( 0x6 )** --- ==步驟5.== 程式碼第 7 行 `a += c;` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | 'A' | | a | 0x1 | ==**0x7**== | | b | 0x2 | 0x4 | | c | 0x3 | 0x5 | | | 0x4 | 'B' | | | 0x5 | 'C' | | | 0x6 | 'AB' | | | 0x7 | ==**'ABC'**== | 變數 a 在上一個步驟完成後的值是 'AB' **( 0x6 )**,而 c 的值是 'C' **( 0x5 )**,字串相加連接後得到 'ABC' 這個值,再賦予給 a,此時 a 的值是 'ABC' **( 0x7 )** 如此我們就能清楚得知 `console.log(a, b, c)` 的結果 : :::spoiler **>> 點我看答案 <<** --- ![](https://i.imgur.com/sTMqye9.jpg) **印出 ABC B C** --- ::: <br> :::success **畫圖表步驟中,這種複製某個值的行為就是大家所通稱的 「傳值 ( call by value )」。** ::: 其實前面有提到一個關鍵字 : ==**複製 ( copy )**== 以下 **基本型別 ( Primitive type )** 的值 : * 字串 string * 數字 number * 布林值 boolean * 空值 null * 未定義 undefined * 符號 symbol 在記憶體複製來複製去,就是 **傳值 ( call by value )** 的概念,不過我認為這只是一個概念而已,實際上還是用畫圖表的記憶方式會最印象深刻。 講了那麼多基本型別的傳值,接下來就讓我們來探討 **物件型別 ( Object type )** 怎麼在記憶體中運作吧 ! <br> ## 物件型別 ( Object type ) 除了 **基本型別 ( Primitive type )** 以外的值,都是屬於 **物件型別 ( Object type )**。 常見的 物件 Object { }、陣列 Array [ ]、函式 function( ) {...},皆是屬於**物件型別**。 為什麼 **陣列 Array** 和 **函式 function** 也是屬於物件型別 ? ~~關於這個問題,以後我們專門做一篇筆記講解~~。 :::danger 而 **物件型別 ( Object type )** 皆是以 **傳址 ( call by reference )** 的方式在記憶體中求值 ::: 其實上面這段話,單就技術名詞的定義解釋,是蠻有爭議的,怎麼說呢 ? 我們接下來做幾個實例 : ```javascript= let person1 = { name: 'Tim', }; let person2 = person1; person1.name = 'Ray'; console.log(person2.name); // ? ``` 啊啊我知道,別想再騙我了,person2 只是複製 person1 的值,所以這裡改了 person1 的 name 屬性,不會動到 person2,因為他們兩個是獨立的嘛 ! (得意) :::spoiler **>> 點我看答案 <<** --- ![devtools](https://i.imgur.com/tT2sj5C.jpg) ( OS : !%@#$^@&@#%@&ˇˋ& ) **嗯... 答案是 `'Ray'`,`person2.name` 的值也被動到了** ~~每個初學 JS 都要被騙過一次的題目~~ 什麼 ? 你問我有沒有被騙過 ? ~~當然被騙過,不然也不會放棄 JS 那麼多次...~~ --- ::: <br> 好,我知道你和我都一樣,心中有股怒火難以發洩,讓我們再用上面學到的畫圖表方式來拆解 : <br> ==步驟 1.== `let person1 = { name: 'Tim' };` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | { name: 'Tim' } | | person1 | 0x1 | 0x0 | 這裡不難看出來,變數 `person1` 的值是指向 0x0 的物件 `{ name: 'Tim' }` --- ==步驟 2.== `let person2 = person1;` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | { name: 'Tim' } | | person1 | 0x1 | 0x0 | | ==**person2**== | 0x2 | ==**0x0**== | 變數 `person2` 複製變數 `person1` 的值指向 0x0 這個物件 `{ name: 'Tim' }` --- ==步驟 3.== `person1.name = 'Ray';` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | ==**{ name: 'Ray' }**== | | person1 | 0x1 | 0x0 | | person2 | 0x2 | 0x0 | 現在我們要 **修改** 變數 `person1` 這個物件的屬性 name 的值為 `'Ray'`。 :::info 這邊講 **修改** 是因為我們使用 **特性存取( property access )**,也就是 `person1.name` 的 ` . ` 去取得值並且修改為 `'Ray'`( 或是使用 **鍵值存取 key access** : `person1['name']`,也是一樣 ),至於這個 `'Ray'` 到底有沒有多占用一個記憶體位址,我比較傾向理解是 `'Ray'` 霸佔了原本的 `'Tim'` 值所在的位置,然後因為 `'Tim'` 值沒有被使用到而被 JavaScript 的記憶體回收機制回收了,當然講這個太深入,~~而且我也不太懂~~,我們就先理解成**修改**或是**覆蓋**就好。 ::: 因為 person1 和 person2 都同樣指向 0x0 這個記憶體位址,因此當 0x0 位址的 name 屬性的值修改成 `'Ray'`,person1 和 person2 的值也都同樣是 `{ name: 'Ray' }` 這個物件。 :::success **這就是 JavaScript 中,大家所通稱的 「傳址 ( call by reference )」。** ::: <br> **但... 好像哪裡怪怪der...** 有沒有覺得步驟 2 很熟悉,跟我們講基本型別傳值的第一個例子,是一樣使用 ==複製== 的方式,person2 複製 person1 的值,那那那... 這不就是 **「傳值 ( call by value )」** ? ![meme1](https://i.imgur.com/YVBhJh5.jpg) 其實這麼理解也是沒什麼問題的,之所以會稱作這是 **call by reference** ,是因為複製的值是 **記憶體指向**,所以如果修改到這個記憶體位址 ( 0x0 ) 的值,person1 和 person2 傳出來的值都會跟著被改變,就有那種 **參考 ( reference )** 的意味在了。 你可能會問說,那 **基本型別 ( Primitive type )** 不也是 ==**複製記憶體指向**== 嗎 ? 我認為沒有錯,但其實基本型別的值若有變動,說**修改**比較不貼切,應該說是**賦予新的值**,這個新的值是會多占用一個記憶體空間的,也就可以稱作 **傳址 ( by reference )** 其實是 **傳值 ( by value )** 的其中一種方式。 :::info 但是為什麼不是**在一個記憶體空間創建新的值**,例如在 0x3 這個位址創建新的值是`{ name: 'Ray'}`,然後 person1 的值為指向 0x3,與 person2 的物件彼此獨立呢 ? ::: 別急,這不就來了嘛,讓我們修改一下剛剛那個例子 : ```javascript= let person1 = { name: 'Tim', }; let person2 = person1; person1 = { name: 'Ray' }; console.log(person2.name); // ? ``` 糾竟會印出什麼呢 ? :::spoiler **>> 點我看答案 <<** --- ![devtools](https://i.imgur.com/FkpFhuu.jpg) **答案是 `'Tim'`** ![meme2](https://i.imgur.com/OY9PpPy.jpg) --- ::: <br> 好,我知道你已經懶得吐槽了... 廢話不多說直接畫圖表 ! ==步驟 1.== `let person1 = { name: 'Tim' };` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | { name: 'Tim' } | | person1 | 0x1 | 0x0 | 變數 `person1` 的值一樣是指向 0x0 這個位址的值 ( 物件 `{ name: 'Tim' }` ) --- ==步驟 2.== `let person2 = person1;` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | { name: 'Tim' } | | person1 | 0x1 | 0x0 | | ==**person2**== | 0x2 | ==**0x0**== | 變數 `person2` 一樣複製變數 `person1` 的值,指向 0x0 這個位址的值 ( 物件 `{ name: 'Tim' }` ) --- ==步驟 3.== `person1 = { 'Ray' };` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | { name: 'Tim' } | | person1 | 0x1 | ==**0x3**== | | person2 | 0x2 | 0x0 | | | 0x3 | ==**{ name: 'Ray' }**== | 這邊就要注意了,當我們使用**物件實體語法 ( Object literal )**,也就是使用 { } 大括號包住 key and value ( `{ name: 'Ray' }` ),是會額外多占用一個記憶體空間 ( 0x3 ),然後將變數 `person1` 重新賦值指向記憶體位址 0x3。 :::info 這種創建新的值,並修改 person1 記憶體指向的行為,不就是「**傳值 ( by value )**」的概念嗎 ? ::: 也就是說,**物件型別 ( Object type )** 同時存在 **傳值** 及 **傳址** 兩個概念呢,同樣類似的情況也發生在 **函式 ( Function )** 中,讓我們來試試 : ```javascript= function add(a, b) { a += b; console.log(a); // 30 } let x = 10; let y = 20; add(x, y); ``` Function 內的 a 值為 30,怎麼求得的呢 ? ==步驟 1.== `let x = 10;` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | 10 | | x | 0x1 | 0x0 | --- ==步驟 2.== `let y = 20;` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | 10 | | x | 0x1 | 0x0 | | | 0x2 | 20 | | y | 0x3 | 0x2 | --- ==步驟 3.== `add(x, y);` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | 10 | | x | 0x1 | 0x0 | | | 0x2 | 20 | | y | 0x3 | 0x2 | | | 0x4 | ==**0x0**== | | ==**a**== | 0x5 | ==**0x4**== | | | 0x6 | ==**0x2**== | | ==**b**== | 0x7 | ==**0x6**== | 我們知道 function 的引數是區域變數,執行 `add(x, y);` 時,會將 x 和 y 的值**複製** **( 在 0x4 的空間複製 x 的值,在 0x6 的空間複製 y 的值 )**,並傳給 function 的引數 a 與 b 承接 **( a 的值指向 0x4,0x4 又指向 0x0,同理套用到 b )**。 --- ==步驟 4.== `a += b;` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | 10 | | x | 0x1 | 0x0 | | | 0x2 | 20 | | y | 0x3 | 0x2 | | | 0x4 | 0x0 | | a | 0x5 | ==**0x8**== | | | 0x6 | 0x2 | | b | 0x7 | 0x6 | | | 0x8 | ==**30**== | 將運算結果的值 30 存放到新的記憶體空間 **0x8**,並且**變數 a** 重新賦值指向到這個 **0x8**。 --- :::success **這種 function 傳入基本型別的值,就是使用「傳值 ( call by value )」的概念。** ::: <br> 那... 如果 function 是傳入 **物件 ( Object )** 呢 ? ```javascript= function updateObj(insideObj) { insideObj.name = 'Ray'; } let outsideObj = { name: 'Tim' }; updateObj(outsideObj); console.log(outsideObj); // ? ``` :::spoiler **>> 點我看答案 <<** --- ![devtools](https://i.imgur.com/pNxnAMj.jpg) 看來 `outsideObj` 這個物件裡面屬性 name 對應的值 `'Tim'` 成功被**修改**成 `'Ray'` 了 ! --- ::: <br> **畫圖拆解步驟 :** ==步驟 1.== `let outsideObj = { name: 'Tim' };` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | { name: 'Tim' } | | outsideObj | 0x1 | 0x0 | --- ==步驟 2.== `updateObj(outsideObj);` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | { name: 'Tim' } | | outsideObj | 0x1 | 0x0 | | | 0x2 | ==**0x0**== | | ==**insideObj**== | 0x3 | ==**0x2**== | 執行 `updateObj(outsideObj);` 時,會將 `outsideObj` 的值複製 ( 在 0x2 的空間複製 `outsideObj` 的值 ),並傳給 function 的引數 `insideObj` 承接 ( `insideObj` 的值指向 0x2,0x2 又指向 0x0 )。 --- ==步驟 3.== `insideObj.name = 'Ray';` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | ==**{ name: 'Ray' }**== | | outsideObj | 0x1 | 0x0 | | | 0x2 | 0x0 | | insideObj | 0x3 | 0x2 | 先前我們說過,`insideObj.name` 這種 **特性存取( property access )** 方式,會取得值並且**修改**他,而 `insideObj` 的值依然是指向 0x2,並且 0x2 的值指向 0x0,藉由對 `insideObj` 的修改,`outsideObj` 的值同樣也是指向 0x0,求得的值也就會是 **`{ name: 'Ray' }`**。 :::success **所以這邊 function 就是使用「傳址 ( call by reference )」的概念。** ::: <br> 那如果我偏偏要在 function 內把物件**重新賦值**呢 ? ```javascript= function updateObj(insideObj) { insideObj = { name: 'Ray' }; console.log('insideObj: ', insideObj); // ? } let outsideObj = { name: 'Tim' }; updateObj(outsideObj); console.log('outsideObj: ', outsideObj); // ? console.log('insideObj: ', insideObj); // ? ``` :::spoiler **>> 點我看答案 <<** --- ![devtools](https://i.imgur.com/dpd4eER.jpg) **`outsideObj` 這個物件沒有變動,而 `insideObj` 僅存在於 function 的塊狀作用域裡面,外面是取值不到的 ( `insideObj is not defined` )。** 這... 這該不會又是**傳值 ( by value )** ? --- ::: <br> **拆解步驟 :** ==步驟 1.== `let outsideObj = { name: 'Tim' };` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | { name: 'Tim' } | | outsideObj | 0x1 | 0x0 | --- ==步驟 2.== `updateObj(outsideObj);` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | { name: 'Tim' } | | outsideObj | 0x1 | 0x0 | | | 0x2 | ==**0x0**== | | ==**insideObj**== | 0x3 | ==**0x2**== | 到目前為止都與上一個例子的步驟一樣。 --- ==步驟 3.== `insideObj = { name: 'Ray' };` | 變數 | 記憶體位址 | 值 | |:----:|:----------:|:----:| | | 0x0 | { name: 'Tim' } | | outsideObj | 0x1 | 0x0 | | | 0x2 | 0x0 | | insideObj | 0x3 | ==**0x4**== | | | 0x4 | ==**{ name: 'Ray' }**== | 有沒有很熟悉,這就是先前提到,使用**物件實體語法 ( Object literal )**,也就是使用 { } 大括號包住 key and value ( `{ name: 'Ray' }` ),額外多占用一個記憶體空間 **( 0x4 )**,然後將變數 `insideObj` 重新賦值指向 **0x4**,此時 `insideObj` 和 `outsideObj` 就是兩個獨立的物件了,而 `insideObj` 只獨立在這個 function 塊狀作用域裡,`outsideObj` 則是在這個 function 外都能取值。 ## 總結 我們現在知道,**物件型別 ( Object type )** 可以 call by value 也可以 call by reference,所以也有人稱此為 : :::success **傳共享 ( call by sharing )** ::: 也就是根據**物件型別 ( Object、Array、Function )** 操作的「行為」不同,會求得不一樣的結果。 <br> ![meme3](https://i.imgur.com/4tqFCuG.jpg) 相信你跟我一樣有同樣的感受 ╰(‵□′)╯ <br> 不過我認為,不管是**傳值、傳址、傳共享**,充其量都只是一種**概念**,是方便我們在溝通時所使用的**代名詞**,但也因為對名詞的定義每個人都不盡相同,因此產生許多爭議。 所以我皆是以拆解步驟,並畫圖表的方式嘗試去理解 **變數、值、記憶體** 之間的~~三角關係~~。 上面所畫的圖表,在 JavaScript 並不是百分之百如此運作,但就是一個方便我們學習、記憶的方式,真的要深入的話,還需要探討到 Stack、Heap、淺拷貝、深拷貝,甚至是原型鍊~~這我還不知道是什麼碗糕小的東東~~。 以上這篇筆記是小弟我對 **傳值 ( call by value )、傳址 ( call by reference )、傳共享 ( call by sharing )** 粗淺的見解,若筆記中其中有矛盾或錯誤的地方,還請留言指教 `(*>﹏<*)′ ## 參考資料 * [深入探討 JavaScript 中的參數傳遞:call by value 還是 reference?](https://blog.huli.tw/2018/06/23/javascript-call-by-value-or-reference/) * [[筆記] 談談 JavaScript 中 by reference 和 by value 的重要觀念](https://pjchender.blogspot.com/2016/03/javascriptby-referenceby-value.html) * [[ Alex 宅幹嘛 ] 👨‍💻 深入淺出 Javascript30 快速導覽 | Day 14:JavaScript References VS Copying](https://youtu.be/sxe-oahUARI) * [[偷米騎巴哥]詳解傳值參考傳址參考差異](https://youtu.be/yXewP4SHXZ4) * [JavaScript’s Memory Model - Ethan Nam](https://medium.com/@ethannam/javascripts-memory-model-7c972cd2c239)