像數學公式一樣徹底簡單理解JavaScript函式呼叫與this 指標

文叔叔發表於2019-05-13

一直以來,大家都對函式的呼叫有很多的疑惑,特別是this指標的指向。 其實理解了核心的原理之後,就很容易理解,甚至就是套用數學公式一樣!

核心原理

讓我們看看原始的函式呼叫語句,也就是call方法

function test(param){
     console.log(this + " loves " + param);
}
複製程式碼

test.call("Warren", "you") //=> Warren loves you

可以看到,我們呼叫了test方法,並且把this指向Warren,傳入引數you。這就是javascript函式呼叫的核心概念。其他函式都可以參考這個概念。

簡單的函式呼叫

若果每次呼叫函式都用call,會變得很麻煩。Javascript允許我們直接呼叫test("you"),其實就相當於

function test(param) {
  console.log("loves " + thing);
}

// this:
test("you")

// 相當於:
test.call(window, "you");
複製程式碼

但是這個在ECMAScript的strict模式會變成:

// this:
test("you")

// 相當於:
test.call(undefined, "you");
複製程式碼

概括就是兩條公式:

fn(...args) 就相當於 fn.call(window [ES5-strict: undefined], ...args).

(function() {})() 就相當於 (function() {}).call(window [ES5-strict: undefined).

成員函式

另一種常見的函式呼叫方法就是obj.test().

var obj = {
  name: "Warren",
  test: function(params) {
    console.log(this + " loves " + params);
  }
}

// this:
obj.test("you")

// 相當於:
obj.test.call(obj, "you");
複製程式碼

可以看到this指向了obj,我們之前是單獨宣告test,再看看如果將它我們關聯給其他物件。

test: function(params) {
    console.log(this + " loves " + params);
}
var obj = {
  name: "Warren",
}
obj.test = test;

obj.test("you") 
// 仍然相當於 obj.test.call(test, "you")

test("you") // "[object DOMWindow]you"
複製程式碼

this指標根據呼叫者的變化而變化!

試下Function.prototype.bind

有時候需要根據呼叫者來改變this指向,這可以用簡單閉包來實現

var obj = {
  name: "Warren",
  test: function(params) {
    console.log(this + " loves " + params);
  }
}

var boundTest = function(params) { return obj.test.call(obj, params); }

boundTest("you");
複製程式碼

套用之前的公式 fn(...args) 就相當於 fn.call(window [ES5-strict: undefined], ...args).

boundTest("you"); => boundTest(window,"you"),但是我們在boundTest裡面已經將test的this指向了obj!

讓我們改造一下這個函式:

var bind = function(func, thisValue) {
  return function() {
    return func.apply(thisValue, arguments);
  }
}

var boundTest = bind(obj.test, obj);
boundTest(you) // "Warren loves you"
複製程式碼

要理解上面的程式碼,只需要知道2點。

  1. apply 與 call的區別只是 apply接受的引數是陣列,call接受的引數是引數列表。
  2. arguments 是像類陣列的某個函式的所有引數 bind函式返回一個陣列,每次呼叫的時候繫結this並且接受新的引數。

上面只是為了更好理解,其實es5本來就有一個bind函式

var boundTest = obj.test.bind(obj);
boundTest(you) // "Warren loves you"
複製程式碼

以上大概就是JavaScript函式呼叫與this 指標的基本原理,只要記住這兩條公式,基本就不虛了。

fn(...args) 就相當於 fn.call(window [ES5-strict: undefined], ...args).

(function() {})() 就相當於 (function() {}).call(window [ES5-strict: undefined).

相關文章