JavaScript中this關(guān)鍵字的幾點(diǎn)說(shuō)明:
****1.JavaScript中的this關(guān)鍵字綁定的內(nèi)容跟函數(shù)無(wú)關(guān),跟函數(shù)執(zhí)行的環(huán)境有關(guān)****
****2.函數(shù)的this綁定的內(nèi)容可以通過(guò)bind,apply和call函數(shù)來(lái)動(dòng)態(tài)進(jìn)行修改****
****3.閉包可以消除不必要的this動(dòng)態(tài)綁定來(lái)提高代碼的可讀性****
1.this綁定內(nèi)容與函數(shù)無(wú)關(guān),與執(zhí)行環(huán)境有關(guān)
一個(gè)函數(shù)在調(diào)用時(shí)會(huì)創(chuàng)建一個(gè)活動(dòng)對(duì)象,活動(dòng)對(duì)象還包含一個(gè)this變量。
var name = "javascript";
var func = function(){
console.log(this.name);
}
func();
當(dāng)調(diào)用func函數(shù)時(shí),js引擎會(huì)創(chuàng)建一個(gè)執(zhí)行上下文,同時(shí)還會(huì)創(chuàng)建一個(gè)作用域鏈,此作用域鏈為:
[[scope chain]] = [
{
Active Object{
arguments:...
this:[global Object],
...
},
global Object:{
name:'javascript'
...
}
}
]
所以在執(zhí)行console.log(this.name)的時(shí)候this綁定的是全局對(duì)象,而之前定義的name就是屬于全局變量。
再看下面的例子:
var name = "jack";
var sex = "man"
var func = function(name){
this.name = name;
}
func.prototype.print = function(){
console.log(this.name);
console.log(this.sex);
console.log(sex);
}
var obj = new func("hello");
obj.print();//結(jié)果是hello undefined man
當(dāng)執(zhí)行obj對(duì)象的print函數(shù)的時(shí)候,執(zhí)行上下文的作用域鏈?zhǔn)沁@樣的:
[[scope chain]] = [
{
Active Object{
arguments:...
this:obj,
...
},
global Object{
name:'jack',
sex:'man',
...
}
}
]
從這個(gè)作用域鏈可以很清楚地看出上面代碼輸出的結(jié)果。
2.this綁定的內(nèi)容可以被動(dòng)態(tài)修改
把上面的例子稍作修改,如下:
var name = "jack";
var sex = "man";
var func = function(name){
this.name = name;
}
func.prototype.print = function(){
console.log(this.name);
console.log(this.sex);
console.log(sex);
}.bind(this);
var obj = new func("hello");
obj.print();//結(jié)果是jack man man
通過(guò)給func.prototype.print函數(shù)添加了bind的調(diào)用,輸出的結(jié)果就不一樣,此時(shí)的this綁定的是global對(duì)象了。
再把上面的代碼修改,如下:
var name = "jack";
var sex = "man";
var func = function(name){
this.name = name;
}
func.prototype.print = function(){
console.log(this.name);
console.log(this.sex);
console.log(sex);
}
var obj = new func("hello");
func.prototype.print.call(obj,"hello");//結(jié)果是hello undefined man
這個(gè)輸出結(jié)果跟直接調(diào)用obj.print是一樣的。但是如果改成:
var obj = new func("hello");
func.prototype.print.call(this,"hello");
//下面的window和this是等價(jià)的
//func.prototype.print.call(window,"hello");
那么輸出的結(jié)果是jack man man
使用bind可以顯式指定函數(shù)使用時(shí)的this綁定,而使用call可以指定this對(duì)象的指向,另外還可以使用apply來(lái)修改this的綁定。call和apply的區(qū)別就是call后面?zhèn)鲄⑹褂玫氖嵌禾?hào)分隔的參數(shù),而apply傳遞的是一個(gè)參數(shù)數(shù)組。
3.閉包消除this動(dòng)態(tài)綁定提高代碼可讀性
假設(shè)要把一個(gè)外部環(huán)境的this變量傳遞到一個(gè)內(nèi)部函數(shù)去使用,一般會(huì)這么做:
var a = 10;
var obj = {
a:1;
b:2;
sum:function(){
var addA = function(a){
return this.a+a;
}.bind(this);
return addA(this.b);
}
}
console.log(obj.sum());//結(jié)果是3
在聲明addA的時(shí)候使用了bind(this),那么addA函數(shù)內(nèi)部的this.a指向的是obj對(duì)象的a變量。如果不用bind,this默認(rèn)指向的是window對(duì)象,那么輸出的結(jié)果就是12了。一般情況下(考慮到sum函數(shù)里面的3個(gè)this)通常會(huì)添加一個(gè)self或者that局部變量來(lái)增加代碼的可讀性,同時(shí)也不用手動(dòng)去調(diào)用bind函數(shù)。
var a = 10;
var obj = {
a:1,
b:2,
sum:function(){
var self = this;
var addA = function(a){
return self.a + a;
};
return addA(this.b);
}
}
console.log(obj.sum());//結(jié)果是3
這里面的self變量利用了閉包的特性,同時(shí)讓代碼更加具有可讀性,也消除了不必要的bind(this)調(diào)用。