深入理解JS:執行上下文中的this(二)

forcheng發表於2020-05-27

目錄

  • 序言
  • Function.prototype.bind() 方法
  • 箭頭函式
  • 參考

1.序言

深入理解JS:執行上下文中的this(一) 中,我們主要深入分析全域性環境和函式環境中函式呼叫的 this,還留下 bind 方法以及箭頭函式的 this 尚未分析,因此我們將在這篇文章進行講解。


2.Function.prototype.bind() 方法

bind() 方法將會建立返回一個新的函式。在 bind() 被呼叫時,這個新函式的 this 將被指定為 bind() 的第一個引數,而其餘引數將作為新函式的引數,供呼叫時使用。

ES5 引入了 bind 方法來設定函式的 this 值,而不用考慮函式如何被呼叫的。

這裡給出一種 bind() 的實現演算法:

var slice = Array.prototype.slice;
Function.prototype.bind = function() {
  var thatFunc = this, thatArg = arguments[0];
  var args = slice.call(arguments, 1);
  if (typeof thatFunc !== 'function') {
    throw new TypeError('Function.prototype.bind called on incompatible ' + thatFunc);
  }
  return function(){
    var funcArgs = args.concat(slice.call(arguments))
    return thatFunc.apply(thatArg, funcArgs);
  };
};

注:上述程式碼並沒有完全按照ES5規範實現,只是作為一種實現參考,更加完善的解決方案可以參考 function-bind

但不論哪種實現,其實質上都是通過類似 Function.prototype.apply(thisArg, argArray) 來是實現指定呼叫函式 this 的。


3.箭頭函式

箭頭函式表示式的語法比函式表示式更簡潔,並且沒有自己的 this,arguments,super 或 new.target。它很適合用作匿名函式,並且不能用作建構函式(為什麼呢?詳情點選檢視)。

ES6 引入了支援 this 詞法解析的箭頭函式(它在閉合的執行環境內設定 this 的值)。

如何理解箭頭函式在閉合的執行環境內設定 this 的值?

簡單理解,箭頭函式不會建立自己的 this,它的 this 與封閉詞法環境的 this 保持一致,即如果箭頭函式在全域性環境中,那麼它的 this 就指向全域性物件,如果箭頭函式在某一個函式中,那麼它的 this 就指向所在函式的 this。


我們來看幾個示例:

(1)全域性環境

var global = this

var foo = () => { return this }

var value = 1

var bar = {
  value: 2,
  getValueByArrowFunc: () => {
    return this.value
  },
  getValue: function (){
    return this.value
  },
}

console.log(foo() === global) // true

// 箭頭函式 this => global
console.log(bar.getValueByArrowFunc()) // 1

// 普通函式 this => bar
console.log(bar.getValue()) // 2

(2)函式環境

ES6的語法:

function foo() {
  this.value = 1
  
  // 箭頭函式表示式
  var arr = () => {
    console.log(this.value)
  }
  
  arr()
}

轉化為ES5:

function foo() {
  var _this = this;

  this.value = 1;

  // 轉化為普通函式表示式
  var arr = function arr() {
    console.log(_this.value);
  };

  arr();
}

對比轉化前後的程式碼可以看出:箭頭函式會轉化為一個普通函式,通過一個臨時變數 _this 來傳遞,它之前的 this 就是所在函式的 this


(3)call() 、 apply() 或 bind() 方法

var global = this

var foo = {
	bar: () => {
    return this
  }
}

var obj = { value : 1 }

console.log(foo.bar() === global) // true
console.log(foo.bar.call(obj) === global) // true
console.log(foo.bar.apply(obj) === global) // true

var bind = foo.bar.bind(obj)
console.log(bind() === global) // true

由於箭頭函式不會建立自己的 this,那麼通過 call() 、 apply() 或 bind() 方法呼叫一個箭頭函式時,只能傳遞引數,第一個引數會被忽略。


4.參考

this 關鍵字 - JavaScript | MDN - Mozilla

Function.prototype.bind() - JavaScript - MDN - Mozilla

箭頭函式- JavaScript | MDN

ECMAScript5.1中文版

ES6 - Arrow functions

相關文章