先上 MDN 的說明:
參數 parameter:A parameter is a named variable passed into a function. Parameter variables are used to import arguments into functions.
引數 argument:An argument is a value (primitive or object) passed as input to a function.
「函式參數(parameters)」是定義函式時所列出的變數,用來將引數導入至函式中;「函式引數(arguments)」則是實際上輸入至函式或是函式收到的值。
上述的例子中 name
和 age
是參數;'Alice'
和 28
則是引數。
undefined
如果定義函式時設定了參數,卻在呼叫函式時沒有傳入引數,函式仍然可以正常運作,不過引數的值會是 undefined
。
只有傳入一個引數也不會報錯,並且可由此得知 JavaScript 是由左至右讀取參數:
為什麼沒有傳入引數的時候,參數的值會是 undefined
呢?這是由於 Javascript 提升(hoisting)的特性。在函式的提升中,Javascript 在編譯時會先宣告函式的參數 name
和 age
,若參數沒有值則會賦予 undefined
的值。
如果想要避免在呼叫函式時沒傳參數(或是傳入 undefined
)導致出現 undefined
的狀況,ES6 允許我們在定義函式時為參數設定指定的預設值。
透過 =
為參數賦予預設值:
上面的例子中,greet()
沒有傳入任何引數,因此函式的參數值為預設值。
不過這樣的寫法不是所有瀏覽器都有支援(例如:IE),保險起見也可以使用另一種寫法:
由於在沒有帶入預設值的情況下,參數值為 undefined
。這時藉由 ||
運算子來為參數賦值:當 ||
左側 undefined
(falsy value) 被強制轉型成 false
時,會回傳 ||
右側的值,也就是我們希望的預設值(number = Alice
、age = 28
)。
其餘參數(rest parameters)可以讓我們用來表示不確定數量的參數,並將其視為一個陣列。
每當我們使用 ...args
作為最後一個傳遞給函式的參數時,表示剩餘的引數物件 (arguments object) 會被轉為陣列。...
為「其餘運算子(rest operator)」,args
可以是任何名稱。
上面的例子中,我們在定義函式 fn
時使用了其餘參數 ...args
。
當我們將 1,2,3 作為引數傳遞給函式 fn()
時, args
會將傳入的引數蒐集在一個陣列中,讓我們能夠以陣列的形式獲得 arguments 物件。
如此一來,我們就可以在 fn()
函式內部取用 args
陣列,並對其使用陣列的各種操作方法,像是reverse()
將陣列原地反轉,或是排序 sort()
、過濾 filter()
⋯等。
arguments
is an array-like object accessible inside functions that contains the values of the arguments passed to that function.
arguments
引數物件是傳入函式的一種類陣列物件(Array-like object),包含著「所有」傳入函式的值。
有時候在定義函式時我們並不確定需要設定幾個參數, arguments
讓我們在定義函式時就算未設定參數,仍然可以透過 arguments
物件取得呼叫函式時所傳入的引數。
這邊提到「arguments
會傳入『所有』傳入函式的值」的意思是,即使傳入的值多於函式所需參數,arguments
還是會接收所有傳入的值;如果傳入的值少於函式所需參數,也不會出現 undefined
的值。
傳入的值多於函式所需參數:
傳入的值少於函式所需參數:
arguments
vs. 陣列需要注意的是,「類陣列」只是長得很像陣列,實際上並不是真的陣列,因此不能使用 Javascript 陣列內建的操作方法。
sort
是陣列的一個方法,但 arguments
並非陣列,因此使用 arguments.sort
會報錯。
總結 arguments
和 array
的相同/相異之處:
0
為開始的索引值length
屬性可以用agruments
比陣列多了一個 callee
屬性:表示目前執行在哪個函式內arguments
並不是陣列,不能使用陣列的操作方法(像是 forEach
, map
…等)arguments
轉為陣列雖然 arguments
並不是陣列,我們仍然可以透過一些方法將 arguments
轉為陣列來使用陣列的操作方法。
Array.from()
將 arguments
轉為陣列ES6 提供了 Array.from()
方法,讓我們可以將物件轉為陣列:
arguments
轉為陣列如同上面介紹到關於其餘參數所提到的,如果函式的(最後一個)命名參數是以 ...
開頭,引數物件便會被視為一個陣列,此時便可以使用陣列的所有操作方法。
call()
將 arguments
轉為陣列使用 call
函式將 this
指向 arguments
物件:
上面的程式碼中,我們將 slice
函式所指向的 this
透過 call
函式來指向 arguments
物件;也可以看作將 arguments
物件當作 this
來呼叫 slice
函式。
由於 slice
函式沒有輸入引數,將回傳原陣列,所以 argsArray
是陣列,含有 3 個元素。
關於 Javascript 的 this
以及使用 call()
改變 this
指向,可以參考先前整理的筆記:Javascript - this 是誰、指向哪裡,以及 call、apply、bind
先來看看這個例子:
上面這個例子中,我們:
myName
fn
及其參數 myName
(也可以命名為任意的名字) ,並在函式內改變了參數 myName
的值fn(myName)
並 console.log
變數 myName
可以看到即使在函式內改變 arguments,也不會改變函式外部的變數的值。
再來看看這個例子:
但在這個例子中,外部變數 myObj
的值卻被函式內部改變了。
當我們在傳入引數時,需要注意函式傳入的引數是傳值(passed by value)還是傳址(passed by reference)。
如果傳入的 arguments 的值為基本型別的值(Primitive type),像是 null
, undefined
, boolean
, number
, string
…,則為「傳值(pass by value)」,那麼在函式中改變 arguments 的值並不會影響函式外的變數的值。
如果傳入的 arguments 的值為物件型別的值(Object type),像是 object
、array
、function
⋯,因為物件型別是以「傳址(pass by reference)」的方式傳遞,傳入函式的是該物件型別的變數的「記憶體參考位址」,因此在函式內改變了物件的值,函式外部的物件也會被改變。
只有當物件被賦予新的值的時候,才會再建立一個新的記憶體位置,此時就不再指向同一個位置: