iT邦幫忙

2022 iThome 鐵人賽

DAY 2
0
Modern Web

前端蛇行撞牆記系列 第 2

Day2 前端蛇行撞牆記 - 今天要來點 pass by value、pass by reference 還是 pass by sharing呢?

  • 分享至 

  • xImage
  •  

要了解 pass by value, pass by reference, pass by sharing 的差別,我認為最主要的就是理解這些資料怎麼被儲存的。

我們都知道JS分為兩大類型,基本型別Primitive、物件型別Object。

基本型別 Primitive 包含的有:

  • number
  • string
  • boolean
  • null
  • undefined
  • symbol

物件型別 Object 包含的有:

  • Object
  • Number(new Number())
  • String(new String())
  • Boolean(new Boolean())

JS儲存這兩種資料的方式不一樣,基本型別是屬於簡單資料;物件型別是屬於複雜資料。

Stack & Heap

在JS儲存的數據區分為Stack, Heap。
簡單資料會直接存在stack裡,而複雜資料是把資料存在Heap地址然後再指到Stack裡面。

Stack是空間小但存取速度快,而Heap空間大,適合存取Object複雜資料,當然存取速度較慢。

假如我有以下資料:

var primitiveA = "Hello";
var primitiveB = 20;
var objectA = [1,2,3];
var objectB = { name: "B" };

示範的儲存方式會是像:
(這裡只是一個概念,真實儲存的狀況會更複雜)

  • Primitive: 儲存值在stack裡面。
  • Object: 儲存在Heap裡面,而stack儲存的是在Heap的地址。

可以試想基本型別的值就是只有一個字串或者一個數字的值,但一個物件型別裡面可能有好多個字串、數字,所以他沒辦法直接被儲存在Stack裡面,必須要先存放在Heap裡面,再給Stack就儲存了他在Heap裡面的地址,所以物件指向的都是地址,而不是實際的值。


Pass by value

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塊,可以買的東西是一模一樣的」

Pass by reference

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 等等,都是在改變原本的陣列,不是直接創造一個新的陣列

Pass by sharing

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拉!

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。(括弧內)

  • 1 指向 addressB0 (addressC的1也同樣指向這個reference)
  • 2 指向 addressB1
  • 3 指向 addressB2

(地址的名字是為了清楚而命名,並不是實際的名稱!)

  • 有pass by value, pass by reference 就沒有 pass by sharing
  • 這兩類要選一類去解釋,不能三個混合解釋!

總結

  • primitive是pass by value,object是pass by sharing
  • 不管型別,所有值都是reference就是pass by sharing

總之傳值、傳址的事情會影響到很多的地方,所以算是最基礎的部分!要操作的時候要清楚現在是物件型別還是基本型別。

明天見囉~


資料來源:
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?


上一篇
Day1 前端蛇行撞牆記 - 為什麼要參加鐵人賽?
下一篇
Day3 前端蛇行撞牆記 - 深拷貝、淺拷貝
系列文
前端蛇行撞牆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
南國安迪
iT邦新手 3 級 ‧ 2022-09-17 11:37:26

圖也太精美

jadddxx iT邦新手 5 級 ‧ 2022-09-17 11:43:05 檢舉

繪畫,我還有用box-shadow

錄取了

0
NoZ
iT邦新手 4 級 ‧ 2022-09-18 13:30:40

有圖解釋整個概念好清楚/images/emoticon/emoticon32.gif

jadddxx iT邦新手 5 級 ‧ 2022-09-18 22:49:08 檢舉

圖也是花了不少心力..謝謝育瑄/images/emoticon/emoticon25.gif

我要留言

立即登入留言