iT邦幫忙

2021 iThome 鐵人賽

DAY 13
0
Modern Web

每日挑戰,從Javascript面試題目了解一些你可能忽略的概念系列 第 13

每日挑戰,從Javascript面試題目了解一些你可能忽略的概念 - Day13

  • 分享至 

  • xImage
  •  
tags: ItIron2021 Javascript

前言

我們前兩天都把心力分給可愛的閉包,又是換換口味的時間囉! 友情提醒,今天的概念也會在未來繼續探討,所以千萬不要錯过今天的文章了!

本日題目與解釋

請解釋call by reference & by value間的差異

防雷防疫,你我有責

thinking-day13

這同樣也是個很常見的問題,在進入理論之前我們先做個簡單的測試

let a = 5
let b = a
let c = []
let d = c

function modifyArray(arr) {
  arr = {}
}

a = 10

c.push(1)

modifyArray(d)

看完上述的程式碼,請問a,b,c,d的數值分別為?

今天破天荒再給你一張防雷圖

最終的輸出結果為以下

a => 10
b => 5
c => [1]
d => [1]

若你對於其中任何一點感到震驚,那很抱歉你需要繼續往下看,至於全部答對的同學可以直接離開,把時間拿去看動漫吧! 最近Netflix上線第二季的新石紀囉! 以下進入正題

簡單的說,js中分成兩種主要的資料型別,分別為基本型別(primitive value)以及物件(Object)

其中基本型別包括: string, number, bigint(這玩意是新夥伴), boolean, undefined, symbol, and null。
除了這些以外全都是物件,這兩種主要的型別在比較、更新與傳值的方法有著明顯的差別。基本型別為call by value而物件則是call by reference(先別急著吐槽,我知道你想說什麼)

這兩種的差別在哪呢? 當你在複製或是比較基本型別時,它在乎的是該值本身,舉個簡單的例子

let a = 5
let b = a

console.log(a === b) // true

上述的程式碼並不是直接把a複製給b,而是先把a的值複製起來再賦值給b,也就是說a & b已經是完全獨立、不同的個體,這也就是為什麼一開始的範例會出現a = 10但b = 5的結果。

以上我們用最最最基本的方法(是的,實際上還要再更複雜一些)講解了什麼是call by value,那物件們的call by reference又是怎麼一回事呢? 簡單來說,當你宣告一個物件的時候會在記憶體建立一個新的物件,你可以把它想像成一個盒子,最終你再將你宣告的變數指向那個盒子。 配合一個簡單的例子來看吧!

let arr1 = []
let arr2 = arr1

上方的程式碼依序發生了以下的事情

  1. 建立一個新盒子,並把arr1指向這個盒子
  2. 將arr2指向arr1目前指向的盒子

也就是說,call by reference是一種用引用(或是參照)的方式在做值的傳遞,上述的例子中兩個陣列都指向同一個盒子,所以你對盒子做的任何操作都會影響到兩個陣列! 這也就是為什麼範例中明明是對c操作,但d最終仍有完全相同的結果。

最後我們來看一個最難懂的部分

let arr = []

function modifyArr(arr) {
  arr = [1,2,3]
}

modifyArr(arr)
console.log(arr) // []

這就奇怪了,我們剛剛才說過物件是call by reference,那這樣的資料更新為什麼沒有影響到arr陣列? 原因在於...js其實並不是真正的call by reference,有一派的說法是實際上js屬於call by sharing,名詞的部分我就不做解釋了,一樣你們自己去查。

簡單來說call by sharing的特點是,當該參數在函數中被重新賦值時,它其實會建立一個傳入參數的複製品並將其重新賦值,也就是說,一旦你選擇重新賦值傳進來的參數,傳進來的參數與原始的資料就完全沒有任何關係了,因此會造成範例中看到的結果,arr仍是一個空陣列。
我知道這有點難懂,不是我想偷懶,不過這系列文章本就不是要深入探討每一個觀念,你可以透過我提供的關鍵字去做搜尋!

本日核心觀念與總結

核心觀念

call by value、call by reference、call by sharing

總結

  • 了解js中主要分為基本型別與物件
  • 了解基本型別屬於call by value
  • 了解由於call by reference,相同參照的物件會互相影響
  • 了解物件屬於call by sharing (也有一派說法是不管是不是物件都是call by sharing)

本文章同步發布於個人部落格,有興趣的朋友也可以來逛逛~!

參考文章

關於今天的主題,中文資源部分我很推薦一篇文章:重新認識 JavaScript: Day 05 JavaScript 是「傳值」或「傳址」?,算是中文資源中講得相當不錯的一篇文章,當時也幫了我不少忙。


上一篇
每日挑戰,從Javascript面試題目了解一些你可能忽略的概念 - Day12
下一篇
每日挑戰,從Javascript面試題目了解一些你可能忽略的概念 - Day14
系列文
每日挑戰,從Javascript面試題目了解一些你可能忽略的概念30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
gior__ann
iT邦新手 2 級 ‧ 2021-10-06 16:04:56

想問一個問題~
/images/emoticon/emoticon16.gif因為太好奇了,所以改了 function 內容如下

let a = 5
let b = a
let c = []
let d = c

function modifyArray(arr) {
  arr = {}
  c = "123"
}

a = 10

c.push(1)

modifyArray(d)

console.log(a) // 10
console.log(b) // 5
console.log(c) // "123"
console.log(d) // [1]

想說如果不是修改 參數arr
而是直接修改 c >> 那是不是直接改到盒子 >> 但是 d 卻沒有一起被修改到
不太懂為甚麼?

@gior_ann 實際上你修改c的時候並沒有改到盒子,你宣告時只是讓c指向某個你建立的新盒子,同時讓d也指向同一個盒子,不管是c或d都不是盒子本身! 也因此你在重新賦值時就是讓c指向一個全新的位置,自然就跟原本的盒子一點關係都沒有囉!

gior__ann iT邦新手 2 級 ‧ 2021-10-07 09:43:06 檢舉

哈哈~原來如此!! 這麼解說我懂了~ 謝謝

0
Jimmy
iT邦新手 5 級 ‧ 2022-07-11 21:34:50

想討論個問題,剛好測試時發現


let a = 5
let b = a
let c = []
let d = c

function modifyArray(arr) {
    arr[0] = 80
}

a = 10

c[1] = 0;

modifyArray(d)

console.log(a) // 10
console.log(b) // 5
console.log(c) //[80,0]
console.log(d) //[80,0]

function帶入的參數跟變數d沒有關係,所以function實作裡參數指向誰都不會影響變數d,在指向其他物件.
但是帶入的依舊是變數d指向的這個物件,所以改動物件裡的屬性,故其他變數有指向該物件的,都會受影響

我要留言

立即登入留言