關(guān)于this的誤解
- 指向自身
- 它的作用域
指向自身
如果要從函數(shù)對象內(nèi)部引用它自身,那只用this是不夠的。一般來說我們需要通過一個指向函數(shù)對象的詞法標識符(變量)來引用它。
//不懂this的指向
function foo(num) {
console.log('foo:' + num);
// console.log(this); //此時this指向window
this.count++;
// foo.count++; //直接用函數(shù)的標識符來代替this引用函數(shù)對象,可解決。但是回避了this指向的問題
}
foo.count = 0;
for (var i = 0; i < 10; i++) {
if(i > 5) {
foo(i);
//但是使用call()來強制this指向函數(shù)本身,也可解決。 我們在面對this的問題
// foo.call(foo, i);
}
}
console.log(foo.count); //結(jié)果為0,而不是4
它的作用域
需要明確的是,this在任何情況下都不指向函數(shù)的詞法作用域。每當自己想要把this和詞法作用域的查找混合使用的時候,就應該提醒自己,這是無法實現(xiàn)的。
//完美尷尬案例
function foo() {
var a = 3;
console.log(this.bar());
}
function bar() {
return this.a
}
foo(); // 出錯
this到底是什么
它不是什么
學習this的第一步就是明白它既不指向函數(shù)自身,也不指向函數(shù)的詞法作用域。
this的綁定是在運行時進行綁定的,而不是在編寫時綁定,和函數(shù)聲明位置沒有任何關(guān)系,只取決于函數(shù)的調(diào)用方式,即調(diào)用位置。
調(diào)用位置
調(diào)用位置,就是函數(shù)被調(diào)用的位置。而有些編程模式可能會隱藏真正的調(diào)用位置。
最重要的是分析調(diào)用棧,調(diào)用位置就在當前正在執(zhí)行函數(shù)的前一個調(diào)用中。
function baz() {
console.log('baz');
bar();
}
function bar() {
console.log('bar');
foo();
}
function foo() {
console.log('foo'); // 當前調(diào)用棧是baz ->bar -> foo
}
baz();
this的綁定規(guī)則
默認綁定
最常用的函數(shù)調(diào)用:獨立函數(shù)調(diào)用。即函數(shù)是直接使用不帶任何修飾的函數(shù)引用進行調(diào)用的。
// 默認綁定
function foo() {
console.log(this.a);
}
var a = 2;
foo(); // foo是直接使用不帶任何修飾的函數(shù)引用所調(diào)用的
var width = 600;
var shape = {
width : 100
}
var showWidth = function() {
console.log(this.width);
}
shape.getWidth = showWidth;
shape.getWidth(); // 前面有修飾,結(jié)果為100
var myWidth = shape.getWidth;
myWidth(); // 前面沒有修飾,結(jié)果為600
隱式綁定
當函數(shù)引用有上下文對象時,就會通過隱式綁定規(guī)則把函數(shù)調(diào)用中的this綁定到這個上下文對象上??梢杂^察調(diào)用位置是否被某個對象擁有或者包含。
// 隱式綁定
function foo() {
console.log(this.a);
}
var obj = {
a : 2, //this被隱式綁定在這個對象里
foo : foo
}
obj.foo();
隱式丟失問題
最常見的就是隱式綁定的函數(shù)會丟失綁定對象,回到默認綁定。
// 隱式綁定丟失情況
function foo() {
console.log(this.a);
}
var obj = {
a : 2,
foo : foo
}
var bar = obj.foo; //這里創(chuàng)建了一個函數(shù)的別名
// 雖然bar是obj.foo的引用,但是它引用的其實是foo本身
var a = '全局下的a';
bar(); // 結(jié)果是this又跑到全局去下了
當函數(shù)被當作參數(shù)傳遞時,就會發(fā)生隱性賦值,從而產(chǎn)生會上面一樣的結(jié)果。
// 當函數(shù)被當作參數(shù)傳遞時,會發(fā)生隱性賦值的情況
function foo() {
console.log(this.a);
}
function doFoo(fn) {
fn(); //調(diào)用位置在這,其實fn引用的就是foo
}
var a = "a在全局下";
var obj = {
a : "a在對象里",
foo : foo
};
doFoo(obj.foo);
硬綁定
我們可以在某個對象上,強制調(diào)用函數(shù)。
- 典型使用場景,創(chuàng)建一個包裹函數(shù),負責接收參數(shù)并返回值。
function foo(something) {
console.log(this.a , something);
return this.a + something;
}
var obj = {
a : 2
};
var bar = function() {
return foo.apply(obj, arguments); //顯式的硬綁定,包裹foo
};
var b = bar(3);
console.log(b);
- 創(chuàng)建一個可以重復使用的輔助函數(shù)
function foo(something) {
console.log(this.a , something);
return this.a + something;
}
// 簡單的輔助綁定函數(shù)
function bind(fn, obj) {
return function() {
return fn.apply(obj, arguments);
};
}
var obj = {
a : 2
};
var bar = bind(foo, obj);
var b = bar(3);
console.log(b);
- 其實我們有內(nèi)置方法
bind
//ES5給硬綁定提供了一個內(nèi)置方法Function.prototype.bind
function foo(something) {
console.log(this.a , something);
return this.a + something;
}
var obj = {
a : 2
};
var bar = foo.bind(obj);
var b = bar(3);
console.log(b);
new綁定
最后一個綁定規(guī)則。
function foo(a) {
this.a = a;
console.log(this); // this指向foo函數(shù)
}
var bar = new foo(2); // new會創(chuàng)建一個新對象并綁定到函數(shù)調(diào)用的this上
console.log(bar.a)
綁定規(guī)則優(yōu)先級
new綁定 => 顯示綁定 => 隱式綁定 => 默認綁定
[TOC]