Javascript中函數綁定bind()的實例講解

函數綁定

在前面我們已經知道setTimeout()很容易就會丟失this,看下面的例子:

let user = {
  firstName: "John",
  sayHi() {
    alert(`Hello, ${this.firstName}!`);
  }
};

setTimeout(user.sayHi, 1000); // Hello, undefined!

這裡this.firstName的值為undefined,因為setTimeout()在接收user.sayHi()時與user對象是隔離的,故this就丟失瞭。它類似於一下的操作:

let f = user.sayHi;
setTimeout(f, 1000); // lost user context

因為在瀏覽器中運行,所以丟失瞭上下文對象user後,this所指向的對象就是全局對象window,故為undefined

解決方案

(1)使用包裝器

let user = {
  firstName: "John",
  sayHi() {
    alert(`Hello, ${this.firstName}!`);
  }
};

setTimeout(function() {
  user.sayHi(); // Hello, John!
}, 1000);

setTimeout的第一個參數直接使用user.sayHi(),此時setTimeout就會根據詞法環境接受user對象作為上下文對象,下面是簡寫的例子:

setTimeout(() => user.sayHi(), 1000); // Hello, John!

但是有一個問題要註意的是,如果setTimeout要調用的執行函數內容在調度前被修改,那麼setTimeout觸發的執行函數為修改過的內容,例如:

let user = {
  firstName: "John",
  sayHi() {
    alert(`Hello, ${this.firstName}!`);
  }
};

setTimeout(() => user.sayHi(), 1000);

// ...within 1 second
user = { sayHi() { alert("Another user in setTimeout!"); } };

// Another user in setTimeout?!?

為瞭結果這個問題,我們需要用bind()來綁定上下文對象

(2)bind()

Javascript的函數都內置瞭bind()方法來綁定上下文對象,它的語法如下:

// more complex syntax will be little later
let boundFunc = func.bind(context);

bind()默認返回修改過上下文對象(this=context)的新函數(boundFunc)

看下面的例子:

let user = {
  firstName: "John"
};

function func(phrase) {
  alert(phrase + ', ' + this.firstName);
}

// bind this to user
let funcUser = func.bind(user);

funcUser("Hello"); // Hello, John (argument "Hello" is passed, and this=user)

上述例子中,當我們調用funcUser(…)的時候,它會去調用func()並且修改上下文對象this=context

現在我們嘗試綁定對象方法,例如:

let user = {
  firstName: "John",
  sayHi() {
    alert(`Hello, ${this.firstName}!`);
  }
};

let sayHi = user.sayHi.bind(user); // (*)

sayHi(); // Hello, John!

setTimeout(sayHi, 1000); // Hello, John!

如果一個對象有許多的方法需要綁定上下文對象,我們可以使用bindAll()來綁定所有的方法,或者我們可以遍歷對象的所有屬性方法來綁定this,例如:

for (let key in user) {
  if (typeof user[key] == 'function') {
    user[key] = user[key].bind(user);
  }
}

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *