前回、TypeScriptとthisのおさらい という記事で TypeScriptでラムダ式を使った場合の this の留意点について簡単におさらいしました。
実際の開発現場では、色々なライブラリと一緒に使った場合に考慮する事があるでしょう。
弊社が経験した TypeScript と組み合わせたライブラリ( jQuery、Knockoutjs )での考慮点を、簡単なサンプルとともに紹介していきたいと思います。
jQuery
TypeScriptではラムダ式を使うと、短く書ける、thisを簡単に束縛できる。と言ったメリットがあるものの、jQueryのAPI中に渡すコールバックに使う場合は注意が必要です。(TypeScript Quick Guide # 9. this キーワード に記載されているパターンを御覧ください。)jQuery はAPI呼び出し時に、各DOMエレメントがthisになるようにapplyによってコールバック関数を呼び出しています。それにより、各要素の操作を簡単に行う事が出来るよう考慮されています。
この仕様を無視して、ラムダ式を使うと本来ならアクセスできるはずの要素に対して操作が出来ない。と言った状況に見舞われてしまいます。
確認していきましょう。以下のコードと、サンプルを御覧ください
デモを御覧ください。要素クリックすると、背景/ボーダーのスタイルが変わる簡単なスクリプトですが、背景の方がうまく適用されないのがわかると思います。
これはthisがラムダ式により置き換わっているせいで、TypeScript上からDOM要素にアクセスすることができなくなっている為です。こういう場合は、thisをthatとして取り置くといった従来のJavaScriptで行っていたようなClosureを使ったパターンか、もしくは event.currentTarget を使うといった解決方法があるでしょう。
引数の型を宣言しておける分、currentTargetを使う方がTypeScriptにマッチした使い方かもしれません。
もちろん、$.each などjQuery側のthisを使わなくても良いAPIの場合はラムダ式を使うほうが便利です。
上記だけだとWindowを参照するだけですが、classメソッド内などであれば有効に働く事と思います。
Knockoutjs
jQueryではラムダを使う場合に注意した方が良いと言ったパターンを紹介しましたが、積極的に使った方が良いというパターンとして Knockoutjs でのパターンを紹介します。Knockoutjs では、html側のbindingの状況によってthisが変わります。withとforeachを使った場合です。通常ViewModel側の関心事にViewの都合を出来るだけ持ち込みたくないのですが、呼び出し側の事を考えておかないといけない場合があるということになります。
以下のコードを御覧ください。
人の名前と年齢を保持するレコードの追加・編集・削除を行う簡単なサンプルになります。
一見すると問題無いコードに見えるかと思います。しかし、このサンプルコードは思ったように動かないのです。
デモ(後述のラムダ式版も含まれています)で、確認してください。
Knockoutjsでは、context.$dataがwithとforeachを使った時に切り替わります。そして、context.$dataがthisになるようにapplyによってコールバック関数を呼び出しています。jQueryと同じく、各要素(現在のコンテキスト)への操作を簡単に行う事ができるよう考慮されているようです。
これは操作がオブジェクトに紐付いている場合(例:toggleEdit)は有効に働きますが、他のオブジェクトから操作したい場合には一手間必要になります。
ただjQueryと異なる点として、引数の1つ目(arguments[0])にもcontext.$data を渡すように作られています(2015/02/05現在)。このパターンではラムダ式を利用することで簡潔になるでしょう。
解決したコードは以下になります。
挙動はさきほどのデモで確認してください。
まとめ
ライブラリによってラムダ式を使っていいか、悪いか、どういったインターフェイスか、等を適宜考える必要があることがわかると思います。この問題はTypeScriptのラムダ式に起因しているというより、ライブラリの仕様による所が大きいです。ラムダ式に関わらず、こういった仕様(機能)は有効に使える所、使い方を見極めながら使っていく必要があるというサンプルの紹介でした。Author
Yutaka Hirayama