天天看點

好程式員Web前端教育訓練分享如何講清楚this指向?

  好程式員Web前端教育訓練分享如何講清楚this指向?不得不說,要搞清楚this是需要一個前提的。

  你首先得知道函數、對象、作用域等基本概念。

  知道 call、apply、bind方法那再好不過了

  當然學好國文是很重要的

  需要知道第一人稱和第二人稱和第三人稱的差別

先來看一個新聞:

“我體内的惡魔已被鎖住了這麼多年,現在這種鎖鍊已經松了。”

“我很害怕,很孤獨,很疑惑,我将要向導緻我的痛苦的根源——社會作出反擊,我想盡我所能地去傷害這個社會,然後死去。”

這段令人不寒而栗的文字,是美國北達科他州系列殺人案嫌犯約瑟夫·鄧肯于2005年5月11日寫下的。當人們在近兩個月後看到這段文字時,他已經将一個 5口之家的3人殘忍地殺害,并綁架了另外2名分别為8歲和9歲的孩子。

假設,你是一個jc,抓到了一個xf

然後你在他的家裡搜出這本日記,此時你作何感想?

你肯定會想,如果這是嫌疑人自己寫的,這不就等于認罪了嘛?

當然,犯罪嫌疑人也可以狡辯說,這日記根本不是我寫的,隻是我從網上摘抄的段子

我們試着把日記稍作改動,看看會有什麼效果變化。

他體内的惡魔已被鎖住了這麼多年,現在這種鎖鍊已經松了。他很害怕,很孤獨,很疑惑,他将要向導緻他的痛苦的根源——社會作出反擊,他想盡他所能地去傷害這個社會,然後死去。

注意到了嗎?

把第一人稱改成了第三人稱

這看上去根本不像日記,更像是一個小說故事。

為什麼一個字的改動會有這麼大差别呢?

帶着對這個問題的思考, 我們來開始今天this指向的學習。

this的英文含義

先看英文解釋 this: 這樣、這個

接下來看一段代碼

var obj = {

show : function(){

    console.log(this);

}}obj.show();
           

結果很容易預測,列印obj對象本身

在JS中,this屬于一個關鍵字,也就是可以了解為,它是一個系統自帶指令

通常,我們把它的含義解釋為:目前對象

那麼問題來了: 目前對象到底是指誰呢?

在上面的代碼案例中,this代表的就是obj這個對象

接下來我們再看一段代碼

function show(){

console.log(this);}show();
           

結果列印window對象

如果你對這個列印結果感到奇怪,那麼可能你忽略了一個常識問題

window對象是可以省略不寫的!

是以,上面的代碼,實際上等價于:

console.log(this);}window.show();
           

我再舉一個常見的例子,關于事件綁定

btn.onclick = function(){

console.log(this);}btn.onclick();   //手動調用函數//除手動調用外,滑鼠單擊按鈕也可以觸發函數執行
           

最終,無論是手動調用,還是單擊按鈕調用

列印結果都是btn對象

我們似乎總結出了一個this指向的規律

this總是指向,調用該函數的對象

如果你的代碼結構是這樣的

對象.函數();

那麼,函數裡的this,必然指向這個對象本身!

假設這個結論是成立的, 我們不妨來驗證一下我們的猜想!!

console.log(this);}window.show();  //列印window對象var obj1 = {};obj1.show1 = show;obj1.show1();  //列印obj1對象var obj2 = {};obj2.show2 = obj1.show1;obj2.show2();  //列印obj2對象
           

從上面代碼的例子中,可以看出來

window對象和obj1對象和obj2對象,共享了一個函數 show

window.show == obj1.show1; //truewindow.show == obj2.show2; //true

三個對象,用了同一個函數

但列印出的this是各不相同的

window.show(); 列印出window對象

obj1.show1(); 列印出obj1對象

obj2.show2(); 列印出obj2對象

這似乎再一次印證了,我們剛才的猜想:

函數由哪個對象調用,this就指向哪個對象

科學是嚴謹的,得出結論之前,我們還是要反複驗證

再看一個例子:

setTimeout(function(){

    console.log(this);

},0)}btn.onclick();
           

實際上,我隻是在原來代碼的基礎上,增加了一個延遲器,并且時間設為0

那麼列印出的this會不會有變化呢??

你可以先思考一番

通常按照直覺,我們會認為,延遲器隻是延緩了執行時間,列印結果依然還是btn對象,沒有變化

但經過測試發現,實際的列印結果,是 window對象

是我們剛才的猜想錯了嗎?

要解釋這個現象,我們得重新來觀察這段代碼

btn.onclick = function(){ //<----這個函數,用A來表示 setTimeout(function(){ //<----這個函數,用B來表示 console.log(this);

},0)}btn.onclick();
           

注意代碼當中出現了兩個函數,我們分别起名字叫做函數A和函數B

按照我們剛才的猜想: 函數由哪個對象調用,this就指向哪個對象

是以,this指向會依賴它所在的函數

而這個函數,到底是 函數A還是函數B呢?

其實你不難從代碼中看的出來, this很明顯是在函數B中的

是以, 結果沒有列印出 btn, 現在我們也不感到奇怪了

