JavaScript 中的 this 含義非常多,它可以是全局對象、當(dāng)前對象或者任意對象,這完全取決于函數(shù)的調(diào)用方式
隨著函數(shù)使用場合的不同,this的值會發(fā)生變化。但是有一個總的原則,那就是this指的是,調(diào)用函數(shù)的那個對象
1 作為函數(shù)調(diào)用
- 在函數(shù)被直接調(diào)用時this綁定到全局對象。在瀏覽器中,window 就是該全局對象
console.log(this);
function fn1(){
console.log(this);
}
fn1();
2 內(nèi)部函數(shù)
- 函數(shù)嵌套產(chǎn)生的內(nèi)部函數(shù)的this不是其父函數(shù),仍然是全局變量
function fn0(){
function fn(){
console.log(this);
}
fn();
}
fn0();
3 setTimeout、setInterval
- 這兩個方法執(zhí)行的函數(shù)this也是全局對象,也就是創(chuàng)建一個定時器而這個定時器屬于window對象
document.addEventListener('click', function(e){
console.log(this);
setTimeout(function(){
console.log(this);
}, 200);
}, false);
4 在構(gòu)造函數(shù)中調(diào)用
構(gòu)造函數(shù)能夠用來生成一個新的對象(object),這是函數(shù)中的this就指向新的對象(object)
在創(chuàng)建對象時new操作符具有以下作用
- 創(chuàng)建一個新對象實例,將實例中的—prototype—屬性指向構(gòu)造函數(shù)的prototype
- 初始化實例,調(diào)用構(gòu)造函數(shù)并傳入?yún)?shù),將this指向該實例
- 返回實例
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();//Byron
p2.printName();//Casper
p3.printName();//Vincent
5 在 JavaScript 中,函數(shù)也是對象,因此函數(shù)可以作為一個對象的屬性,此時該函數(shù)被稱為該對象的方法,在使用這種調(diào)用方式時,this 被自然綁定到該對象
var obj1 = {
name: 'Byron',
fn : function(){
console.log(this);
}
};
obj1.fn();//obj1
var a =obj1.fn
a()//window
6 DOM對象綁定事件
- 在事件處理程序中this代表事件源DOM對象(低版本IE有bug,指向了window)
document.addEventListener('click', function(e){
console.log(this);//document
var _document = this;
setTimeout(function(){
console.log(this);//window
console.log(_document);//document
}, 200);
}, false);
7 Function.prototype.bind
- bind,返回一個新函數(shù),并且使函數(shù)內(nèi)部的this為傳入的第一個參數(shù)
var obj1 = {
fn: function() {
console.log(this)
}
}
var fn3 = obj1.fn.bind(obj1);
fn3();//obj1
8 使用call和apply設(shè)置this
- call apply,調(diào)用一個函數(shù),傳入函數(shù)執(zhí)行上下文及參數(shù)
fn.call(context, param1, param2...)
fn.apply(context, paramArray)
第一個參數(shù)都是希望設(shè)置的this對象,不同之處在于call方法接收參數(shù)列表,而apply接收參數(shù)數(shù)組
fn2.call(obj1);
fn2.apply(obj1);
函數(shù)的執(zhí)行環(huán)境
JavaScript中的函數(shù)既可以被當(dāng)作普通函數(shù)執(zhí)行,也可以作為對象的方法執(zhí)行,這是導(dǎo)致 this 含義如此豐富的主要原因
一個函數(shù)被執(zhí)行時,會創(chuàng)建一個執(zhí)行環(huán)境(ExecutionContext),函數(shù)的所有的行為均發(fā)生在此執(zhí)行環(huán)境中,構(gòu)建該執(zhí)行環(huán)境時,JavaScript 首先會創(chuàng)建 arguments變量,其中包含調(diào)用函數(shù)時傳入的參數(shù)
接下來創(chuàng)建作用域鏈,然后初始化變量。首先初始化函數(shù)的形參表,值為 arguments變量中對應(yīng)的值,如果 arguments變量中沒有對應(yīng)值,則該形參初始化為 undefined。
如果該函數(shù)中含有內(nèi)部函數(shù),則初始化這些內(nèi)部函數(shù)。如果沒有,繼續(xù)初始化該函數(shù)內(nèi)定義的局部變量,需要注意的是此時這些變量初始化為 undefined,其賦值操作在執(zhí)行環(huán)境(ExecutionContext)創(chuàng)建成功后,函數(shù)執(zhí)行時才會執(zhí)行,這點對于我們理解JavaScript中的變量作用域非常重要,最后為this變量賦值,會根據(jù)函數(shù)調(diào)用方式的不同,賦給this全局對象,當(dāng)前對象等
至此函數(shù)的執(zhí)行環(huán)境(ExecutionContext)創(chuàng)建成功,函數(shù)開始逐行執(zhí)行,所需變量均從之前構(gòu)建好的執(zhí)行環(huán)境(ExecutionContext)中讀取
三種變量
實例變量:(this)類的實例才能訪問到的變量
靜態(tài)變量:(屬性)直接類型對象能訪問到的變量
私有變量:(局部變量)當(dāng)前作用域內(nèi)有效的變量
例子
function ClassA(){
var a = 1; //私有變量,只有函數(shù)內(nèi)部可以訪問
this.b = 2; //實例變量,只有實例可以訪問
}
ClassA.c = 3; // 靜態(tài)變量,也就是屬性,類型訪問(函數(shù)也是對象)
console.log(a); // error
console.log(ClassA.b) // undefined(在實例創(chuàng)建之后才能訪問到)
console.log(ClassA.c) //3
var classa = new ClassA();
console.log(classa.a);//undefined(a作為實例的屬性沒有被定義,與函數(shù)內(nèi)部定義的變量a沒有任何關(guān)系)
console.log(classa.b);// 2
console.log(classa.c);//undefined c是ClassA構(gòu)造函數(shù)(對象)的屬性創(chuàng)建實例時 new操作符只執(zhí)行構(gòu)造函數(shù)內(nèi)部的代碼,不會繼承構(gòu)造函數(shù)的其他屬性