Backbone.jsとCoffeeScriptをお使いのみなさん、こんにちは。
イベントのcallbackに登録するメソッドをfat arrowで定義してませんか?
Backbone.Events#on
の3番目の引数context
を使いましょう。
メソッドをfat arrowで定義するとmixin出来ない問題についてもちょこっと触れます。
例
- イベント通知のために、グローバルな
PubSub
が定義されています。 -
NyaModel#initialize
で、NyaModel#sayMessage
メソッドをPubSub
に登録しています。 -
PubSub
から(」・ω・)」うー!
イベントを受け取ると、NyaModel#sayMessage
が呼ばれます。 -
NyaModel#sayMessage
は、NyaModel
のmessage
属性をコンソールに出力します。
今までの僕のfat arrowなやりかた
PubSub
のcallbackに渡すメソッドの中でNyaModel
の属性を使いたい。
だがしかしPubSub
のcallback関数中ではthis
がPubSub
になる。
this
をNyaModel
に固定するため、メソッドをfat arrow(=>
)で定義してた。
window.PubSub = _.clone(Backbone.Events)
class NyaModel extends Backbone.Model
defaults:
message: '(/・ω・)/にゃー!'
initialize: ->
PubSub.on '(」・ω・)」うー!', @sayMessage
# thisがNyaModelにbindされるようfat arrowで定義
sayMessage: =>
console.log @get('message')
PubSub.on '(」・ω・)」うー!', ->
console.log '(」・ω・)」うー!'
new NyaModel
PubSub.trigger '(」・ω・)」うー!'
PubSub.trigger '(」・ω・)」うー!'
new NyaModel message: "Let's\(・ω・)/にゃー!"
PubSub.trigger '(」・ω・)」うー!'
# (」・ω・)」うー!
# (/・ω・)/にゃー!
# (」・ω・)」うー!
# (/・ω・)/にゃー!
# (」・ω・)」うー!
# (/・ω・)/にゃー!
# Let's\(・ω・)/にゃー!
contextを使ったfat arrowじゃないやりかた
Backbone.Events#on
の第3引数でcontextを与えると、callback関数中のthis
がcontextになります。
これでfat arrowを使う理由がなくなった。
window.PubSub = _.clone(Backbone.Events)
class NyaModel extends Backbone.Model
defaults:
message: '(/・ω・)/にゃー!'
initialize: ->
# contextとして@を渡してあげる
PubSub.on '(」・ω・)」うー!', @sayMessage, @
# context渡すとthisがNyaModelになるのでfatじゃなくておk
sayMessage: ->
console.log @get('message')
PubSub.on '(」・ω・)」うー!', ->
console.log '(」・ω・)」うー!'
new NyaModel
PubSub.trigger '(」・ω・)」うー!'
PubSub.trigger '(」・ω・)」うー!'
new NyaModel message: "Let's\(・ω・)/にゃー!"
PubSub.trigger '(」・ω・)」うー!'
# (」・ω・)」うー!
# (/・ω・)/にゃー!
# (」・ω・)」うー!
# (/・ω・)/にゃー!
# (」・ω・)」うー!
# (/・ω・)/にゃー!
# Let's\(・ω・)/にゃー!
fat arrowのままでもいいじゃないの?
確かに今までの例だとその通りです。
しかしfat arrowのままだとmixinなどでsayMessage
を入れ替えたいときに問題が生じます。
fat arrowはmixinできない
fat arrowなmethodをmixinするとき、同じようにfat arrowを使ってこう書くのは間違いです
_.extend NyaModel::
sayMessage: =>
console.log "(」・ω・)」うー! #{@get('message')}"
これは次のようなJavaScriptに展開されます。
var _this = this;
_.extend(NyaModel.prototype, {
sayMessage: function() {
return console.log("(」・ω・)」うー! " + (_this.get('message')));
}
});
```
`this`がトップレベル、ブラウザだと`window`オブジェクトにbindされてしまいます。
しかしfat arrowをやめると、`(」・ω・)」うー!`イベントで呼び出された際に、`this`が`PubSub`になってしまうのでエラーが出ます。
``` nyaru.coffee
window.PubSub = _.clone(Backbone.Events)
class NyaModel extends Backbone.Model
defaults:
message: '(/・ω・)/にゃー!'
initialize: ->
PubSub.on '(」・ω・)」うー!', @sayMessage
sayMessage: ->
console.log @get('message')
_.extend NyaModel::
sayMessage: ->
console.log "(」・ω・)」うー! #{@get('message')}"
new NyaModel
PubSub.trigger '(」・ω・)」うー!'
# thisがPubSubにbindされちゃう><
# => TypeError: Object #<Object> has no method 'get'
```
## contextがあればmixinも怖くない
``` nyaru.coffee
window.PubSub = _.clone(Backbone.Events)
class NyaModel extends Backbone.Model
defaults:
message: '(/・ω・)/にゃー!'
initialize: ->
PubSub.on '(」・ω・)」うー!', @sayMessage, @
sayMessage: ->
console.log @get('message')
_.extend NyaModel::
# contextのおかげでthisがmodelになってる
sayMessage: ->
console.log "(」・ω・)」うー! #{@get('message')}"
new NyaModel
PubSub.trigger '(」・ω・)」うー!'
PubSub.trigger '(」・ω・)」うー!'
new NyaModel message: "Let's\(・ω・)/にゃー!"
PubSub.trigger '(」・ω・)」うー!'
# (」・ω・)」うー! (/・ω・)/にゃー!
# (」・ω・)」うー! (/・ω・)/にゃー!
# (」・ω・)」うー! (/・ω・)/にゃー!
# (」・ω・)」うー! Let's\(・ω・)/にゃー!
```
## まとめ
- `Backbone.events#on`の3番目の引数`context`を渡すと、callback関数中の`this`が`context`にbindされる。
- イベントのcallbackに渡すメソッド中の`this`を固定するためにfat arrow(`=>`)を使う必要はありません。`context`を使いましょう。
- メソッドをfat arrowで定義すると、mixinできません。
- 正確にはできるけど`this`をbindしなおす必要があって面倒です。