深入學習JavaScript之初識this

  JavaScript是一個詞法作用域的編程語言,詞法作用域和動態作用域的區別我也說過了,具體在我的《深入理解JavaScript之詞法域》https://blog.csdn.net/qq_41889956/article/details/83061472中有介紹,

  總的來說

  動態作用域是根據調用棧關係來確定變量值的,比如在當前的函數找不到,那麼就會到它調用的函數中找。

  詞法作用域是在詞法分析階段(代碼編寫時)就已經決定的了。

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

今天要學習的  this機制   與   動態作用域差不多。

  this是JavaScript中最爲重要的機制之一,同時也是最爲複雜的機制之一,在大型項目裏,this來this去會讓你感到一頭霧水。這個  this  與英語單詞中理解的  this  不太一樣,並不是單純的指這個函數對象的本身。

 1.1爲什麼要用this

  接下來請看看例子,讓我們來看看this的用處


        function identify() {
            return this.name.toUpperCase();

        }
        function speack() {
            var greeting="Hello,I'm"+identify.call(this);
            console.log(greeting);
        }
        var me={
            name:"Kyle"
        };
        var you={
            name:"Reader"
        };

       console.log(identify.call(me));    //KYLE
       console.log( identify.call( you ));      //READER

        speack.call(me);      //Hello,I'm KYLE
        speack.call(you);     //Hello,I'm RAEDER
    

  在理解這篇代碼之前,讓我們先來看看,.call()是什麼,這是一個能夠修改" this"指針的方法,作用是將函數內的"this"指針對象轉移到"()"內的對象中

  接下來我們看看代碼,代碼中,identify(...)函數將"this.name.toUpperCase()"作爲返回值,此函數是修改傳入對象的name值,使其輸出大寫。

   這段代碼可以在不同的上下文對象(me和you)中重複使用函數identify(...)以及speack(...)來輸出不同的結果,而不用對不同的對象編寫不同的結果。

 

如果不使用  this  的話,我們就需要顯示的創建一個上下文對象,


        function identify(context) {
        return context.name.toUpperCase();
    }
    function speak(context) {
        var greeting = "Hello, I'm " + identify(context);
        console.log( greeting );
    }
    var me = {
        name: "Kyle"
    };
    var you = {
        name: "Reader"
    };
   console.log(identify(me));    //KYLE
   console.log( identify( you ));      //READER
    speak( me ); // Hello, 我是 KYLE
    speak( you ); // Hello, 我是 READER

  使用  this  能夠隱式的傳遞對象的引用,讓你的代碼變得更加的優美,簡潔,易於複用。

  隨着你使用的模式越來越複雜,顯式傳遞上下文對象會讓代碼變得越來越混亂,使用  this  能夠很好的解決這個問題

 

1.2  誤解

   在我們正式瞭解  this  的作用機制的時候,先讓我們來消除一些對  this  常有的誤解。

   太拘泥於  this  的字面意思會產生一個誤解。有兩種對  this  的誤解,它們都是錯誤的

 

1.2.1  誤解一:指向自身

   在英語字面上解釋  this  的話,通常是指向函數本身,在函數作用域中使用它,目的也是指向自己(函數內部調用自己),那麼在函數內部指向自己(執行自己)是在什麼情況呢?

  一般是在遞歸的情況下,比如:階乘函數,就需要不斷的調用自己、或者在第一次調用時自己解綁的事件處理器。

 在JavaScript一些開發者很容易混淆存儲狀態的位置(存儲狀態=屬性的值),因爲JavaScript中有多種模式可以存儲狀態,有一部分人認爲,函數是一個對象(在JavaScript中,函數看作對象),那麼就可以在調用函數時存儲狀態 這是可行的,但是你要記住,存儲狀態不止這一個位置。

   接下來我們看看這個代碼


        function foo(num) {
            console.log("foo"+num);

            this.count++;
        }
        foo.count=0;
        var i;
        for(i=0;i<10;i++){
            if(i>5){
                foo(i);     //6-7-8-9
            }
        }
        console.log("foo被調用:"+foo.count+"次");    //0
    

  console.log(...)輸出了四條語句,證明foo(...)函數確實被調用了四次,但是foo(...)函數的計數器並沒有發生計數,很明顯,是foo(...)中this  對象的錯誤。

  執行  foo.count=0  時,確實是向函數對象  foo  添加了一個屬性  count。但是函數內部代碼  this.count依然是0,那就證明  this.count中的  this  並不是指向  foo  這個對象,屬性相同但是對象不同。

     實際上如果深究的話,foo(...)中  this.conut  是創建了一個全局變量conut,它的值爲NaN。

    爲什麼呢?還記得我們之前講過的查找方式吧,this.count++  使用的是LHS查找,在當前作用域中查找不到count變量,於是它返回到上一層查找,這時,在上一層(詞法作用域)中同樣沒有查找到,因爲進行的是LHS查找,所以它會自動在全局作用域中創建一個變量count,它的值爲NaN

-------------------------------------------這充分說明了  this  並不是指向函數本身

  要解決上面的問題有多種方法

  • 創建全局變量count,此方法沒有用到  this  而是用到了詞法作用域
  • 利用foo代替this,此方法同樣沒有用到  this  用到的是foo(...)的函數作用域
  • 利用call強制foo(...)中的  this  與foo進行綁定,在foo(i)------->>>foo.call(foo,i)   call正確用法是:this存在的函數.call(需要this綁定的函數,傳入this存在的函數的參數)

1.2.2  誤解二:它的作用域

  第二種常見的誤區是將this指向它的作用域。這個問題涉及到了很多東西,在某些情況下,它是正確的,在某些情況下,它是錯誤的。

 

    this在任何情況下都不會指向函數的詞法作用域,在JavaScript內部,作用域和對象類似,可見的標識符都是它的屬性。但是作用域對象無法通過JavaScript代碼訪問,它存在於JavaScript引擎內部。

 

我們看看以下的代碼,

  function foo() {
           var a=2;
           this.bar();
       }
       function bar() {
           console.log(this.a);
       }
       foo();

  這個代碼中有很多個錯誤,這段代碼似圖跨過邊界,使用 this 來隱式引用函數的詞法作用域。但這是無法實現的

  首先,這段代碼似圖通過  this.bar()  來引用bar(...)函數。這個是不可能成功的,要調用bar(...)最好的方法是直接使用詞法引用標識符bar(...),不要  this。

  此外這段代碼還試圖用  this  溝通foo()、bar()的詞法域,從而讓bar()能夠訪問foo()中的變量a,這是不可能實現的,

因爲你無法使用this來引用詞法作用域裏面的東西。

 

1.3  this到底是什麼?

    我們之前說過  this  是在運行時進行綁定的,就同動態作用域一樣,它的上下文取決於函數調用的各種條件

    this的綁定和函數聲明的位置沒有任何的關係,只取決於函數調用的方式

 簡單說下  this  。當一個函數被調用時,會創建一個活動記錄(執行上下文)。這個記錄會包含函數在哪被調用(調用棧),函數調用的方法,傳入的參數等等。this就是記錄其中的一個屬性而已,會在函數執行時用到。

 

總結:this 機制是JavaScript中最爲複雜以及最爲重要的機制之一,它的用處非常廣。

          要認識this機制首先要知道,this並不是指向函數自身,也不是指向函數的作用域。

          this  實際上是在函數被調用時綁定的,它指向什麼完全取決於函數在哪被調用。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章