this指向問題
this機制:this綁定只取決於函數調用的方式,大的來說有三種:
- 函數調用:window/undefined
- 方法調用:調用對象
- 構造函數:返回的實例對象
此外,注意兩點:
- 利用call、apply、bind可以改變this指向
- 箭頭函數的this繼承外層函數
1. this機制
- 全局作用域中,window
- 函數調用(普通調用、IIFE),window
- 方法調用,綁定到調用對象
- 隱式丟失(被傳遞給變量或者參數)
var a = 0;
function foo(){
console.log(this.a);
};
var obj = {
a: 1,
foo: foo
};
obj.foo(); // 1
var bar = obj.foo; // 隱式丟失
bar(); // 0
(obj.foo)(); // 1
(obj.foo = obj.foo)(); // 0
- 顯式綁定 : bind、apply、call (作業幫面試)
- 硬綁定:綁定後無法修改this
var a = 0;
var obj = {a: 1};
function foo(){
console.log(this.a);
};
function bar(){
foo.call(obj);
};
bar(); // 1
var baz = foo.bind(obj);
baz(); // 1
- 數組內置函數顯示綁定:
map forEach filter some every
- new綁定:構造函數
- 構造函數一般不使用return語句,通常初始化新對象,當構造函數的函數體執行完畢時,會顯示返回。this指向新返回的實例對象
- 構造函數使用了return語句,但沒指定返回值,或者返回一個原始值,那麼這時將忽略返回值。返回構造出來的實例對象。
- 構造函數顯示地使用return語句返回一個對象,this指向這個對象
// 1
function Car(){
this.counts = 1200;
}
var c = new Car(); // {counts: 1200}
// 2
function Car(){
this.counts = 1200;
return;
}
var c = new Car(); // {counts: 1200}
// 3
function Car(){
this.counts = 1200;
return {noCounts: 0};
}
var c = new Car(); // {noCounts: 0}
- 嚴格模式下,獨立調用的函數的this指向undefined
- 非嚴格模式下,call、apply中的null、undefined會被轉化成window
function Foo(v){
this.a = v;
};
var obj = {};
var bar = Foo.bind(obj);
bar(0);
console.log(obj.a); // 0
var baz = new bar(1);
console.log(obj.a); // 0
console.log(baz.a); // 1
2. this指向優先級
優先級:new > call/apply/bind > 方法調用 > 函數調用
// 1.
var a = 0;
function foo(){
function test(){
console.log(this.a);
};
return test;
};
var obj = {
a: 1,
foo: foo
}
obj.foo()(); // 0
// 2.
var a = 0;
function foo(){
var that = this;
function test(){
console.log(that.a);
};
return test;
};
var obj = {
a: 1,
foo: foo
}
obj.foo()(); // 1
3. 箭頭函數
- 箭頭函數:改變this機制,不再只是根據函數調用方式決定;箭頭函數會繼承外層this指向 —> 這直接解決了
var that = this;
的寫法 - call/apply/bind 無法改變箭頭函數的this指向
var a = 0;
function foo(){
var test = () => this.a;
return test;
};
var obj1 = {
a: 1,
foo: foo
};
var obj2 = {
a: 2,
foo: foo
};
obj1.foo().call(obj2); // 1