1. 程式人生 > >Javascript學習---函式繫結bind()

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);
  }
}