前言
這篇文章會對參數的各種狀態進行更詳細的介紹
函式在運行之前,會帶入哪些參數
var globalVariables = '全域變數';
var obj = {
afunction: function (para) {
var localVariables = '區域變數';
console.log(para, localVariables, arguments, this, globalVariables);
}
};
// 執行
obj.afunction('一段描述', 2, 3);
這段程式碼在執行的時候帶入的參數是3個,分別是 '一段描述'、 2 、 3 ,但接受的 function 只有 para
一個參數,也就是說 para 這個參數只會對應到 '一段描述' 這個字串,其他的2、3 沒有傳進去。
我們來看一下執行的結果
我們可以看到 localVariables 以及 globalVariables 都有對應到正確的值,而全域變數之前的章節也有討論過,如果找不到就
會向外尋找直到找到為止,不然就回傳 not defined。
this 的部分後面會再詳細的討論,這裡就先跳過。
重點是又多了一個 arguments 的變數,而且他好像是一個陣列,裝著 '一段描述', 2, 3 的三個值。
但其實這個 arguments 被稱作 「類陣列」 ,但其實他不是陣列。雖然它可以被 for 迴圈處理內部的資料,但對於大部分陣列操
作的指令他都無法執行。
Hoisting 影響參數的運行
function callName (a) {
console.log(a); // 小明
}
callName('小明');
上述執行的結果很明顯答案就是 小明
那麼如果改成下面這樣呢?
function callName (a) {
console.log(a); // 小明
var a;
console.log(a); // 小明
}
callName('小明');
執行起來也還是 小明 喔!原因是因為,我們再宣告一個變數的時候,這個變數名稱已經存在的時候,這個宣告就是無效的,
所以 var a; 是無效的,自然 a 就還是保持在 小明 的狀態。
那接下來我們把 a 改成 '杰倫' 呢?
function callName (a) {
console.log(a); // 小明
var a;
console.log(a); // 小明
a = '杰倫';
console.log(a); // 杰倫
}
callName('小明');
這時候就會是 '杰倫' 了,所以要改變內部的參數,直接覆蓋就可以了!
如果我今天在裡面定義的是函式陳述式呢?
根據之前學過的觀念是,函式陳述式會最早被寫入在記憶體空間中。我們來實際操作看看
function callName (a) {
console.log(a);
function a () {}
var a;
console.log(a);
a = '杰倫';
console.log(a);
}
callName('小明');
很明顯在 a 還沒被 '杰倫' 覆蓋之前,傳入的 '小明' 就被函式陳述式給附蓋了,所以雖然函式陳述式會最早被寫入在記憶體
空間中,但最早是指在這個函式內的 { } 的最早,因此函式陳述式並不會比 '小明' 這個參數還早,所以顯示的結果才是函式
陳述式。
定義的參數名稱的規則
定義函式接收參數名稱的時候,注意的只有參數的數量,參數的名稱都是可以自定義的,只要不要重複就好。
我們接下來來看看下面的例子
function callMore (d,c,b,a){
console.log(d,c,b,a);
}
var a = 'a';
var b = 'b';
var c = 'c';
callMore(a, b ,c);
目前雖然傳了 3 個參數進去,也就是 callMore(a, b ,c); ,但函式接受的參數定義是 4 個,
也就是 function callMore (d,c,b,a) ,因此 function callMore (d,c,b,a) 最後一個 a
就會在函式內被定義為 undefined
傳入參數是物件類型的資料,依然會維持傳參考的特性
function callObject (obj) {
obj.name = '杰倫家';
}
var family = {
name: '小明家'
}
callObject(family);
console.log(family);
上面這個範例,執行後的結果很明顯就是 family.name 會被改成 '杰倫家'
這邊也要提醒一下,盡量避免傳入物件類型的資料後,又在函是內部修改資料內容,避免不預期的結果。當然也是有人利用這種
特性進行撰寫,但其實這樣的撰寫風格不易追蹤,維護成本也較高,因此不太建議這麼寫。
CallBack Function
function FnB (fn) {
fn('小明');
}
FnB(function (a) {
console.log(a);
});
這樣的函式直接執行,我們就先把參數預定在 function FnB 中,等到執行的時候,傳入ㄧ個 function 到
FnB (function( ){ }),而 FnB (function( ){ }) 就會接收到 function FnB 中的參數,並做ㄧ些處理。
我們也可以改寫成
function callSomeone (name) {
console.log(name + '你好');
}
function FnB (fn) {
fn('小明');
}
FnB(callSomeone);
這樣的寫法,我們把 callSomeone 賦予到 FnB 的參數 fn上。
我們是透過函式表達式的方式傳入到 FnB 的 function 中,因此真正執行的地方是在 callSomeone 的 function 裡面,
而這樣呼叫的方法呢,我們又稱作 callback function。
類陣列 Arguments
剛剛有說到 arguments 是一個類陣列,每個函式在運行的時候都會有,他不需要特別宣告就可以取用,他會接受所有傳進來的
值,並以一個類似陣列的結構乘裝著,但他不是真正的陣列。
function callArg (a) {
console.log(a, arguments);
}
callArg(1,2,3,'a','5');
我們可以對這個 arguments 做 for 迴圈 的取值動作
function callArg (a) {
console.log(a, arguments);
for (var i = 0; i < arguments.length; i ++) {
console.log(arguments[i]);
}
}
callArg(1,2,3,'a','5');
但我們就沒辦法對她使用 forEach 的方法
function callArg (a) {
console.log(a, arguments);
arguments.forEach(function () {});
}
callArg(1,2,3,'a','5');
關於甚麼是類陣列,在這個章節還不需要先學習。
只要知道 arguments 是在函式運行的時候自帶的變數,並且會乘裝ㄧ切傳入的值,這樣就可以了。