一般的な言語はたいてい触れたことがありますが、その中でも得意なのがJavaScript, Python, PHP, C#あたりです。今日はJavaScriptでハマる this の問題について書いていこうと思います。
バリデータのようなもの
フィールドのIDと正規表現をオブジェクトとして管理してバリデータを実装してみましょう。以下のコードは動くか試していませんので、雰囲気だけ味わってください。
これでバリデータオブジェクトvができたので、
とすれば、ID#age の内容が数字でない場合にバリデーションエラーを表示するようになります。
では、このバリデーションを submit ボタンが押された時に実行するにはどうしたら良いでしょうか。まず単純にボタンのクリックイベントにvalidateメソッドを設定することを考えます。
しかし、これだとvalidate内でthis.patternやthis.idが見つからないとエラーが発生します。v.validateが実行されている時には、thisはv自身ではなくdocument.getElementById("submit")になってしまうためです。これを回避するための方法はいくつかありますが、最も簡単なのは一度匿名関数を挟む方法です。
v.validate() の形で呼べば、validate内ではthisはvになるので、問題なく動きます。もう一つシンプルなのはbindメソッドを使う方法です。
bindメソッドを関数に対して使うと、その関数がどのように切り離されて実行されてもthisの値を固定することができます。
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 件のコメント :
コメントを投稿