好程式設計師Web前端培訓分享如何講清楚this指向?

好程式設計師發表於2020-05-09

  好程式設計師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物件是可以省略不寫的!

所以,上面的程式碼,實際上等價於:

function show(){

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

 

我再舉一個常見的例子,關於事件繫結

btn.onclick  =  function(){

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

最終,無論是手動呼叫,還是單擊按鈕呼叫

列印結果都是btn物件

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

this總是指向,呼叫該函式的物件

如果你的程式碼結構是這樣的

物件.函式();

那麼,函式里的this,必然指向這個物件本身!

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

function show(){

    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就指向哪個物件

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

再看一個例子:

btn.onclick  =  function(){

    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);}

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

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

var obj  = {

    show :  function(){

        console.log( this);

    }}

上面的程式碼,最終列印 obj 物件
無論經過多少曲折,我們最終只看一個結論,那就是:

this所在的函式,由哪個物件呼叫?


我把程式碼進一步改造

function fn(){

    console.log( this);}

var obj  = {

    show : fn}

btn.onclick  =  function(){

    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衍生出的問題

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

btn.onclick  =  function(){

    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的指向非常清晰了吧

圖片1

2. 箭頭函式可以用call來改變this指向嗎?

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

圖片2

最後一個特例: 建構函式

1. 什麼是建構函式?

假設有一個函式Fn, 我們有兩種方式來呼叫它

  • 普通的呼叫 Fn()
  • 配合new 關鍵字來呼叫 new Fn()

第二種呼叫方式, 函式就變成了建構函式

---------------------------------------

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

----------------------------------------

2. 那建構函式里的this是誰呢?

請期待下一篇文章《建構函式與class》


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69913864/viewspace-2690834/,如需轉載,請註明出處,否則將追究法律責任。

相關文章