箭頭函數中的this

ES6中新增了箭頭函數這種語法,箭頭函數以其簡潔性和方便獲取this的特性,俘獲了大批粉絲兒

它也可能是面試中的寵兒, 我們關鍵要搞清楚 箭頭函數和普通函數中的this

一針見血式總結:

普通函數中的this:

1. this總是代表它的直接調用者(js的this是執行上下文), 例如 obj.func ,那麼func中的this就是obj

2.在默認情況(非嚴格模式下,未使用 'use strict'),沒找到直接調用者,則this指的是 window (約定俗成)

3.在嚴格模式下,沒有直接調用者的函數中的this是 undefined

4.使用call,apply,bind(ES5新增)綁定的,this指的是 綁定的對象

箭頭函數中的this

箭頭函數沒有自己的this, 它的this是繼承而來; 默認指向在定義它時所處的對象(宿主對象),而不是執行時的對象, 定義它的時候,可能環境是window; 箭頭函數可以方便地讓我們在 setTimeout ,setInterval中方便的使用this

下面通過一些例子來研究一下 this的一些使用場景


要整明白這些, 我們需要首先了解一下作用域鏈:

當在函數中使用一個變量的時候,首先在本函數內部查找該變量,如果找不到則找其父級函數,

最後直到window,全局變量默認掛載在window對象下


1.全局變量默認掛載在window對象下


  1. <script>
  2.  var aa = 2;
  3.  alert(window.aa);
  4.  (function () {
  5.    aa = 3;
  6.  })();
  7.  alert(window.aa);
  8. </script>

我們僅僅聲明瞭一個全局變量aa,但是打印出window.aa卻和aa保持一致,爲什麼呢?

眼見爲實, 我們使用 console.dir(window) 打印 window對象看看

我們可以看到在window屬性中,看到 aa 屬性了;此外,函數也適用於此情況,全局函數也會掛在在window對象下

我們常見的window的屬性和方法有: alert, location,document,parseInt,setTimeout,setInterval等,window的屬性默認可以省略window前綴!

2.在普通函數中,this指向它的直接調用者;如果找不到直接調用者,則是window

我們來看一些例子

示例1:


  1. <script>
  2.  function test() {
  3.    console.log(this);
  4.  }
  5.  test();
  6. </script>

結果是: window 

原因: test()是一個全局函數,也就是說是掛在window對象下的,所以 test()等價於 window.test() ,所以此時的this是window

示例2:


  1. <script>
  2.  var obj = {
  3.    say: function () {
  4.      setTimeout(function () {
  5.        console.log(this)
  6.      });
  7.    }
  8.  }
  9.  obj.say();
  10. </script>

結果是: window

匿名函數,定時器中的函數,由於沒有默認的宿主對象,所以默認this指向window

問題: 如果想要在setTimeout/setInterval中使用這個對象的this引用呢?

用一個 變量提前把正確的 this引用保存 起來, 我們通常使用that = this, 或者 _this = this來保存我們需要的this指針!


  1. <script>
  2.  var obj = {
  3.    func: function() {},
  4.    say: function () {
  5.      var that = this;   //此時的this就是obj對象
  6.      setTimeout(function () {
  7.        console.log(this)
  8.        that.func()
  9.      });
  10.    }
  11.  }
  12.  obj.say();
  13. </script>

我們也可以使用 func.bind(this) 給回調函數直接綁定宿主對象, bind綁定宿主對象後依然返回這個函數, 這是更優雅的做法

[javascript] view plain copy
  1. <span style="font-family:'Times New Roman';"><script>  
  2.   var obj = {  
  3.     func: function() {},  
  4.     say: function () {  
  5.        // 此時的this就是obj對象  
  6.       setTimeout(function () {  
  7.         console.log(this)  
  8.         this.func()  
  9.       }.bind(this));  
  10.     }  
  11.   }  
  12.   obj.say(); // obj  
  13. </script></span>  

示例3(改變自360面試題):


  1.  window.val = 1;
  2.  var obj = {
  3.    val: 2,
  4.    dbl: function () {
  5.      this.val *= 2;
  6.      val *= 2;
  7.      console.log(val);
  8.      console.log(this.val);
  9.    }
  10.  };
  11.  // 說出下面的輸出結果
  12.  obj.dbl();
  13.  var func = obj.dbl;
  14.  func();

結果是:  2   4    8   8

<1> 12行代碼調用

val變量在沒有指定對象前綴,默認從函數中找,找不到則從window中找全局變量

即 val *=2 就是 window.val *= 2

this.val默認指的是 obj.val ;因爲 dbl()第一次被obj直接調用

<2>14行代碼調用

func() 沒有任何前綴,類似於全局函數,即  window.func調用,所以

第二次調用的時候, this指的是window, val指的是window.val

第二次的結果受第一次的影響

3.在嚴格模式下的this


  1. <script>
  2.  function test() {
  3.    'use strict';
  4.    console.log(this);
  5.  }
  6.  test();
  7. </script>

結果是: undefined

4.箭頭函數中的 this


  1. <script>
  2.  var obj = {
  3.    say: function () {
  4.      setTimeout(() => {
  5.        console.log(this)
  6.      });
  7.    }
  8.  }
  9.  obj.say(); // obj
  10. </script>

此時的 this繼承自obj, 指的是定義它的對象obj, 而不是 window!

