1. <em id="2qvri"><tr id="2qvri"></tr></em>
      1. 首頁»JavaScript»深入淺出JavaScript之this

        深入淺出JavaScript之this

        來源:MuYunyun 發布時間:2016-10-13 閱讀次數:

        JavaScript中的this比較靈活,根據在不同環境下,或者同一個函數在不同方式調用下,this都有可能是不同的。但是有一個總的原則,那就是this指的是,調用函數的那個對象。

         

        下面是我的學習筆記,把它羅列成8種情況。

        全局的this(瀏覽器) 

        全局作用域的this一般指向全局對象,在瀏覽器中這對象就是window,在node中這對象就是global。

        console.log(this.document === document); // true (document === window.document)
        console.log(this === window); // true 
        this.a = 37;  //相當于創建了一個全局變量a
        console.log(window.a); // 37

        一般函數的this(瀏覽器) 

        一般的函數聲明或者函數表達式,直接調用函數的話,this依然指向全局對象,在瀏覽器中這對象就是window,在node中這對象就是global。

        function f1(){  
          return this;  
        } 
        f1() === window; // true, global object
        

        再舉一個例子,看完就非常透徹了

        function test(){
         this.x = 1;
          alert(this.x);
        }
        test(); // 1

        為了證明this就是全局對象,對代碼做一些改變:

        var x = 1;
        function test(){
         alert(this.x);
        }
        test(); // 1

        運行結果還是1。再變一下:

        var x = 1;
        function test(){
         this.x = 0;
        }
        test();
        alert(x); //0

        但是在嚴格模式下,一般函數調用的時候this指向undefined,這也是node為什么要用嚴格模式的一個原因。

        function f2(){  
          "use strict"; // see strict mode  
          return this; 
        } 
        f2() === undefined; // true
        

        作為對象方法的函數的this 

        this作為對象方法來使用是比較常見的。

        下面這個例子,我們創建了一個對象字面量o,o里面有個屬性f,它的值是一個函數對象,把函數作為對象屬性的值這種方式我們常常叫作對象的方法。作為對象的方法調用的時候,這時候this指向對象o

        var o = {  
           prop: 37,  
           f: function() {    
             return this.prop;    
          } 
        };  
        
        console.log(o.f()); // logs 37

        我們不一定要定義成函數字面量這樣子的對象,像下面這種情況,我們只定義了一個對象o,如果直接調用independent()函數的話,this會指向window,但是我們通過賦值的方式,臨時創建一個屬性f,并指向函數對象的時候,我們仍然拿到了37。

        var o = {prop: 37}; 
        
        function independent() {  
           return this.prop; 
        } 
        
        o.f = independent;  
        console.log(o.f()); // logs 37

         所以并不是看函數是怎么創建的,而是只要將函數作為對象的方法去調用,this就會指向這個對象。

        對象原型鏈上的this 

        下面這個例子中:我們先創建了一個對象o,里面有一個屬性f,函數作為對象屬性的值,我們通過Object.create(o)創建了一個對象p,p是一個空對象,它的原型會指向o,然后使用p.a = 1; p.b = 4創建對象p上的屬性,那么我們調用原型上的方法時,this.a,this.b依然能取到對象p上的a和b。這里需要注意的是p的原型才是o,我們調用p.f(),調用的是原型鏈o上的屬性f,原型鏈上的this可以拿到當前的對象p。

        var o = {f:function(){ return this.a + this.b; }};
        var p = Object.create(o); 
        p.a = 1; 
        p.b = 4; 
        console.log(p.f()); // 5 

        get/set方法與this 

        get/set方法中的this一般會指向get/set方法所在對象里面

        function modulus(){   
           return Math.sqrt(this.re * this.re + this.im * this.im); 
        } 
        var o = { 
          re: 1, 
          im: -1, 
          get phase(){      
             return Math.atan2(this.im, this.re);    
          } 
        }; 
        Object.defineProperty(o, 'modulus', {       //臨時動態給o對象創建modules屬性
          get: modulus, enumerable:true, configurable:true}); 
        
        console.log(o.phase, o.modulus); // logs -0.78 1.4142

        構造函數中的this

        用new把MyClass作為構造函數調用的話,this會指向空的對象,并且這個對象的原型會指向MyClass.prototype(可以看這篇文章對原型鏈的總結),但是調用的時候做了this.a = 37的賦值,所以最后this會作為返回值(沒寫return語句,或者return的是基本類型的話,會將this作為返回值),第二個例子return語句返回了對象,那么就會將a = 38作為返回值

        function MyClass(){    
           this.a = 37; 
        } 
        var o = new MyClass();  
        console.log(o.a); // 37 
        
        function C2(){    
           this.a = 37;   
           return {a : 38};  
        } 
        
        o = new C2();  
        console.log(o.a); // 38 

        call/apply方法與this 

        除了不同的調用方式外,函數對象有些方法能修改函數執行的this,比如call/apply。

        call和apply基本上沒差別,只不過call傳參的方式是扁平的,而apply是把一個數組傳進去。如下面這個例子

        什么時候用call和apply呢?比如我們想調用Object.prototype.toString,但是我們想指定某個this的時候,那我們就可以就用Object.prototype.toString.call(this)這樣子的方式來調用些無法直接調用的方法。如下面這個例子:

        function add(c, d){  
           return this.a + this.b + c + d;  
        } 
        var o = {a:1, b:3}; 
        add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16     //第一個參數接收的是你想作為this的對象
        add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34 
        
        function bar() {  
           console.log(Object.prototype.toString.call(this)); 
        } 
        bar.call(7); // "[object Number]"

        bind方法與this 

        bind方法是es5開始提供的,所以ie9+才支持

        function f(){  
           return this.a;  
        } 
        
        var g = f.bind({a : "test"});   //想把某個對象作為this的時候,就把它傳進去,得到一個新對象g
        console.log(g()); // test       //重復調用的時候,this已經指向bind參數。這對于我們綁定一次需要重復調用依然實現綁定的話,會比apply和call更加高效(看下面這個例子)
        
        var o = {a : 37, f : f, g : g};  
        console.log(o.f(), o.g()); // 37, test   //o.f()通過對象的屬性調用,this指向對象o;比較特殊的是即使我們把新綁定的方法作為對象的屬性調用,o.g()依然會按之前的綁定去走,所以答案是test不是g

        總結

        做項目的時候才發現這些基礎概念有多么的重要,如果不把它們逐個落實了,真的是一不小心就會掉進坑里。后續我還會對原型鏈,作用域,繼承,鏈式調用,正則等知識進行總結,歡迎關注 

        QQ群:WEB開發者官方群(515171538),驗證消息:10000
        微信群:加小編微信 849023636 邀請您加入,驗證消息:10000
        提示:更多精彩內容關注微信公眾號:全棧開發者中心(fsder-com)
        網友評論(共0條評論) 正在載入評論......
        理智評論文明上網,拒絕惡意謾罵 發表評論 / 共0條評論
        登錄會員中心
        江苏快3投注技巧