以下都出自《javaScript忍者祕籍第二版》
在函數定義中顯式聲明的參數之外,函數調用時還會傳遞兩個隱式的參數:arguments 和 this。
這些隱式參數在函數聲明中沒有明確定義,但會默認傳遞給函數並且可以在函數內正常訪問。在函數內可以像其他明確定義的參數一樣引用它們。接下來依次介紹這些隱式參數。
arguments 參數是傳遞給函數的所有參數集合。無論是否有明確定義對應的形參,通過它我們都可以訪問到函數的所有參數。
length 屬性:
length - 實參的確切個數。可通過數組索引的方式來獲取單個參數的值。
tips:
避免把arguments參數當作_數組_。你可能會被它的用法誤導,畢竟它有length屬性,而且可以通過數組下標的方式訪問到每一個元素。但它並非JavaScript數組,如果你嘗試在arguments對象上使用數組的方法(例如,上一章中用到的sort方法),會發現最終會報錯。arguments對象僅是一個類數組的結構,在使用中要尤爲注意。
使用arguments參數對所有函數參數執行操作:
function test() {
let sum = 0
for (var i = 0; i < arguments.length; i ++) {
sum += arguments[i]
}
return sum
}
let r = test(1, 2, 3, 4, 5)
console.log(r)
這個例子中我們首先定義了一個沒有顯式聲明任何參數的sum函數,儘管如此,我們依然可以通過arguments對象訪問所有的函數參數。遍歷所有的參數即可計算它們的和。
arguments對象作爲函數參數的別名:
arguments參數有一個有趣的特性:它可以作爲函數參數的別名。例如,如果爲arguments[0]賦一個新值,那麼,同時也會改變第一個參數的值。
function test() {
arguments[0] = 20
console.log(arguments)
}
test(1, 2)
注意:
將arguments對象作爲函數參數的別名使用時會影響代碼的可讀性,因此在JavaScript提供的嚴格模式(strict mode)中將無法再使用它。
this參數:函數上下文
當調用函數時,除了顯式提供的參數外,this參數也會默認地傳遞給函數。this參數是面向對象JavaScript編程的一個重要組成部分,代表函數調用相關聯的對象。因此,通常稱之爲函數上下文。
函數上下文
是來自面嚮對象語言(如Java)的一個概念。在這些語言中,this通常指向定義當前方法的類的實例。
this參數的指向:
this參數的指向不僅是由定義函數的方式和位置決定的,同時還嚴重受到函數調用方式的影響。
函數的調用方式對函數內代碼的執行有很大的影響,主要體現在this參數以及函數上下是如何創建的。這點尤爲重要。
調用方式:
(1)作爲函數了,直接被調用 function test () { } -> test()
(2)作爲一個方法,關聯在一個對象上,實現面向對象編程。obj = { test: () => {} } -> obj.test
(3)作爲一個構造函數 (constructor)——new Test() 實例化一個新的對象
(4)通過函數的 apply 或者 call 方法—— test.apply(ninja) 或者 test.call(ninja)
1、函數直接被調用:
如果一個函數沒有作爲方法、構造函數或者通過apply和call調用的話,我們就稱之爲作爲函數被直接調用。
當以下邊三種方式調用時,函數上下文(this關鍵字的值)有兩種可能性:在非嚴格模式下,它將是全局上下文(window對象),而在嚴格模式下,它將是 undefined
function test () {}
test() // 函數定義作爲函數被調用
var test = function () {}
test() // 函數表達式作爲函數被調用
(function () {})() // 會被立即調用的函數表達式
2、函數作爲方法被調用:
當函數作爲某個對象的方法被調用時,該對象會成爲函數的上下文,並且在函數內部可以通過this訪問到。這也是JavaScript實現面向對象編程的主要方式之一。
let obj = {
test: function() {}
}
obj.test()
3、函數作爲構造函數調用:
函數作爲構造函數調用並沒有什麼特別之處。構造函數的聲明和其他函數類似,通過可以使用函數聲明和函數表達式很容易地構造新的對象。
使用關鍵字new調用函數會觸發以下幾個動作:
1、創建一個新的空對象。
2、該對象,作爲this參數傳遞給構造函數,從而成爲構造函數的函數上下文。
3、新構造的對象作爲new 運算符的返回值返回。
下邊圖中:
當使用關鍵字new調用函數時,會創建一個空的對象實例並將其設置爲構造函數的上下文(this參數)
注意:
1、構造函數的目的是根據初始條件對函數調用創建的新對象進行初始化。
4、使用call和apply方法調用:
試想下,如果我們想改變函數上下文怎麼辦?如果想要顯式指定它怎麼辦?如果……好吧,我們爲什麼會提出這樣的問題?
JavaScript爲我們提供了一種調用函數的方式,從而可以顯式的指定任何對象作爲函數的上下文。我們可以使用每個函數上都存在的這兩種方法來完成:apply 和 call。
是的,我們所指的正是函數的方法。作爲第一類對象(順便說一下,函數是由內置的Function構造函數所創建),函數可以像其他對象類型一樣擁有屬性,也包括方法。
若想使用apply方法調用函數,需要爲其傳遞兩個參數:
作爲函數上下文的對象和一個數組作爲函數調用的參數。call方法的使用方式類似,不同點在於是直接以參數列表的形式,而不再是作爲數組傳遞。
方法的第一個參數都會被作爲函數上下文,不同處在於後續的參數。apply方法只需要一個額外的參數,也就是一個包含參數值的數組;call方法則需要傳入任意數量的參數值,這些參數將用作函數的實參。
call和apply這兩個方法對於我們要特殊指定一個函數的上下文對象時特別有用,在執行回調函數時可能會經常用到。
總結:
1、
當調用函數時,除了傳入在函數定義中顯式聲明的參數之外,同時還傳入兩個隱式參數:arguments與this。arguments參數是傳入函數的所有參數的集合。具有length屬性,表示傳入參數的個數,通過arguments參數還可獲取那些與函數形參不匹配的參數。在非嚴格模式下,arguments對象是函數參數的別名,修改arguments對象會修改函數實參,可以通過嚴格模式避免修改函數實參。
函數的調用方式有4種:
(1)作爲函數調用:skulk();
(2)作爲方法調用:ninja.skulk();
(3)作爲構造函數調用:new Ninja();
(4)通過apply與call方法調用:skulk.apply(ninja)或skulk.call(ninja)。
函數的調用方式影響this的取值。
如果作爲函數調用,
(1)在非嚴格模式下,this指向全局window對象;
在嚴格模式下,this指向undefined。
作爲方法調用,
this通常指向調用的對象。
作爲構造函數調用,
this指向新創建的對象。
(5)通過call或apply調用,this指向call或apply的第一個參數。
箭頭函數沒有單獨的this值,this在箭頭函數創建時確定。所有函數均可使用bind方法,創建新函數,並綁定到bind方法傳入的參數上。被綁定的函數與原始函數具有一致的行爲。