`that = this` について思ったこと

thatに思いを馳せる

JavaScriptにおいて that = this とか self = this なパターンを頻繁に使うと、作業者の理性が保証されない場合に下記に示す2点の問題が起こりう得ると思っている。

「あー、どうなのかなー、うーん」と思いながら書いてみる。

1.メソッド分割が適切におこなわれない雰囲気

ちょっと極端かも知れないが、Backboneっぽいコードを例にしてみる。

initialize: function() {
  var that = this;

  this.listenTo(this.entity, 'success', function() {
    var bar = that.foo();
    that.$el.find('.qux').text(bar);
    // long.
    // long..
    // logic...
  });
  this.entity.execute();
}

いわゆるthat = thisをするって、こういうことだと思う。練度が低い or 理性を失ったメンバーがこれを扱うのはマズい空気がある。thatにまつわるロジックが長々と拡張されたり、色々な変数を2つのスコープでいっぱい使いたいからモリモリ作ってまたがせ始めたりとか危うい。

initialize: function() {
  this.listenTo(this.entity, 'success', _.bind(this.onSuccess, this));
  this.entity.execute();
},
onSuccess: function() {
  var bar = that.foo();
  this.$el.find('.qux').text(bar);
  // long.
  // long..
  // logic...
}

このように分割しておいたほうがよいと思うのです。個々に_.bind()するのが面倒であれば、_.bindAll()だってあるし。

2. スコープ広がりすぎでリファレンスカウントこわい

いわゆるレキシカルスコープ的な特性の話になるのだけど

initialize: function() {
  var that = this;

  this.listenTo(this.entity, 'success', function() {
    var bar = that.foo();
    that.$el.find('.qux').text(bar);
    // (ry
  });
  this.entity.execute();
}

たとえばこれが

initialize: function() {
  var that = this,
      bigObject = this.getFatObject(); // ** BIG! SO BIG!! **

  this.listenTo(this.entity, 'success', function() {
    var bar = that.foo();
    that.$el.find('.qux').text(bar);
    // (ry
  });
  this.entity.execute();
}

とかbigObjectを呼んで

initialize: function() {
  var that = this,
      bigObject = this.getFatObject(); // ** NOT COLLECTED BY GC **

  this.listenTo(this.entity, 'success', function() {
    var bar = that.foo();
    that.$el.find('.qux').text(bar);
    // (ry
  });

  $('#outer-element').on('click', function() {
    var hoge = that.getHoge();
    $(this).val(bigObject.prop);
    // (ry
  });

  this.entity.execute();
}

とか迂闊なものを加えられると、DOMイベントのリスナとして登録されたクロージャが、bigObjectのリファレンスカウントを抱えたまま失踪する珍事になりかねないと怖がっている。それこそdelegateEvents()を使えという話だが。

listenTo()も同様に、オブジェクトを使い終わったらremove()なりstopListening()なりしないと同じような話になるだろう。

あ・・・

全体的に、コードレビュー徹底しやがれという話なのですが、やはりあまり良い習慣とは思えないのでした。とはいえ、実は自分自身bindの利用歴がさほど長くないので、bindの副作用とかアンチパターンも思いつかず。どうなのかな〜と。

ここまで書いて、[].map()とか_.find()みたいなときに渡すthat = thisもあるよなーと気づいた。まあでも似たような話とは思うし、そもそもcontextないしthisObjectを渡せるな彼らは。