1. 程式人生 > >javascript開發:ES5與ES6的“this”深入分析

javascript開發:ES5與ES6的“this”深入分析

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

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

一針見血式總結:

普通函式中的this:

1. this總是代表它的直接呼叫者, 例如 obj.func ,那麼func中的this就是obj

2.在預設情況(非嚴格模式下,未使用 'use strict'),沒找到直接呼叫者,則this指的是 window

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

4.使用call,apply,bind(ES5新增)繫結的,this指的是 繫結的物件

箭頭函式中的this

預設指向在定義它時,它所處的物件(宿主物件),而不是執行時的物件, 定義它的時候,可能環境是window

下面通過一些例子來研究一下 this的一些使用場景[ 使用最新版 chrome測試 ]

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

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

最後直到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繫結宿主物件後依然返回這個函式, 這是更優雅的做法

  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,而不是 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, 所以不管有多層巢狀,都是 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是undefined!!!所以箭頭函式中的也是undefined!