JS 里的 this
- 1、在 function 內(nèi)部被創(chuàng)建
- 2、指向調(diào)用時(shí)所在函數(shù)所綁定的對(duì)象
- 3渔彰、this 不能被賦值,但可以被 call/apply/bind 改變
this 的指向在函數(shù)定義的時(shí)候是確定不了的推正,只有函數(shù)執(zhí)行的時(shí)候才能確定 this 到底指向誰(shuí)恍涂,實(shí)際上 this 的最終指向的是最近調(diào)用該 this 所在的函數(shù)的對(duì)象
( this 所在的函數(shù)由哪個(gè)最近的對(duì)象調(diào)用,this 就會(huì)指向誰(shuí))舔稀,當(dāng)函數(shù)執(zhí)行時(shí)乳丰,沒(méi)有明確的調(diào)用對(duì)象時(shí),則 this 指向 window
例子1:
function a(){
var user = "簡(jiǎn)書(shū)";
console.log(this); // this --> Window
console.log(this.user); // undefined
}
a();
`按照我們上面說(shuō)的 this 最終指向的是最近調(diào)用該函數(shù)的對(duì)象内贮,這里的函數(shù) a 實(shí)際是被 Window 對(duì)象所點(diǎn)出來(lái)的产园,下面的代碼就可以證明`
function a(){
var user = "簡(jiǎn)書(shū)";
console.log(this); // this --> Window
console.log(this.user); // undefined
}
window.a();
例子2:
var o = {
user: "簡(jiǎn)書(shū)",
fn: function(){
console.log(this); // this --> o
console.log(this.user); // 簡(jiǎn)書(shū)
}
}
o.fn();
`這里的 this 指向的是對(duì)象 o,因?yàn)槟阏{(diào)用這個(gè) fn 是通過(guò) o.fn() 執(zhí)行的夜郁,那自然指向就是對(duì)象 o什燕,
這里再次強(qiáng)調(diào)一點(diǎn),this 的指向在函數(shù)創(chuàng)建的時(shí)候是決定不了的竞端,在調(diào)用的時(shí)候才能決定屎即,誰(shuí)調(diào)用的就指向誰(shuí),一定要搞清楚這個(gè)`
var o = {
user: "簡(jiǎn)書(shū)",
fn: function(){
console.log(this); // this --> o
console.log(this.user); // 簡(jiǎn)書(shū)
}
}
window.o.fn();
`這段代碼和上面的那段代碼幾乎是一樣的事富,但是這里的 this 為什么不是指向 window技俐,如果按照上面的理論,最終 this 指向的是調(diào)用該函數(shù)的對(duì)象统台,
這里先說(shuō)個(gè)題外話雕擂,window 是 js 中的全局對(duì)象,我們創(chuàng)建的變量實(shí)際上是給 window 添加屬性贱勃,所以這里可以用 window 點(diǎn) o 對(duì)象`
這里先不解釋為什么上面的那段代碼 this 為什么沒(méi)有指向 window井赌,我們?cè)賮?lái)看一段代碼
例子3:
var o = {
a: 10,
b: {
a: 12,
fn: function(){
console.log(this); // this --> b
console.log(this.a); // 12
}
}
}
o.b.fn()
`這里同樣也是對(duì)象 o 點(diǎn)出來(lái)的谤逼,但是同樣 this 并沒(méi)有指向它,那你肯定會(huì)說(shuō)我一開(kāi)始說(shuō)的那些不就都是錯(cuò)誤的嗎仇穗?
其實(shí)也不是流部,只是一開(kāi)始說(shuō)的不準(zhǔn)確,接下來(lái)我將補(bǔ)充一句話纹坐,我相信你就可以徹底的理解 this 的指向的問(wèn)題`
情況1:如果一個(gè)函數(shù)中有 this枝冀,但是它沒(méi)有被上一級(jí)的對(duì)象所調(diào)用,那么 this 指向的就是 window恰画,這里需要說(shuō)明的是在 js 的嚴(yán)格版中 this 指向的不是 window宾茂,但是我們這里不探討嚴(yán)格版的問(wèn)題,你想了解可以自行上網(wǎng)查找
情況2:如果一個(gè)函數(shù)中有 this拴还,這個(gè)函數(shù)有被上一級(jí)的對(duì)象所調(diào)用跨晴,那么 this 指向的就是上一級(jí)的對(duì)象
情況3:如果一個(gè)函數(shù)中有 this,這個(gè)函數(shù)中包含多個(gè)對(duì)象片林,盡管這個(gè)函數(shù)是被最外層的對(duì)象所調(diào)用端盆,this 指向的也只是它上一級(jí)的對(duì)象,例子3可以證明费封,如果不相信焕妙,那么接下來(lái)看例子4
例子4:
var o = {
a: 10,
b: {
// a: 12,
fn: function(){
console.log(this); // this --> b
console.log(this.a); // undefined
}
}
}
o.b.fn()
`盡管對(duì)象 b 中沒(méi)有屬性 a壁肋,這個(gè) this 指向的也是對(duì)象 b越败,因?yàn)?this 只會(huì)指向它的上一級(jí)對(duì)象,不管這個(gè)對(duì)象中有沒(méi)有 this 要的東西`
例子5:
var o = {
a: 10,
b: {
a: 12,
fn: function(){
console.log(this); // this --> window
console.log(this.a); // undefined
}
}
}
var j = o.b.fn;
j();
這里 this 指向的是 window衡瓶,是不是有些蒙了韧献?其實(shí)是因?yàn)槟銢](méi)有理解一句話末患,這句話同樣至關(guān)重要
this 永遠(yuǎn)指向的是最后調(diào)用它的對(duì)象,也就是看它執(zhí)行的時(shí)候是誰(shuí)調(diào)用的锤窑,例子5中雖然函數(shù) fn 是被對(duì)象 b 所引用璧针,但是在將 fn 賦值給變量 j 的時(shí)候并沒(méi)有執(zhí)行,所以最終指向的是 window渊啰,這和例子4是不一樣的探橱,例子4是直接執(zhí)行了 fn
this 講來(lái)講去其實(shí)就是那么一回事,只不過(guò)在不同的情況下指向的會(huì)有些不同绘证,上面的總結(jié)每個(gè)地方都有些小錯(cuò)誤隧膏,也不能說(shuō)是錯(cuò)誤,而是在不同環(huán)境下情況就會(huì)有不同嚷那,所以我也沒(méi)有辦法一次解釋清楚胞枕,只能你慢慢地的去體會(huì)
再舉一個(gè)常見(jiàn)的例子,關(guān)于事件綁定
例子6:
btn.onclick = function(){
console.log(this); // this --> btn
}
btn.onclick();
function fn(){
console.log(this); // this --> obj
}
var obj = {
show: fn
}
btn.onclick = function(){
window.setTimeout(function(){
obj.show();
}, 100);
}
`自行體會(huì)一下车酣,為什么這個(gè) this 指向的是 obj`
科學(xué)是嚴(yán)謹(jǐn)?shù)那冢贸鼋Y(jié)論之前,我們還是要反復(fù)驗(yàn)證湖员,再看一個(gè)例子
例子7:
btn.onclick = function(){
setTimeout(function(){
console.log(this); // this --> Window
}, 0);
}
btn.onclick();
`當(dāng)函數(shù)執(zhí)行時(shí)贫悄,沒(méi)有明確的調(diào)用對(duì)象時(shí),則 this 指向 Window`
從例子7代碼中看的出來(lái)娘摔, this 不再函數(shù) btn 的內(nèi)部窄坦,而是在函數(shù) setTimeout 的內(nèi)部,所以結(jié)果沒(méi)有打印出 btn凳寺, 現(xiàn)在我們也不感到奇怪了
你可能還要問(wèn)鸭津,為什么函數(shù) setTimeout 里的 this 指向 window 呢?
這里其實(shí)算是一個(gè)特例肠缨,傳入定時(shí)器的函數(shù)逆趋,是由哪個(gè)對(duì)象調(diào)用的?我們不得而知晒奕,這種情況闻书,this 就指向 window,以下例子8脑慧、例子9同樣
例子8:
function m1(){
function m2(){
console.log(this); // this --> Window
}
m2();
}
m1();
例子9:
var name = "the window";
var object = {
name: "my object",
getNameFunc: function(){
return function(){
return this.name; // this --> Window
}
}
}
console.log(object.getNameFunc()()); // the window
由 this 衍生出的問(wèn)題
剛才遺留了一個(gè)問(wèn)題沒(méi)有解決
btn.onclick = function(){
setTimeout(function(){
console.log(this); // this --> Window
}, 0);
}
btn.onclick();
我們期待 this 指向 btn魄眉,而 this 現(xiàn)在卻指向了 window,這個(gè)問(wèn)題該怎么修復(fù)呢闷袒? 有很多辦法
如果你不知道call坑律、apply、bind囊骤,那么恐怕你只能看得懂例子10的方法
例子10:
var name = "the window";
var object = {
name: "my object",
getNameFunc: function(){
var that = this;
return function(){
return that.name; // that --> object
}
}
}
console.log(object.getNameFunc()()); // my object
btn.onclick = function(){
var that = this; // 使用變量保存 this晃择,that 變量的值是不會(huì)隨著環(huán)境改變的
setTimeout(function(){
console.log(that); // that --> btn
},0);
}
btn.onclick();
例子11:
btn.onclick = function(){
var that = this; // 使用變量保存 this
function fn(){ // 將代碼寫(xiě)在一個(gè)函數(shù) fn 中
console.log(this); // this --> btn
}
setTimeout(function(){
fn.call(that); // 強(qiáng)行指定 this 為 that 對(duì)象
}, 0);
}
btn.onclick();
/*
call 方法的作用,是調(diào)用函數(shù)淘捡,同時(shí)指定 this 可以代表誰(shuí)
例如 fn.call(obj)
意思就是 調(diào)用函數(shù) fn藕各,并且 this 指向 obj 對(duì)象
*/
例子12:
btn.onclick = function(){
var that = this; // 使用變量保存 this
function fn(){ // 將代碼寫(xiě)在一個(gè)函數(shù) fn 中
console.log(this); // this --> btn
}
setTimeout(function(){
fn.apply(that); // 使用 apply 方法調(diào)用函數(shù),強(qiáng)行指定 this 為 that 對(duì)象
}, 0);
}
btn.onclick();
/*
apply 方法的作用焦除,是調(diào)用函數(shù)激况,同時(shí)指定 this 可以代表誰(shuí)
例如 fn.apply(obj)
意思就是 調(diào)用函數(shù) fn,并且 this 指向 obj 對(duì)象
*/
例子13:
btn.onclick = function(){
setTimeout(function(){
console.log(this); // this --> btn
}.bind(this), 0);
// 使用 bind 方法膘魄,將定時(shí)器函數(shù)的 this 強(qiáng)行綁定為事件函數(shù)的 this
}
btn.onclick();
/*
bind 方法的作用乌逐,是綁定函數(shù)的 this,同時(shí)返回綁定后的新函數(shù)
例如
var fb = fn.bind(obj);
window.fb();
無(wú)論誰(shuí)調(diào)用 fb 函數(shù), 函數(shù)的 this 都會(huì)指向 obj
*/
關(guān)于自行改變 this 的指向請(qǐng)看JavaScript中call, apply, bind方法的總結(jié)创葡,詳細(xì)的說(shuō)明了我們?nèi)绾问謩?dòng)更改 this 的指向
箭頭函數(shù)版 this
1.如何判斷箭頭函數(shù)的 this
因?yàn)榧^函數(shù)不具備自己的 this浙踢,所以非常簡(jiǎn)單,假裝它不存在灿渴,就像這樣
這下 this 的指向非常清晰了吧
2. 箭頭函數(shù)可以用 call 來(lái)改變 this 指向嗎洛波?
不能R扔摺! 試圖改變箭頭函數(shù)的 this 是徒勞的
構(gòu)造函數(shù)版 this
function Fn(){
this.user = "簡(jiǎn)書(shū)";
}
var a = new Fn();
console.log(a.user); // 簡(jiǎn)書(shū)
這里之所以對(duì)象 a 可以點(diǎn)出函數(shù) Fn 里面的 user 是因?yàn)?new 關(guān)鍵字可以改變 this 的指向蹬挤,將這個(gè) this 指向?qū)ο?a缚窿,為什么我說(shuō) a 是對(duì)象,因?yàn)橛昧?new 關(guān)鍵字就是創(chuàng)建一個(gè)對(duì)象實(shí)例焰扳,我們這里用變量 a 創(chuàng)建了一個(gè) Fn 的實(shí)例(相當(dāng)于復(fù)制了一份 Fn 到對(duì)象 a 里面)倦零,此時(shí)僅僅只是創(chuàng)建,并沒(méi)有執(zhí)行吨悍,而調(diào)用這個(gè)函數(shù) Fn 的是對(duì)象 a扫茅,那么 this 指向的自然是對(duì)象 a,那么為什么對(duì)象 a 中會(huì)有 user育瓜,因?yàn)槟阋呀?jīng)復(fù)制了一份 Fn 函數(shù)到對(duì)象 a 中葫隙,用了 new 關(guān)鍵字就等同于復(fù)制了一份
更新一個(gè)小問(wèn)題當(dāng) this 碰到 return 時(shí)
function fn() {
this.user = '簡(jiǎn)書(shū)';
return {};
}
var a = new fn;
console.log(a.user); // undefined
function fn() {
this.user = '簡(jiǎn)書(shū)';
return function(){};
}
var a = new fn;
console.log(a.user); // undefined
function fn() {
this.user = '簡(jiǎn)書(shū)';
return 1;
}
var a = new fn;
console.log(a.user); // 簡(jiǎn)書(shū)
function fn() {
this.user = '簡(jiǎn)書(shū)';
return undefined;
}
var a = new fn;
console.log(a.user); // 簡(jiǎn)書(shū)
如果返回值是一個(gè)對(duì)象,那么 this 指向的就是那個(gè)返回的對(duì)象躏仇,如果返回值不是一個(gè)對(duì)象那么 this 還是指向函數(shù)的實(shí)例
function fn() {
this.user = '簡(jiǎn)書(shū)';
return undefined;
}
var a = new fn;
console.log(a); // fn {user: "簡(jiǎn)書(shū)"}
還有一點(diǎn)就是雖然 null 也是對(duì)象停蕉,但是在這里 this 還是指向那個(gè)函數(shù)的實(shí)例,因?yàn)?null 比較特殊
function fn() {
this.user = '簡(jiǎn)書(shū)';
return null;
}
var a = new fn;
console.log(a.user); // 簡(jiǎn)書(shū)
知識(shí)點(diǎn)補(bǔ)充:
1.在嚴(yán)格版中的默認(rèn)的 this 不再是 window钙态,而是 undefined慧起。
2.new 操作符會(huì)改變函數(shù) this 的指向問(wèn)題,雖然我們上面講解過(guò)了册倒,但是并沒(méi)有深入的討論這個(gè)問(wèn)題蚓挤,網(wǎng)上也很少說(shuō),所以在這里有必要說(shuō)一下
function fn(){
this.num = 1;
}
var a = new fn();
console.log(a.num); // 1
為什么 this 會(huì)指向 a驻子?首先 new 關(guān)鍵字會(huì)創(chuàng)建一個(gè)空的對(duì)象灿意,然后會(huì)自動(dòng)調(diào)用一個(gè)函數(shù) apply 方法,將 this 指向這個(gè)空對(duì)象崇呵,這樣的話函數(shù)內(nèi)部的 this 就會(huì)被這個(gè)空的對(duì)象替代
通過(guò) call() 和 apply() 改變函數(shù)執(zhí)行環(huán)境的情況下缤剧,this 就會(huì)指向其他對(duì)象