JavaScriptヘッダ定義

広告

2016年1月3日日曜日

JavaScriptのthisの話

私はメインの仕事がプログラミングのいわゆるソフトウェアエンジニアではないので、コアの業務ではコードを書くより読むほうが多いのですが、自動化や解析用のプログラムを書くことを推奨してる会社なので、空き時間によくいろいろツールを作っています。フリーランスで働いていた時や、修論(arXiv1, arXiv2)もプログラムはバリバリ書いていたので、それなりにコードは書けます。
一般的な言語はたいてい触れたことがありますが、その中でも得意なのがJavaScript, Python, PHP, C#あたりです。今日はJavaScriptでハマる this の問題について書いていこうと思います。


バリデータのようなもの

フィールドのIDと正規表現をオブジェクトとして管理してバリデータを実装してみましょう。以下のコードは動くか試していませんので、雰囲気だけ味わってください。

var Validator = function(pattern, id) {
  this.pattern = pattern;
  this.id = id;
};
Validator.prototype.validate = function() {
  if (!this.pattern.test(document.getElementById(this.id).value) {
    alert('validation error!!');
  }
};

var v = new Validator(/[0-9]+/, "age");

これでバリデータオブジェクトvができたので、

v.validate();

とすれば、ID#age の内容が数字でない場合にバリデーションエラーを表示するようになります。
では、このバリデーションを submit ボタンが押された時に実行するにはどうしたら良いでしょうか。まず単純にボタンのクリックイベントにvalidateメソッドを設定することを考えます。

document.getElementById("submit").onclick = v.validate;

しかし、これだとvalidate内でthis.patternやthis.idが見つからないとエラーが発生します。v.validateが実行されている時には、thisはv自身ではなくdocument.getElementById("submit")になってしまうためです。これを回避するための方法はいくつかありますが、最も簡単なのは一度匿名関数を挟む方法です。

document.getElementById("submit").onclick = function() { v.validate(); };

v.validate() の形で呼べば、validate内ではthisはvになるので、問題なく動きます。もう一つシンプルなのはbindメソッドを使う方法です。


document.getElementById("submit").onclick = v.validate.bind(v);

bindメソッドを関数に対して使うと、その関数がどのように切り離されて実行されてもthisの値を固定することができます。

結局どうするのがいいか

よく、thisを使うと上記のような問題が発生するという理由で、thisをthatに代入して使ったり、名前付きのオブジェクトであればオブジェクト名をいちいち書いたり、といったことが行われていますが、正直エレガントではありません。個人的には一番短くて済むbindをよく使います。オブジェクトのメソッドを代入するときには必ずbindをつけて、そのオブジェクトのメソッドとして実行されるように指定しておく癖をつけると、変なエラーを起こさなくていいですね。

クイズ

a.execute();

var b = a.execute;
b();
は挙動が異なる場合があります。挙動が異なるサンプルを実装してください。

0 件のコメント :

コメントを投稿

広告