綁定物件的方法

javascript 中一個設計是,物件的方法是不綁定的, 當要傳遞物件的方法時,會失去對物件的綁定, 只會傳入一個半完成沒有 this 的方法。 除了在傳遞方法時手動用 bind 綁定, 也能在建物實例時,手動綁定。 或把 bind 的過程隱藏在 getter 中,來做存取時綁定, 或直接為物件新增一個 bind 方法。

物件綁定方法或方法綁定物件

javascript 的函數有 bind 方法, 可以把一個函數綁到某一個物件上。 一般會在要把物件方法當回呼函數傳遞時, 把方法 bind 到物件上。

// 只傳了 remove 函數,沒有傳入 button
button.addEventListener('click', button.remove)

// bind 一個方法寫起來很長
button.addEventListener('click', button.remove.bind(button))

但這樣 bind 寫起來很長不太方便, 於是可以為物件新增一個方法, 可以返回該物件上綁好的方法。 從原本把函數綁到物件上, 變成把已經屬於物件的方法再綁定。

Object.prototype.bindMethod = function (method) {
  if (typeof method == 'function') return method.bind(this)
  else return this[method].bind(this)
}
button.addEventListener('click', button.bindMethod('remove'))

這樣寫起來就比較短。 而且還玩了一個小把戲, 如果傳入的是函數,就直接綁定。 可以選擇傳入屬性名或直接傳入方法。

用 getter 返回綁定的方法

物件的方法也可以是一個返回函數的 getter, 如果在每次 get 時都把方法先綁定再返回, 就可以達到效果。

const object = {
  name: 'obj',
  _say(sentence) {
    console.log(`${this.name}: ${sentence}`)
  },
  get say() {
    return this._say.bind(this)

    // 或直接用上面定義的 bindMethod 方法
    // return this.bindMethod('_say')
  }
}

這裡是在要在存取時綁定, 就是不用傳遞回呼函數時綁定, 變成每次存取時都回傳一個已經綁定好的函數。

// 存取 say 屬性時,就直接返回一個綁定了的函數
button.addEventListener('click', object.say)

// 不用再手動綁定
// button.addEventListener('click', object._say.bind(object))

如果用 class 定義成物件原型的 getter, 就可以每個實例都繼承到 getter, 每個實例在存取 getter 時都是 bind 到該實例上。 而不用在構造成實例時,手動把方法綁定。

class Obj {
  constructor(name) {
    this.name = name

    // 在構造時就把所有方法都綁定
    this.say = this.say.bind(this)
    this.bye = this.bye.bind(this)
  }
  say(sentence) {
    console.log(`${this.name}: ${sentence}`)
  }
  bye() {
    this.say('bye')
  }
}

getter 返回未綁定的函數

另外一點蠻有趣,如果 getter 是返回一個函數, 那該函數的 this 也是指向 getter 的所屬物件。

function showThisName() {
  console.log(this.name)
}
const object = {
  get getShowThisName() {
    return showThisName
  },
  name: 'obj'
}

object.getShowThisName == showThisName
object.getShowThisName() // obj

這點還沒有想到什麼應用,只是覺得很有趣而已。