就像 Function.prototype.bind() 的 Binding 不會發生作用,apply() 和 call() 也同樣無效。只會依照語彙位置來判定 this 物件。
這個例子裡的 getFullName() 和 whatsThis() 往外一層都是 Global Context,因此不管在一般模式或嚴謹模式,this 都是 Global 物件,而 Global 物件裡並沒有 firstName 和 lastName 變數,所以印出 "undefined undefined":
4. 函數作為建構子
傳統函數
將函數當作建構子,透過 new 關鍵字來產生一個物件,該物件會形成自己的環境 (Context),原本函數內的 this.xxx 變成新物件的屬性。例如以下範例:
Arrow Functions
Arrow Function 所宣告的函數不能拿來當建構子,也不存在 this 的問題。
5. 回呼函數 (Callback Function) 裡的 this
5.1. 簡單呼叫 Callback Function
傳統函數
我們會把某函數 A 當作參數傳入函數 B,函數 A 就是 Callback Function。
而傳統函數裡,Callback Function 裡的 this 是誰,視乎在函數 B 裡是怎麼呼叫函數 A。如果是最常見的「簡單呼叫」的形式,此時 this 在一般模式下就是 Global 物件,嚴謹模式則是 undefined:
Arrow Functions I
當函數 A (Callback Function) 是傳統函數,不管函數 B 是傳統函數 (hero.act1()) 還是箭頭函數 (hero.act2()),因為 Callback Function 本身是傳統函數,裡面的 this 比照傳統函數的判斷方式,也就是看呼叫方式。
由於都是透過簡單呼叫,所以 this 在一般模式下是 Global 物件,嚴謹模式是 undefined:
Arrow Functions II
當函數 A (Callback Function) 是箭頭函數,不管函數 B 是哪一種函數,都是看 Callback Function 本身的語彙位置。
由於 sayHi() 沿用外層的 this,不管是一般模式或嚴謹模式,this 都是 Global 物件:
5.2. 用 apply() / call() 將物件本身傳入 Callback Function
傳統函數
透過 apply() / call() 可以明確地控制函數裡的 this 物件是誰:
Arrow Functions I
當函數 A (Callback Function) sayHi() 是傳統函數時,受 apply() 效果影響:
當函數 B 也是傳統函數 (hero.act1()) :hero.act1() 自己的 this 看呼叫者是誰,也就是 hero,所以 apply() 將 hero 綁定為 Callback Function 的 this,因此印出 "Hi I am a Hero"。
當函數 B 是 Arrow Function (hero.act2()) :hero.act2() 自己的 this 沿用外層,也就是 Global 物件 (無論一般模式或嚴謹模式);再透過 apply() 將 Global 物件綁定於 sayHi() 的 this,因此印出 "Hi I am Global"。
Arrow Functions II
由於 apply() / call() 的綁定效果對 Arrow Function 無效,如果函數 A (Callback Function) sayHi() 是 Arrow Function,無論函數 B 是傳統函數 (hero.act1()) 或者 Arrow Function (hero.act2()),sayHi() 裡的 this 都是沿用外層,也就是 Global 物件 (無論一般模式或嚴謹模式):
var Hero = function(n){
this.exp = n;
};
var h = new Hero(100);
console.log(h); // Hero {exp: 100}
console.log(h.exp); // 100
var Hero = (n) => {
this.exp = n;
};
var h = new Hero(100); // TypeError: Hero is not a constructor
var name = "Hi I am Global";
var sayHi = function(){
return this.name;
}
var hero = {
name: "Hi I am a Hero",
act: function(cbk){
return cbk();
}
};
console.log( sayHi() ); // Hi I am Global
console.log( hero.act(sayHi) ); // Hi I am Global
var name = "Hi I am Global";
var sayHi = function(){
return this.name;
}
var hero = {
name: "Hi I am a Hero",
act1: function(cbk){
return cbk();
},
act2: (cbk) => { // arrow function
return cbk();
}
};
console.log( sayHi() ); // Hi I am Global
console.log( hero.act1(sayHi) ); // Hi I am Global
console.log( hero.act2(sayHi) ); // Hi I am Global
var name = "Hi I am Global";
var sayHi = () => { // arrow function
return this.name;
}
var hero = {
name: "Hi I am a Hero",
act1: function(cbk){
return cbk();
},
act2: (cbk) => { // arrow function
return cbk();
}
};
console.log( sayHi() ); // Hi I am Global
console.log( hero.act1(sayHi) ); // Hi I am Global
console.log( hero.act2(sayHi) ); // Hi I am Global
var name = "Hi I am Global";
function sayHi(){
return this.name;
}
var hero = {
name: "Hi I am a Hero",
act: function(cbk){
return cbk.apply(this); // 將物件本身傳入 Callback Function
}
};
console.log( sayHi() ); // Hi I am Global
console.log( hero.act(sayHi) ); // Hi I am a Hero
var name = "Hi I am Global";
var sayHi = function(){
return this.name;
}
var hero = {
name: "Hi I am a Hero",
act1: function(cbk){
return cbk.apply(this); // 將物件本身傳入 Callback Function
},
act2: (cbk) => { // arrow function
return cbk.apply(this); // 將物件本身傳入 Callback Function
}
};
console.log( sayHi() ); // Hi I am Global
console.log( hero.act1(sayHi) ); // Hi I am a Hero
console.log( hero.act2(sayHi) ); // Hi I am Global
var name = "Hi I am Global";
var sayHi = () => { // arrow function
return this.name;
}
var hero = {
name: "Hi I am a Hero",
act1: function(cbk){
return cbk.apply(this); // 將物件本身傳入 Callback Function
},
act2: (cbk) => { // arrow function
return cbk.apply(this); // 將物件本身傳入 Callback Function
}
};
console.log( sayHi() ); // Hi I am Global
console.log( hero.act1(sayHi) ); // Hi I am Global
console.log( hero.act2(sayHi) ); // Hi I am Global