要了解 pass by value, pass by reference, pass by sharing 的差別,我認為最主要的就是理解這些資料怎麼被儲存的。
我們都知道JS分為兩大類型,基本型別Primitive、物件型別Object。
基本型別 Primitive 包含的有:
物件型別 Object 包含的有:
JS儲存這兩種資料的方式不一樣,基本型別是屬於簡單資料;物件型別是屬於複雜資料。
在JS儲存的數據區分為Stack, Heap。
簡單資料會直接存在stack裡,而複雜資料是把資料存在Heap地址然後再指到Stack裡面。
Stack是空間小但存取速度快,而Heap空間大,適合存取Object複雜資料,當然存取速度較慢。
假如我有以下資料:
var primitiveA = "Hello";
var primitiveB = 20;
var objectA = [1,2,3];
var objectB = { name: "B" };
示範的儲存方式會是像:
(這裡只是一個概念,真實儲存的狀況會更複雜)
可以試想基本型別的值就是只有一個字串或者一個數字的值,但一個物件型別裡面可能有好多個字串、數字,所以他沒辦法直接被儲存在Stack裡面,必須要先存放在Heap裡面,再給Stack就儲存了他在Heap裡面的地址,所以物件指向的都是地址,而不是實際的值。
Primitive的資料是跟儲存位置綁在一起的,直接用值來傳遞。
範例:
var primitiveA = "Hello"
var primitiveB = primitiveA;
primitiveB = "World";
console.log(primitiveA);
// Hello
console.log(primitiveB);
// "World"
當創造了一個新的變數 primitiveB 的時候,就等於直接在Stack創了一個新的空間,只是值是跟變數 primitiveA 一樣而已,所以當這個變數重新被賦值新的字串的時候,他就是被改到 primitiveB 裡面的值,跟primitiveA 一點關係也沒有。
他們是一樣的東西嗎?
var primitiveA = "Hello"
var primitiveB = primitiveA;
console.log(primitiveA === primitiveB);
// true
Oh yes, 因為 "Hello" 就等於 "Hello" 啊,這個是在比較純粹的值。
套句008所說的,「10塊就是10塊,可以買的東西是一模一樣的」
object的資料不是跟儲存位置綁在一起的,而是用地址來傳遞。
var objectA = [1,2,3];
var objectB = objectA;
objectB.push(4)
console.log(objectA) // [1, 2, 3, 4]
console.log(objectB) // [1, 2, 3, 4]
賦值objectA給objectB的意思是給了objectB跟objectA一樣的參考地址,所以他們兩個現在會一起指向同一個地址。
所以對objectB做push()的時候,就會改變到objectA。
這邊要注意一件事情是現在還是在對原本的陣列做事情,幫陣列加一個值、減一個值、全部的值一起加1 等等,都是在改變原本的陣列,不是直接創造一個新的陣列
object的資料不是跟儲存位置綁在一起的,而是用地址來傳遞。
但如果現在不是對原陣列做事,而是直接賦值的話還會是原本的地址嗎?
var objectA = [1,2,3];
var objectB = objectA;
objectB = [2,3,4]
console.log(objectA) //[1,2,3]
console.log(objectB) //[2,3,4]
原本 objectA 跟 objectB 共享同一個地址,但是當一個新的陣列賦值給objectB的時候,這個新的陣列需要一個空間來存放,於是就產生了一個新的地址,指向objectB,這個時候 objectB 變斷開跟原本 objectA 共享地址的連結了。
objectA 跟 objectB 就已經指向不同的地址了,已經是不同的東西。
but 因為現在objectB裡面的value都是基本型別,所以裡面的內容不會是參考原本的reference,就像是完全不會被objectA影響,但如果裡面又是一個物件的話那就會還是參考objectA的reference!!
所以這個行為既有pass by value也有pass by reference,不知道如何去定義,混合出來的就是pass by sharing拉!
9/19更正:經過Chris的指導,發現我上面那個範例依然是pass by reference, 就只是創造了不同的reference而已。
而要講到pass by sharing,就得屏棄pass by value, pass by reference。
pass by sharing裏面不管是什麼型別,所有的值都有reference。
可以看到在addressC裡面的物件 a 的值是 1 ,而這個 1 又指向了addressB0的reference。(綠色線)
而addressB的陣列裡面各個值也都指向各自的reference。(括弧內)
(地址的名字是為了清楚而命名,並不是實際的名稱!)
總之傳值、傳址的事情會影響到很多的地方,所以算是最基礎的部分!要操作的時候要清楚現在是物件型別還是基本型別。
明天見囉~
資料來源:
JS 變數傳遞探討:pass by value 、 pass by reference 還是 pass by sharing?
Day26 X Memory Management In JavaScript
了解瀏覽器的棧內存 (Stack) & 堆內存 (Heap)
008 「傳址」還是「傳值」?
你不可不知的 JavaScript 二三事#Day26:程式界的哈姆雷特 —— Pass by value, or Pass by reference?