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

注:本文和 this(他喵的)究竟是什麼 — 理解 JavaScript 中的 this、call、apply 和 bind一塊兒食用更佳。

最近翻譯了一篇關於 this 的文章,不少人評價不錯,可是原文沒有講到箭頭函數的 this,因此我來補充一篇文章來專門講解。瀏覽器

箭頭函數是 ES6 添加的新語法,基礎知識就很少介紹了,下面咱們來說一講箭頭函數的 this 怎麼指向。bash

問題起源

在以往的函數中,this 有各類各樣的指向(隱式綁定,顯示綁定,new 綁定, window 綁定......),雖然靈活方便,但因爲不能在定義函數時而直到實際調用時才能知道 this 指向,很容易給開發者帶來諸多困擾。閉包

假如咱們有下面這段代碼(本文代碼都是在瀏覽器下運行),app

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 對象!函數

之前是怎麼解決的呢?看下面的代碼:post

1. 使用閉包ui

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. 使用顯示綁定 — bindthis

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 的能夠傳更多參數的特性spa

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

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

歡迎在下方評論交流哦!

相關文章
相關標籤/搜索