Object.observeに未来を感じたので、es5でもちょっとだけ動くようにしてみた

互換っぽいのを作った話。

今日なんとなくAngular.jsを触ってて、「いや、俺が欲しかったのはこれじゃないんだ!」という感がムンムンだったので、前々から気になっていたObject.Observeを試してみた。

詳細は次のエントリが詳しい。

次世代JavaScriptでデータバインディング: Object.observe() を試す - ぼちぼち日記 | Postolog http://postolog.com/bbbkL2

哲学の話

Object.observeのやってることはBackbone.Modelと変わらないんだが、Backbone.ModelはBackboneのデータ構造であることをユーザーに強いてくる。どのモデルか意識せずに、オブジェクトの変化をViewから監視したい。

MVCのMの部分、JSのやるべき仕事はデータモデリングがメインになると思っていて、とくに明記せずともデータ構造を持つオブジェクトであると捉える必要がある。

ES5でも使いたくなった

つくった
mizchi/es5observe · GitHub https://github.com/mizchi/es5observe

イテラブルなオブジェクトを誤魔化すために、Object.definePropertyの嵐です。
詳細はREADME。しかし適当に作りすぎたせいで全然仕様を満たせておらず、名前空間を汚す副作用も残っている。誰かPullReqください。
リークしてたらすいませんって感じなんだけど、きたるべき未来の為に updated だけでも動く環境を作りたかった。

なにができるか

JSのオブジェクトをモデルとして、Viewの実装を次のように行える。
上のコードを読み込んだ状態か、Chrome Dev版でObject.observeが実装されてる版なら、このコードはちゃんと動く。

"""
HTML

<p id='status'>
  <span class='hp'></span>
  <span class='x'></span>
  <span class='y'></span>
</p>
"""

window.onload = ->
  player_status = hp: 10, x: 3, y: 0
  renderer = new StatusView $('#status'), player_status
  
class View
  binding: {}
  hook: {}

  constructor: (@$el, @model) ->
    Object.observe @model, @_handler
    @_initialize()

  _initialize: ->
    for key, val of @model
      if typeof val in ['string', 'number']
        @_update_value(key)

  _handler: (events) =>
    for e in events when e.type in ['updated', 'new']
      @_update_value e.name
      for method_name, values of @hook
        for v in values when v is e.name
          @[method_name](events)
          break

  _update_value: (value_name) ->
    selector = @binding[value_name] or '.'+value_name
    @$el.find(selector).text @model[value_name]

class StatusView extends View
  binding:
    hp: '.hp' # "default binding is '.' prefix"

  hook:
    render: ['x', 'y']

  render: (events) =>
    console.log 'called by hook', events

とにかくJSの仕事をデータモデリングにだけにすると色々と幸せになるはずなんです!