呼叫函式時,隱式的函式引數 this 和 arguments 會被靜默的傳遞給函式
this
表示呼叫函式的上下文物件
arguments
表示函式呼叫過程中傳遞的所有引數。通過 arguments 引數可以訪問 函式呼叫過程中傳遞的實際引數。
函式呼叫的方式 對 函式的隱式引數有很大的影響
4.1 隱式的函式引數
arguments 和 this
arguments 引數
arguments 引數是 傳遞給函式的所有引數的集合
//arguments 是一個類陣列物件
//length 屬性 表示實參的確切個數
arguments[2]
function whatever(a,b,c){
arguments.length //實際傳入的引數個數
}
//arguments 物件是函式引數的別名 所以改變了 arguments 物件的值,同時也會影響到對應的函式引數
複製程式碼
在嚴格模式下,不能改變 arguments[index] 的值。
"use strict"
function infiltrate(person){
arguments[0] = 'ninja'; //嚴格模式下 報錯
}
複製程式碼
this 引數:函式上下文
呼叫函式時,除了顯示提供的引數外,this 引數也會預設的傳遞給函式。 => 函式上下文
this
引數的指向 不僅是由 定義函式的方式 和 位置決定的,還受到 函式呼叫方式的影響。
4.2 函式呼叫
函式呼叫的4種方式
//1.作為一個函式直接呼叫
function skulk(name){}
shulk('Hattori');
//2.作為一個方法,關聯到物件上呼叫
var ninja = {
shulk: function(){}
};
ninja.shulk('Hattori');
//3.作為建構函式呼叫
function Ninja(name){}
ninja = new Ninja('Hattori');
//4.通過apply 和 call呼叫
skulk.apply(ninja,['Hattori']);
skulk.call(ninja,'Hattori');
複製程式碼
作為函式直接呼叫
函式上下文 this 有兩種可能性:
1.在非嚴格模式下,this是全域性上下文(window物件)
2.在嚴格模式下,this是undefined
//函式定義 作為函式呼叫
function ninja(){};
ninja();
//函式表示式 作為函式呼叫
var samurai = function(){};
samurai();
(function(){})(); //立即呼叫的函式表示式,作為函式被呼叫
複製程式碼
//非嚴格模式下的函式呼叫
function ninja(){
return this; //window
}
//嚴格模式下的函式呼叫
function ninja(){
"use strict";
return this; //undefined
}
複製程式碼
作為方法被呼叫
當一個函式被賦值給一個物件的屬性,並且通過物件屬性引用的方式呼叫函式,函式會被作為 物件的方法 呼叫
當函式作為某個物件的方法被呼叫時,該物件會成為函式的上下文,並且在函式內部可以通過引數(this)訪問到
var ninja = {};
ninja.skulk = function(){};
ninja.skulk();
複製程式碼
function whatsMyContext(){
return this;
}
whatsMyContext(); //當作函式呼叫 this == window
var getMyThis = whatsMyContext();
getMyThis(); //建立原函式的引用 當作函式呼叫 this == window
var ninja1 = {
getMyThis: whatsMyContext
}
ninja1.getMyThis == ninja1 //this 返回函式上下文 == 該方法所在的物件
複製程式碼
作為建構函式呼叫
//通過建構函式的方式呼叫,需要在函式呼叫之前使用關鍵字new
new MyObject();
function Ninja(){
this.skulk = function(){
return this;
}
}
var ninja1 = new Ninja();
ninja1.skulk() === ninjia1 //true
複製程式碼
當用 new
呼叫建構函式時,觸發以下幾個動作
- 建立一個新的空物件
- 該物件作為 this 引數傳遞給建構函式,從而成為建構函式的函式上下文
- 新構造的物件作為 new 運算子的返回值
建構函式的目的:建立一個新物件,並進行初始化設定,然後將其作為建構函式的返回值。
建構函式的返回值
function Ninja(){
this.skulk = function(){
return true;
};
return 1; //返回一個基本資料型別
}
Ninja() //返回值為1
//返回值1被忽略了,一個新的初始化物件返回
var ninja = new Ninja();
typeof ninja === "object" //true
typeof ninja.skulk === "function" //true
var puppet = {rules:false};
function Ninja(){
this.skulk = function(){
return true;
}
return puppet; //返回一個新物件
}
複製程式碼
- 如果建構函式返回了一個物件,那麼該物件作為整個表示式的值返回,傳入建構函式的 this 被丟棄
- 如果建構函式返回了 非物件型別,則忽略返回值,返回新建立的物件
apply/call方法呼叫
不同型別函式呼叫之間的區別:最終作為 函式上下文(this) 傳遞給 執行函式的物件不同。
直接函式:window undefined
方法:方法所在的物件
建構函式:新建立的物件例項
==每個函式都存在這兩個方法==
改變函式的上下文,顯示指定函式的上下文物件
事件回撥函式的上下文 是 觸發事件的物件 => 呼叫的位置
function Button(){
this.clicked = false;
this.click = function(){
this.clicked = true;
assert(button.clicked,"The button has been clicked");
}
}
var button = new Button();
var elem = document.getElementById("test");
elem.addEventListener("click",button.click);
複製程式碼
//apply和call方法來設定函式上下文
function juggle(){
var result = 0;
for (var n=0;n<arguments.length;n++){
result += arguments[n];
}
this.result = result;
}
var ninja1 = {};
var ninja2 = {};
juggle.apply(ninja1,[1,2,3,4]);
juggle.call(ninja2,5,6,7,8);
ninja1.result == 10
ninja2.result == 26
複製程式碼
強制指定回撥函式的函式上下文
forEach 遍歷函式將每個元素傳給回撥函式,將當前元素作為回撥函式的上下文
function forEach(list,callback){
for(var n=0;n<list.length;n++){
callback.call(list[n],n); //當前元素作為函式上下文,迴圈索引作為回撥函式的引數
}
}
var weapons = [{type:'abc'},{type:'bcd'},{type:'efg'}];
forEach(weapons,function(index){
this === weapons[index] //list[n]
})
複製程式碼
4.3 解決函式上下文的問題
上下文不一致可以考慮以下幾種辦法:
1.call 和 apply 顯示指定 this 上下文
2.箭頭函式
3.bind 方法
箭頭函式繞過函式上下文
箭頭函式沒有單獨的this值.
箭頭函式的 this 與宣告所在的上下文相同
//箭頭函式自身不含上下文,從定義時所在的函式繼承上下文
//呼叫箭頭函式時,不會隱式傳入this引數,而是從定義時的函式繼承上下文
function Button(){
this.clicked = false;
this.click = () => {
this.clicked = true;
button.clicked == true; //button == this
}
}
var button = new Button();
var elem = document.getElementById("test");
elem.addEventListener("click",button.click);
複製程式碼
箭頭函式與物件字面量
只有一個按鈕,不需要使用建構函式。直接使用物件字面量。單例模式
//箭頭函式與物件字面量
//物件字面量在全域性程式碼中定義,所以this與全域性程式碼的this相同
var button = {
clicked: false,
click: () => { //箭頭函式在建立時確定了this的指向
this.clicked = true;
button.clicked; //false
this === window; //true
window.clicked; //true
}
}
var elem = document.getElementById("test");
elem.addEventListener("click",button.click);
複製程式碼
bind 方法
函式可以訪問 bind 方法建立新函式,建立的的新函式與 **原始函式的函式體 **相同,新函式被繫結到指定的物件上,this 被設定為物件本身
呼叫 bind 方法不會修改 原始函式,而是建立了一個全新的函式
var button = {
clicked : false,
click : function(){
this.clicked = true;
button.clicked; //true
}
}
var elem = document.getElementById("test");
elem.addEventListener("click",button.click.bind(button));
//bind方法會新建立一個全新的函式
var boundFunction = button.click.bind(button);
boundFunction != button.click //true
複製程式碼
4.4 總結
-
當呼叫函式時,除了傳入在函式定義中顯式宣告的引數之外,同時還傳入兩個隱式引數:arguments 與 this。
-
arguments 引數是傳入函式的所有引數的集合。具有 length 屬性,表示傳入引數的個數,通過 arguments 引數還可獲取那些與函式形參不匹配的引數。 在非嚴格模式下,arguments 物件是函式引數的別名,修改 arguments 物件 會修改函式實參,可以通過嚴格模式避免修改函式實參。
-
this 表示函式上下文,即與函式呼叫相關聯的物件。函式的定義方式和呼叫 方式決定了 this 的取值。
-
-
函式的呼叫方式有 4 種。
-
作為函式呼叫:skulk()。
-
作為方法呼叫:ninja.skulk()。
-
作為建構函式呼叫:new Ninja()。
-
通過 apply 與 call 方法呼叫:skulk.apply(ninja)或 skulk.call(ninja)。
-
-
函式的呼叫方式影響 this 的取值。
-
如果作為函式呼叫,在非嚴格模式下,this 指向全域性 window 物件;在嚴格 模式下,this 指向 undefined。
-
作為方法呼叫,this 通常指向呼叫的對像。
-
作為建構函式呼叫,this 指向新建立的物件。
-
通過 call 或 apply 呼叫,this 指向 call 或 apply 的第一個引數。
-
-
箭頭函式沒有單獨的 this 值,this 在箭頭函式建立時確定。
-
所有函式均可使用 bind 方法,建立新函式,並繫結到 bind 方法傳入的引數上。被繫結的函式與原始函式具有一致的行為。