示例(多層嵌套的箭頭函數):


  1. <script>
  2. var obj = {
  3. say: function () {
  4.   var f1 = () => {
  5.     console.log(this); // obj
  6.     setTimeout(() => {
  7.       console.log(this); // obj
  8.     })
  9.   }
  10.   f1();
  11.   }
  12. }
  13. obj.say()
  14. </script>

因爲f1定義時所處的函數 中的 this是指的 obj, setTimeout中的箭頭函數this繼承自f1, 所以不管有多層嵌套,都是 obj

示例(複雜情況: 普通函數和箭頭函數混雜嵌套)


  1. <script>
  2. var obj = {
  3. say: function () {
  4.   var f1 = function () {
  5.     console.log(this); // window, f1調用時,沒有宿主對象,默認是window
  6.     setTimeout(() => {
  7.       console.log(this); // window
  8.     })
  9.   };
  10.   f1();
  11.   }
  12. }
  13. obj.say()
  14. </script>
結果: 都是 window,因爲 箭頭函數在定義的時候它所處的環境相當於是window, 所以在箭頭函數內部的this函數window

示例(嚴格模式下的混雜嵌套)


  1. <script>
  2. var obj = {
  3. say: function () {
  4.   'use strict';
  5.   var f1 = function () {
  6.   console.log(this); // undefined
  7.   setTimeout(() => {
  8.     console.log(this); // undefined
  9.   })
  10.   };
  11.   f1();
  12.  }
  13. }
  14. obj.say()
  15. </script>

結果都是undefined


關於this指向問題的討論一直是學習js不可忽視的重要部分,那些一個又一個圍繞this挖的筆試坑,彷彿永遠也填不完

[javascript] view plain copy
  1. var obj={  
  2.     fn:function(){  
  3.         console.log(this);  
  4.     }  
  5. }  
  6. obj.fn();//object  
以上這段代碼是再淺顯不過的this指向問題,也就是誰調用的函數,函數體中的this就指向誰

再看下面這段

[javascript] view plain copy
  1. var obj={  
  2.     fn:function(){  
  3.         setTimeout(function(){  
  4.             console.log(this);  
  5.         });  
  6.     }  
  7. }  
  8. obj.fn();//window  
這次this指向了最外層的window對象,爲什麼呢,還是那個道理,這次this出現在全局函數setTImeout()中的匿名函數裏,並沒有某個對象進行顯示調用,所以this指向window對象

假如我們在這裏使用箭頭函數呢

[javascript] view plain copy
  1. var obj={  
  2.     fn:function(){  
  3.         setTimeout(() => {  
  4.             console.log(this);  
  5.         });  
  6.     }  
  7. }  
  8. obj.fn();//object  
this又指向函數的宿主對象了

爲了更加清楚的對比一般函數和箭頭函數this指向的區別,我們給對象添加變量

[javascript] view plain copy
  1. var obj={  
  2.     num:3,  
  3.     fn:function(){  
  4.         setTimeout(function(){  
  5.             console.log(this.num);  
  6.         });  
  7.     }  
  8. }  
  9. obj.fn();//undefined  
  10. //............................................................  
  11. var obj1={  
  12.     num:4,  
  13.     fn:function(){  
  14.         setTimeout(() => {  
  15.             console.log(this.num);  
  16.         });  
  17.     }  
  18. }  
  19. obj1.fn();//4  
如上代碼,在沒有使用箭頭函數的情況下,this指向了window(匿名函數,沒有調用的宿主對象),而window對象並沒有num屬性(num屬性在obj中定義),而在使用箭頭函數的情況下,this的指向卻對象obj1,自然可以輸出obj1中定義的屬性num。

接下來看更復雜的情況

多層嵌套的箭頭函數

[javascript] view plain copy
  1. var obj1={  
  2.     num:4,  
  3.     fn:function(){  
  4.         var f=() => {    //object,也就是指obj1  
  5.             console.log(this);  
  6.             setTimeout(() => {  
  7.                 console.log(this);// //object,也就是指obj1  
  8.             });  
  9.         }  
  10.         f();  
  11.     }  
  12. }  
  13. obj1.fn();  
假如我們改動兩層箭頭函數的其中一處,看會出現什麼結果

[javascript] view plain copy
  1. var obj1={  
  2.     num:4,  
  3.     fn:function(){  
  4.         var f=function(){      
  5.             console.log(this); //window,因爲函數f定義後並沒有對象調用,this直接綁定到最外層的window對象  
  6.             setTimeout(() => {  
  7.                 console.log(this);//window,外層this綁定到了window,內層也相當於定義在window層(全局環境)  
  8.             });  
  9.         }  
  10.         f();  
  11.     }  
  12. }  
  13. obj1.fn();  
好,接下來改變另一處

[javascript] view plain copy
  1. var obj1={  
  2.     num:4,  
  3.     fn:function(){  
  4.         var f=() => {      
  5.             console.log(this); //object,f()定義在obj1對象中,this就指向obj1,這就是箭頭函數this指向的關鍵  
  6.             setTimeout(function() {  
  7.                 console.log(this);//window,非箭頭函數的情況下還是要看宿主對象是誰,如果沒有被對象調用,函數體中的this就綁定的window上  
  8.             });  
  9.         }  
  10.         f();  
  11.     }  
  12. }  
  13. obj1.fn();  

總結:

1.箭頭函數的this綁定看的是this所在的函數定義在哪個對象下,綁定到哪個對象則this就指向哪個對象

2.如果有對象嵌套的情況,則this綁定到最近的一層對象上

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