也談箭頭函式的 this 指向問題及相關

子非發表於2018-09-20

也談箭頭函式的 this 指向問題及相關
注:本文和 this(他喵的)到底是什麼 — 理解 JavaScript 中的 this、call、apply 和 bind一起食用更佳。

最近翻譯了一篇關於 this 的文章,很多人評價不錯,但是原文沒有講到箭頭函式的 this,所以我來補充一篇文章來專門講解。

箭頭函式是 ES6 新增的新語法,基礎知識就不多介紹了,下面我們來講一講箭頭函式的 this 怎麼指向。

問題起源

在以往的函式中,this 有各種各樣的指向(隱式繫結,顯示繫結,new 繫結, window 繫結......),雖然靈活方便,但由於不能在定義函式時而直到實際呼叫時才能知道 this 指向,很容易給開發者帶來諸多困擾。

假如我們有下面這段程式碼(本文程式碼都是在瀏覽器下執行),

function User() {
  this.name = 'John';

  setTimeout(function greet() {
    console.log(`Hello, my name is ${this.name}`); // Hello, my name is 
    console.log(this); // window
  }, 1000);
}

const user = new User();
複製程式碼

greet 裡的 this 可以由上一篇文章的四個規則判斷出來。對,因為沒有顯示繫結、隱式繫結或 new 繫結、所以直接得出結論 this 指向 window。但實際上我們想把 this 指向 user 物件!

以前是怎麼解決的呢?看下面的程式碼:

1. 使用閉包

function User() {
  const self = this;
  this.name = 'John';

  setTimeout(function greet() {
    console.log(`Hello, my name is ${self.name}`); // Hello, my name is John
    console.log(self); // User {name: "John"}
  }, 1000);
}

const user = new User();
複製程式碼

2. 使用顯示繫結 — bind

function User() {
  this.name = 'John';

  setTimeout(function greet() {
    console.log(`Hello, my name is ${this.name}`); // Hello, my name is John
    console.log(this); // User {name: "John"}
  }.bind(this)(), 1000);
}

const user = new User();
複製程式碼

3. 利用 setTimeout 的可以傳更多引數的特性

其實第三種和第一種比較像,都用到了閉包。

function User() {
  this.name = 'John';

  setTimeout(function greet(self) {
    console.log(`Hello, my name is ${self.name}`); // Hello, my name is John
    console.log(self); // User {name: "John"}
  }, 1000, this);
}

const user = new User();
複製程式碼

三種方法都可以解決問題,但是都要額外寫冗餘的程式碼來指定 this

現在,箭頭函式(Arrow Function)正是 ES6 引入來解決這個問題的,它可以輕鬆地讓 greet 函式保持 this 指向 user 物件。

箭頭函式如何解決

下面是箭頭函式版本:

function User() {
  this.name = 'John';

  setTimeout(() => {
    console.log(`Hello, my name is ${this.name}`); // Hello, my name is John
    console.log(this); // User {name: "John"}
  }, 1000);
}

const user = new User();
複製程式碼

完美,直接把普通函式改成箭頭函式就能解決問題。

箭頭函式在自己的作用域內不繫結 this,即沒有自己的 this,如果要使用 this ,就會指向定義時所在的作用域的 this。在上面的程式碼中即指向 User 函式的 this,而 User 函式通過 new 繫結,所以 this 實際指向 user 物件。

如果上述程式碼在嚴格模式下執行會有影響嗎?

function User() {
  this.name = 'John';

  setTimeout(() => {
    'use strict'
    console.log(`Hello, my name is ${this.name}`); // Hello, my name is John
    console.log(this); // User {name: "John"}
  }, 1000);
}

const user = new User();
複製程式碼

答案是沒有影響。因為箭頭函式沒有自己的 this,它的 this 來自於 Userthis只要 Userthis 不變,箭頭函式的 this 也保持不變

那麼使用 bindcall 或者 apply 呢?

function User() {
  this.name = 'John';

  setTimeout((() => {
    console.log(`Hello, my name is ${this.name}`); // Hello, my name is John
    console.log(this); // User {name: "John"}
  }).bind('no body'), 1000);
}

const user = new User();
複製程式碼

答案還是沒有影響。因為箭頭函式沒有自己的 this,使用 bindcall 或者 apply 時,箭頭函式會自動忽略掉 bind 的第一個引數,即 thisArg

總結:箭頭函式在自己的作用域內沒有自己的 this,如果要使用 this ,就會指向定義時所在的作用域的 this

歡迎在下方評論交流哦!

相關文章