在javascript中,this的指向是在執(zhí)行上下文的創(chuàng)建階段確定的,其實(shí)只要知道不同執(zhí)行方式下,this的指向分別是是什么,就能很好的掌握this這個(gè)讓人摸不透的東西。
一、全局執(zhí)行
全局執(zhí)行又分為瀏覽器和node下的執(zhí)行環(huán)境
1、瀏覽器
console.log(this);//window
2、node
console.log(this);//global
在瀏覽器器下全局執(zhí)行的this指向window,而在node環(huán)境下全局執(zhí)行的this指向global
二、函數(shù)執(zhí)行
函數(shù)執(zhí)行又分為純粹的函數(shù)調(diào)用還有嚴(yán)格模式下的函數(shù)調(diào)用
1、純粹的函數(shù)調(diào)用
function test(){
console.log(this);
}
test();//window
2、嚴(yán)格模式下的函數(shù)調(diào)用
'use strict'
function test(){
console.log(this);
}
test();//undefined
純粹的函數(shù)調(diào)用的時(shí)候,this默認(rèn)在全局執(zhí)行,所以指向window,但是在嚴(yán)格模式下,this指向undefined
三、作為對(duì)象的方法調(diào)用
當(dāng)作為對(duì)象的方法調(diào)用的時(shí)候,this指向當(dāng)前的對(duì)象
var obj = {
name:'McRay';
foo:function(){
console.log(this.name);
}
}
obj.foo();//McRay
我們還可以這樣寫
function test(){
console.log(this.name);
}
var obj = {
name:'McRay';
foo:test;
}
obj.foo();//McRay
對(duì)于函數(shù)test來(lái)說(shuō),函數(shù)名test是一個(gè)引用,foo:test這句話的作用就是讓foo也指向test所指向的函數(shù),所以它們調(diào)用的是同一個(gè)函數(shù),所以this指向的是foo所在的對(duì)象。
再來(lái)看一種情況,把對(duì)象的方法賦值給另一個(gè)變量,然后直接調(diào)用這個(gè)變量
var obj = {
name:'McRay';
foo:function(){
console.log(this);
}
}
var test = obj.foo();
test();//window
}
我們發(fā)現(xiàn)此時(shí)的this指向的是全局環(huán)境下的window,因?yàn)?code>obj.foo()只是一個(gè)函數(shù)引用,讓test等于這個(gè)引用,就與obj這個(gè)對(duì)象沒(méi)有了關(guān)系了,所以當(dāng)調(diào)用test()的時(shí)候,其實(shí)就是全局執(zhí)行,this就指向window。
上面的問(wèn)題在平時(shí)的編程中也會(huì)遇到,其中比較經(jīng)典的就是異步回調(diào)函數(shù)的調(diào)用,請(qǐng)看下面的例子
var obj = {
name:'McRay';
foo:function(){
console.log(this);
},
foo2:function(){
console.log(this);//obj
setTimeout(this.foo,1000);//window
}
};
obj.foo2();
foo2()方法中的this第一次指向obj,但是為什么第二次this就指向window呢?道理其實(shí)和上一個(gè)問(wèn)題一樣,setTimeout函數(shù)的第一個(gè)參數(shù)的接受一個(gè)函數(shù),好比如fun=this.foo(),所以相當(dāng)于把this.foo()這個(gè)函數(shù)引用賦給了fun,已經(jīng)擺脫了obj對(duì)象的關(guān)系了,所以相當(dāng)于在全局環(huán)境下執(zhí)行。
解決方法是利用閉包的特性,代碼如下
var obj = {
name:'McRay';
foo:function(){
console.log(this);
}
foo2:function(){
console.log(this);
var that = this;
setTimeout(function(){
console.log(this);//window
console.log(that);//obj
},1000);
}
}
obj.foo2();
四、作為一個(gè)構(gòu)造函數(shù)調(diào)用
在之前的文章里面,我已經(jīng)總結(jié)過(guò),在調(diào)用構(gòu)造函數(shù)(new),創(chuàng)建一個(gè)對(duì)象的過(guò)程都發(fā)生了什么,這里簡(jiǎn)單回顧一下,主要以下幾個(gè)步驟:
- 1、創(chuàng)建一個(gè)空對(duì)象
- 2、讓這個(gè)空對(duì)象的原型指向構(gòu)造函數(shù)的原型
- 3、讓構(gòu)造函數(shù)中的this指向這個(gè)空對(duì)象
- 4、返回這個(gè)新的對(duì)象
所以我們?cè)趯?shí)例化一個(gè)對(duì)象的過(guò)程中,其實(shí)就是將this指向該實(shí)例化出來(lái)的新對(duì)象。
function Person(name){
this.name = name;
console.log(this);
}
var p = new Person('McRay');//p
五、箭頭函數(shù)
ES6中新增加的箭頭函數(shù),讓函數(shù)體內(nèi)的this對(duì)象,指向的就是定義時(shí)的對(duì)象,拿之前的例子說(shuō)明一下。
var obj = {
name:'McRay';
foo:function(){
console.log(this);
}
foo2:function(){
console.log(this);obj
setTimeout(()=>{
console.log(this);//obj
}
}
}
obj.foo2();
當(dāng)我們?cè)趕etTimeout中使用箭頭函數(shù)的時(shí)候,函數(shù)體中的this默認(rèn)指向的就是定義這個(gè)這個(gè)函數(shù)時(shí),所在的對(duì)象,就是obj
六、call、apply、bind
這三個(gè)方法的目的都是差不多,就是可以動(dòng)態(tài)改變this的指向
1、call的第一個(gè)參數(shù)接受this指向的對(duì)象,然后是逐個(gè)傳參
2、apply的第一個(gè)參數(shù)和call相同,不同的是后面?zhèn)魅氲氖菂?shù)數(shù)組
3、bind是將生成一個(gè)新的函數(shù),將該函數(shù)的this指向我們規(guī)定的對(duì)象或者函數(shù),并不會(huì)立即執(zhí)行