由于綁定期的特性价说,JS中的 this
含義很多辆亏,它可以是全局對(duì)象、當(dāng)前對(duì)象或者任意對(duì)象鳖目,這完全取決于函數(shù)的調(diào)用方式
隨著函數(shù)使用場合的不同扮叨,this的值會(huì)發(fā)生變化
總的原則:this指的是 調(diào)用函數(shù)的那個(gè)對(duì)象
作為函數(shù)調(diào)用 (以下有三種情況)
在全局下和函數(shù)被直接調(diào)用時(shí) this
綁定到全局對(duì)象(瀏覽器中的全局對(duì)象 window
)
被直接調(diào)用 fn1 可以理解為:window.fn1()
console.log(this);
function fn1(){
console.log(this);
}
fn1();
內(nèi)部函數(shù)
函數(shù)嵌套產(chǎn)生的內(nèi)部函數(shù)的 this
不是其父函數(shù),仍然是全局變量(記憶)
function fn0(){
function fn(){
console.log(this);
}
fn();
}
fn0();
setTimeout领迈、setInterval
上面兩個(gè)方法的執(zhí)行函數(shù) this 也是全局對(duì)象
document.addEventListener('click', function(e){
console.log(this); // this 指的是 事件源DOM對(duì)象
setTimeout(function(){
console.log(this); // this 指的是 全局對(duì)象
}, 200);
}, false);
作為構(gòu)造函數(shù)調(diào)用
所謂構(gòu)造函數(shù)彻磁,就是通過這個(gè)函數(shù)生成一個(gè)新對(duì)象(object)。這是惦费,this 指的是這個(gè)新對(duì)象
重溫 new 運(yùn)算符:
new 運(yùn)算符接受一個(gè)函數(shù) F 及其參數(shù): new F(arguments)
兵迅。這一過程分為三步:
- 創(chuàng)建類的實(shí)例。這步是把一個(gè)空的對(duì)象的
__proto__
屬性設(shè)置為F.prototype
- 初始化實(shí)例薪贫。函數(shù) F 被傳入?yún)?shù)并調(diào)用,關(guān)鍵字 this 被設(shè)定為該實(shí)例刻恭。
- 返回實(shí)例瞧省。
function Person(name){
this.name = name;
}
Person.prototype.printName = function(){
console.log(this.name);
};
//
var p1 = new Person('Byron');
var p2 = new Person('Casper');
var p3 = new Person('Vincent');
//
p1.printName(); // 每個(gè) this 為調(diào)用其方法的實(shí)例
p2.printName();
p3.printName();
作為對(duì)象方法調(diào)用
在 JavaScript 中,函數(shù)也是對(duì)象鳍贾,因此函數(shù)可以作為一個(gè)對(duì)象的屬性鞍匾,此時(shí)該函數(shù)被稱為該對(duì)象的方法,在使用這種調(diào)用方式時(shí)骑科,this 被自然綁定到該對(duì)象
var obj1 = {
name: 'Byron',
fn : function(){
console.log(this);
}
};
obj1.fn(); // obj1
注意下面這種情況:函數(shù)名 實(shí)際上就是一個(gè)地址橡淑,可以通過賦值給 fn2
,調(diào)用時(shí)不會(huì)再去管obj1
咆爽,fn2
執(zhí)行時(shí)可以理解為 window.fn2
調(diào)用
var fn2 = obj1.fn
fn2() // window
DOM對(duì)象綁定事件
在事件處理程序中 this
代表事件源DOM對(duì)象(低版本IE有bug梁棠,指向了window)
document.addEventListener('click', function(e){
console.log(this); // 事件源DOM對(duì)象
var _document = this;
setTimeout(function(){
console.log(this); // window 全局對(duì)象
console.log(_document); // 事件源DOM對(duì)象
}, 200);
}, false);
Function.prototype.bind
bind置森,返回一個(gè)新函數(shù),并且使函數(shù)內(nèi)部的 this 為傳入的第一個(gè)參數(shù)
下面的代碼改寫上面需要注意的賦值情況
var fn3 = obj1.fn.bind(obj1)
fn3() // obj1
使用 call 和 apply 設(shè)置 this
call apply符糊,調(diào)用一個(gè)函數(shù)凫海,傳入函數(shù)執(zhí)行上下文及其參數(shù)
fn.call(context, param1, param2...)
fn.apply(context, paramArray)
語法同樣簡單,第一個(gè)參數(shù)都是希望設(shè)置的 this
對(duì)象男娄,不同之處在于 call
方法接收參數(shù)列表行贪,而 apply
接收參數(shù)數(shù)組
call 舉例
function sum() { // 用 call 實(shí)現(xiàn)參數(shù)的累加
var result = 0
Array.prototype.forEach.call(arguments, function(value) {
result += value
})
return result
}
// 另一種方法
function sum2() {
var result = 0
// 下面將 參數(shù)列表 轉(zhuǎn)變成數(shù)組
var argsArr = Array.prototype.slice.call(arguments, 0)
console.log(Array.isArray(argsArr)) // true
argsArr.forEach(function(value) {
result += value
})
return result
}
apply 舉例
var arr = [3,1,0,2,-10,99,20]
// Math.max( 3,1,0,2,-10,99,20 )
console.log(Math.max.apply(null, arr)) // 99
console.log(Math.min.apply(null, arr)) // -10
caller
在函數(shù) A
調(diào)用函數(shù) B
時(shí),被調(diào)用函數(shù) B
會(huì)自動(dòng)生成一個(gè) caller
屬性模闲,指向調(diào)用它的函數(shù)對(duì)象建瘫,如果函數(shù)當(dāng)前未被調(diào)用,或并非被其他函數(shù)調(diào)用尸折,則 caller
為 null
arguments
- 在函數(shù)調(diào)用時(shí)啰脚,會(huì)自動(dòng)在該函數(shù)內(nèi)部生成一個(gè)名為 arguments 的隱藏對(duì)象
- 該對(duì)象類似于數(shù)組,可以使用 [ ] 運(yùn)算符獲取函數(shù)調(diào)用時(shí)傳遞的實(shí)參
- 只有函數(shù)被調(diào)用時(shí)翁授,arguments 對(duì)象才會(huì)創(chuàng)建拣播,未調(diào)用時(shí)其值為 null
callee (嚴(yán)格模式不可用)
當(dāng)函數(shù)被調(diào)用時(shí),它的 arguments.callee 對(duì)象就會(huì)指向自身收擦,也就是一個(gè)對(duì)自己的引用
由于 arguments 在函數(shù)被調(diào)用時(shí)才有效贮配,因此 arguments.callee 在函數(shù)未調(diào)用時(shí)是不存在的(即 null.callee ),且引用它會(huì)產(chǎn)生異常
function fn6(){
console.log(arguments.callee);
}
fn6();
匿名函數(shù)特好用
var i =0;
window.onclick = function(){
console.log(i);
if(i<5){
i++;
setTimeout(arguments.callee, 200);
}
} // 0 1 2 3 4 5
函數(shù)的執(zhí)行環(huán)境
JavaScript中的函數(shù)既可以被當(dāng)作普通函數(shù)執(zhí)行塞赂,也可以作為對(duì)象的方法執(zhí)行泪勒,這是導(dǎo)致 this 含義如此豐富的主要原因
一個(gè)函數(shù)被執(zhí)行時(shí),會(huì)創(chuàng)建一個(gè)執(zhí)行環(huán)境(ExecutionContext)宴猾,函數(shù)的所有的行為均發(fā)生在此執(zhí)行環(huán)境中圆存,構(gòu)建該執(zhí)行環(huán)境時(shí),JavaScript 首先會(huì)創(chuàng)建 arguments變量仇哆,其中包含調(diào)用函數(shù)時(shí)傳入的參數(shù)
接下來創(chuàng)建作用域鏈沦辙,然后初始化變量。首先初始化函數(shù)的形參表讹剔,值為 arguments變量中對(duì)應(yīng)的值油讯,如果 arguments變量中沒有對(duì)應(yīng)值,則該形參初始化為 undefined延欠。
如果該函數(shù)中含有內(nèi)部函數(shù)陌兑,則初始化這些內(nèi)部函數(shù)。如果沒有由捎,繼續(xù)初始化該函數(shù)內(nèi)定義的局部變量兔综,需要注意的是此時(shí)這些變量初始化為 undefined,其賦值操作在執(zhí)行環(huán)境(ExecutionContext)創(chuàng)建成功后,函數(shù)執(zhí)行時(shí)才會(huì)執(zhí)行软驰,這點(diǎn)對(duì)于我們理解JavaScript中的變量作用域非常重要涧窒,最后為this變量賦值,會(huì)根據(jù)函數(shù)調(diào)用方式的不同碌宴,賦給this全局對(duì)象杀狡,當(dāng)前對(duì)象等
至此函數(shù)的執(zhí)行環(huán)境(ExecutionContext)創(chuàng)建成功,函數(shù)開始逐行執(zhí)行贰镣,所需變量均從之前構(gòu)建好的執(zhí)行環(huán)境(ExecutionContext)中讀取
三種變量
- 實(shí)例變量:(this)類的實(shí)例才能訪問到的變量
- 靜態(tài)變量:(屬性)直接類型對(duì)象能訪問到的變量
- 私有變量:(局部變量)當(dāng)前作用域內(nèi)有效的變量
function ClassA(){
var a = 1; //私有變量呜象,只有函數(shù)內(nèi)部可以訪問
this.b = 2; //實(shí)例變量,只有實(shí)例可以訪問
}
ClassA.c = 3; // 靜態(tài)變量碑隆,也就是屬性恭陡,類型訪問
console.log(a); // error
console.log(ClassA.b) // undefined
console.log(ClassA.c) //3
var classa = new ClassA();
console.log(classa.a);//undefined
console.log(classa.b);// 2
console.log(classa.c);//undefined c 只是在 ClassA函數(shù)上