1. 程式人生 > 其它 >js函式的隱式arguments

js函式的隱式arguments

1.1 背景

JavaScript 允許函式在呼叫時傳入的實參個數和函式定義時的形參個數不一致, 比如函式在定義時聲明瞭 n 個引數, 在呼叫函式時不一定非要傳入 n 個引數,例如:

1 2 3 4 5 // 1. 定義有一個形參的函式fn() function fn(arg){} // 2. 在呼叫時傳入 0 個或 多個引數,並不會報錯 fn(); // 傳入 0 個引數 fn(1,'a',3); // 傳入多個引數

1.2 arguments 與 形參的對應關係

arguments是個類陣列結構,它儲存了函式在呼叫時傳入的所有實參, 通過訪問它的length屬性可以得到其中儲存的實參的個數,並可以通過arguments[n]按順序取出傳入的每個引數(n=1,2,..,arguments.length-1)。
引數在arguments中儲存的順序和傳入的順序相同, 同時也和形參宣告的順序相同,例如:

1 2 3 4 5 6 function fn(arg1, arg2, arg3){ console.log(arg1 === arguments[0]); // true console.log(arg2 === arguments[1]); // true console.log(arg3 === arguments[2]); // true } fn(1,2,3); // 呼叫

當傳入的實參多於形參個數時,想要獲得多餘出的實參,就可以用arguments[n]來獲取了, 例如:

1 2 3 4 5 6 7 8 9 10 11 12 13 // 定義只有一個形參的函式 function fn(arg1){ console.log('length of arguments is:',arguments.length); console.log('arguments[0] is:', arguments[0]); // 獲取傳入的第一個實參, 也就是形參 arg1 的值 console.log('arguments[1] is:', arguments[1]); // 獲取第二個實參的值, 沒有形參與其對應 console.log('arguments[2] is:', arguments[2]); // 獲取第二個實參的值, 沒有形參與其對應
} fn(1,2,3); // 傳入 3 個實參 // 可以得到實際上傳入的實參的個數並取出所有實參 // length of arguments is: 3 // arguments[0] is: 1 // arguments[1] is: 2 // arguments[2] is: 3

1.3 arguments 與 形參的值相互對應

在非嚴格模式下, 修改arguments中的元素值會修改對應的形參值;同樣的,修改形參的值也會修改對應的arguments中儲存的值。下面的實驗可以說明:

1 2 3 4 5 6 7 8 9 10 11 function fn(arg1, arg2){ // 1. 修改arguments元素,對應的形參也會被修改 arguments[0] = '修改了arguments'; console.log(arg1); // 2. 修改形參值,對應的arguments也會被修改 arg2 = '修改了形參值'; console.log(arguments[1]); } fn(1,2); // '修改了arguments' // '修改了形參值'

但是,在嚴格模式下不存在這種情況, 嚴格模式下的arguments和形參的值之間失去了對應的關係:

1 2 3 4 5 6 7 8 9 10 11 12 'use strict'; // 啟用嚴格模式 function fn(arg1, arg2){ // 修改arguments元素,對應的形參也會被修改 arguments[0] = '修改了arguments'; console.log(arg1); // 修改形參值,對應的arguments也會被修改 arg2 = '修改了形參值'; console.log(arguments[1]); } fn(1,2); // 1 // 2

注意: arguments 的行為和屬性雖然很像陣列, 但它並不是陣列,只是一種類陣列結構:

1 2 3 4 5 function fn(){ console.log(typeof arguments); // object console.log(arguments instanceof Array); // false } fn();

1.4 為什麼要了解 arguments

在ES6中, 可以用靈活性更強的解構的方式(...符號)獲得函式呼叫時傳入的實參,而且通過這種方式獲得的實參是儲存在真正的陣列中的,例如:

1 2 3 4 5 6 7 function fn(...args){ // 通過解構的方式得到實參 console.log(args instanceof Array); // args 是真正的陣列 console.log(args); // 而且 args 中也儲存了傳入的實參 } fn(1,2,3); // true // Array(3) [1, 2, 3]

那麼在有了上面這種更加靈活的方式以後,為什麼還要了解arguments呢? 原因是在維護老程式碼的時候可能不得不用到它。

2. 函式上下文: this

在函式呼叫時, 函式體內也可以訪問到 this 引數, 它代表了和函式呼叫相關聯的物件,被稱為函式上下文。
this的指向受到函式呼叫方式的影響, 而函式的呼叫方式可以分成以下4種:

  1. 直接呼叫, 例如: fn()
  2. 作為物件的方法被呼叫, 例如: obj.fn()
  3. 被當做一個建構函式來使用, 例如: new Fn()
  4. 通過函式 call() 或者 apply() 呼叫, 例如: obj.apply(fn) / obj.call(fn)

下面分別討論以上 4 種呼叫方式下 this 的指向.

2.1 直接呼叫一個函式時 this 的指向

有些資料說在直接呼叫一個函式時, 這個函式的 this 指向 window, 這種說法是片面的, 只有在非嚴格模式下而且是瀏覽器環境下才成立, 更準確的說法是:在非嚴格模式下, this值會指向全域性上下文(例如在瀏覽器中是window, Node.js環境下是global)。而在嚴格模式下, this 的值是 undefined。實驗程式碼如下:

1 2 3 4 5 // 非嚴格模式 function fn(){ console.log(this); } fn(); // global || Window

嚴格模式下:

1 2 3 4 5 'use strict'; function fn(){ console.log(this); } fn(); // undefined

總結: 在直接呼叫一個函式時, 它的 this 指向分成兩種情況: 在非嚴格模式下指向全域性上下文, 在嚴格模式下指向 undefined.