因為, this已經不再函數 A的内部了,而是函數B的内部

你可能還要問,為什麼函數B裡的 this指向window呢?

這裡其實算是一個特例,傳入定時器的函數,由哪個對象調用,我們不得而知

這種情況,this就指向window

你暫時記住這個規律就好了,等你學完了作用域鍊,你就會明白其中的本質

回到我們開頭的新聞

假設日記就是嫌疑人寫的。 但日記裡全是第三人稱。那麼 『 他 』到底是誰就很難說了

反過來如果日記裡用的都是第一人稱寫的。 那麼 『 我 』肯定指的是嫌疑人自己

JS函數當中的this關鍵字, 就相當于我們說話中的第一人稱代詞我

例如這樣一個例子:

A對B說:“我要殺了你!”

這裡的『我』指代A, 『你』指代B

B對A說:”我要弄死你!”

這裡的『我』指代B, 『你』指代A

是以你看,同樣的一個字,它可以指代任何人,關鍵看從誰的嘴裡說出來

function fn(){

//this, 就相當于中文裡的我    //不要上來就問this會指向誰    //我們必須搞清楚上下文環境,fn是誰調用的?(相當于這句話從誰的嘴裡說出來)    //如果我們不能弄清楚這個問題,讨論this指向就沒有意義    console.log(this);}
           

到目前為止,我們差不多可以得出結論了

下面用幾個練習最終驗證一下

show: function(){

    console.log(this);

}}
           

上面的代碼,最終列印obj對象

無論經過多少曲折,我們最終隻看一個結論,那就是:

this所在的函數,由哪個對象調用?

我把代碼進一步改造

console.log(this);}
           
show: fn}
           
window.setTimeout(function(){

    obj.show();

}, 100);}
           

上面的代碼,最終列印還是obj對象

當然了,也總會有一些例外情況, 比如下面這個:

function m1(){

function m2(){

    console.log(this);

}

m2();}m1();
           

我們不禁要問,函數m2是由哪個對象調用的?

我們想盡了各種可能,最終發現都是錯的。

我們始終不知道這個m2由哪個對象調用,好像它就那樣執行了

而實際的列印結果呢?

不出意外,還是window對象

最後的結論

  1. 所有的this關鍵字,在函數運作時,才能确定它的指向
  2. this所在的函數由哪個對象調用,this就會指向誰
  3. 當函數執行時,沒有明确的調用對象時,則this指向window

    由this衍生出的問題

剛才遺留了一個問題沒有解決

setTimeout(function(){

    console.log(this);

},0)}btn.onclick();
           

我們期待this指向btn,而this現在卻指向了window

這個問題該怎麼修複呢? 有很多辦法

如果你不知道call、apply、bind,那麼恐怕你隻能看得懂方法A

//方法Abtn.onclick = function(){

var self = this; //使用變量儲存this,self變量的值是不會随着環境改變的    setTimeout(function(){

    console.log(self);  

},0)}btn.onclick();
           

//方法Bbtn.onclick = function(){

var self = this; //使用變量儲存this 

function fn(){  //将代碼寫在一個函數fn中        console.log(this);

}
           
setTimeout(function(){

    fn.call(self); //強行指定this為self對象    },0)}btn.onclick();/*  call方法的作用,是調用函數,同時指定this可以代表誰  例如 fn.call(obj)  意思就是 調用函數fn,并且this指向obj對象*/
           

//方法Cbtn.onclick = function(){

var self = this; //使用變量儲存this 

function fn(){  //将代碼寫在一個函數fn中        console.log(this);

}
           
setTimeout(function(){

    fn.apply(self); //使用apply方法調用函數,強行指定this為self對象    },0)}btn.onclick();/*  apply方法的作用,是調用函數,同時指定this可以代表誰  例如 fn.apply(obj)  意思就是 調用函數fn,并且this指向obj對象*/
           

//方法Dbtn.onclick = function(){

setTimeout(function(){

    console.log(this);

}.bind(this), 0 )

//使用bind方法,将定時器函數的this強行綁定為事件函數的this}btn.onclick();/*  bind方法的作用,是綁定函數的this,同時傳回綁定後的新函數  例如   var fb = fn.bind(obj);  window.fb();  無論誰調用fb函數, 函數的this都會指向obj*/
           

接下來的内容,請學完ES6的箭頭函數再來看吧

  1. 如何判斷箭頭函數的this?

因為箭頭函數不具備自己的this,是以非常簡單,假裝它不存在,就像這樣:

這下this的指向非常清晰了吧

好程式員Web前端教育訓練分享如何講清楚this指向?
  1. 箭頭函數可以用call來改變this指向嗎?

不能!! 試圖改變箭頭函數的this是徒勞的。

好程式員Web前端教育訓練分享如何講清楚this指向?

最後一個特例:構造函數

  1. 什麼是構造函數?

假設有一個函數Fn, 我們有兩種方式來調用它

普通的調用 Fn()

配合new關鍵字來調用 new Fn()

第二種調用方式, 函數就變成了構造函數

注意,在構造函數中, 上面我們所講的結論,是不成立的!!

  1. 那構造函數裡的this是誰呢?

請期待下一篇文章《構造函數與class》

繼續閱讀