2014
05
23
05
23
JavaScript: メソッド?とthisの振る舞いと
以前にもこのブログで書いたが、JavaScriptでオブジェクトのメソッドを使おうとすると、thisがおかしな参照をしてないかと思わされるケースがある。このthisの振る舞いからオブジェクトのメソッドについて考えることを、Pythonと比較しながら改めてまとめる。
数年前からCoffeeScriptというのを見かけるようになって、どういう経過をたどるかと気にしていたら、VisualStudioに導入されているしGithubでは「新しいjsファイルを作るな。CoffeeScript書け」とスタイルガイドに書かれているし。CoffeeScriptがありがたがられる一つの理由にJavaScriptでのthisの振る舞いがある。
JavaScriptでたとえばオブジェクトを一つ作って、そのメソッドをsetTimeoutから呼び出してみる。
window.setTimeout(obj.method, 10);
そうするとメソッド内で使われているthisはwindowオブジェクトを参照しており、慣れない人や知らない人は戸惑う。なぜこうなるかを考えてみると、JavaScriptではメソッドがあるというより、ただオブジェクトのスロットに関数への参照が入っているんだと考えたほうが妥当に思う。そしてthisは関数が呼ばれたコンテキストに従って参照先を選ぶ。それをコードで検証してみる。
オブジェクトを定義して、そのメソッドを任意の変数に渡してから実行してみる。
上記のコードの実行結果として一行目に3、二行目にundefinedが表示される(関数printは別途定義)。メソッドを変数に渡してそこから実行すると、thisが元のオブジェクトを参照できていない。
http://jsfiddle.net/hiroaki/57mCn/2/
同様のことをPythonでやってみる。
実行結果は先のJavaScriptと違い、一行目も二行目も3が表示される。メソッドが元のオブジェクトを参照できているということ。
逆をやってみる。ただの関数を定義しておき、それをオブジェクトに渡して、オブジェクトからその関数を実行する。
これを実行すると3が表示される。関数としてよそで定義さていたものをオブジェクトに渡すことで、メソッドとして利用できた。
http://jsfiddle.net/hiroaki/4xprd/2/
ここまでに書いたコードの動作から、JavaScriptでのメソッドというのは他にあるPythonやC#などのようなクラスベースの言語に見られるメソッドと異なるもので、どんな場合でも自分が所属するオブジェクトを参照するものではない。ただの関数と捉えたほうがしっくりくる。そしてthisは元のオブジェクトでなく、実行されたコンテキストによって参照先を変える。thisはa.f()が実行されればaを、b.f()が実行されればbを、変数cに渡されたメソッドをc()として実行すればwindowオブジェクトが参照される。
数年前からCoffeeScriptというのを見かけるようになって、どういう経過をたどるかと気にしていたら、VisualStudioに導入されているしGithubでは「新しいjsファイルを作るな。CoffeeScript書け」とスタイルガイドに書かれているし。CoffeeScriptがありがたがられる一つの理由にJavaScriptでのthisの振る舞いがある。
JavaScriptでたとえばオブジェクトを一つ作って、そのメソッドをsetTimeoutから呼び出してみる。
window.setTimeout(obj.method, 10);
そうするとメソッド内で使われているthisはwindowオブジェクトを参照しており、慣れない人や知らない人は戸惑う。なぜこうなるかを考えてみると、JavaScriptではメソッドがあるというより、ただオブジェクトのスロットに関数への参照が入っているんだと考えたほうが妥当に思う。そしてthisは関数が呼ばれたコンテキストに従って参照先を選ぶ。それをコードで検証してみる。
オブジェクトを定義して、そのメソッドを任意の変数に渡してから実行してみる。
var foo =
{
x:3,
y:5,
print_x:function() {print(this.x);}
};
foo.print_x();
var a = foo.print_x;
a();
上記のコードの実行結果として一行目に3、二行目にundefinedが表示される(関数printは別途定義)。メソッドを変数に渡してそこから実行すると、thisが元のオブジェクトを参照できていない。
http://jsfiddle.net/hiroaki/57mCn/2/
同様のことをPythonでやってみる。
class Foo:
x = 3
def print_x(self):
print self.x
foo = Foo()
foo.print_x()
a = foo.print_x
a()
実行結果は先のJavaScriptと違い、一行目も二行目も3が表示される。メソッドが元のオブジェクトを参照できているということ。
逆をやってみる。ただの関数を定義しておき、それをオブジェクトに渡して、オブジェクトからその関数を実行する。
var foo =
{
x:3,
y:5
};
function notMethod(str)
{
print(this.x);
}
foo.print_x = notMethod;
foo.print_x();
これを実行すると3が表示される。関数としてよそで定義さていたものをオブジェクトに渡すことで、メソッドとして利用できた。
http://jsfiddle.net/hiroaki/4xprd/2/
ここまでに書いたコードの動作から、JavaScriptでのメソッドというのは他にあるPythonやC#などのようなクラスベースの言語に見られるメソッドと異なるもので、どんな場合でも自分が所属するオブジェクトを参照するものではない。ただの関数と捉えたほうがしっくりくる。そしてthisは元のオブジェクトでなく、実行されたコンテキストによって参照先を変える。thisはa.f()が実行されればaを、b.f()が実行されればbを、変数cに渡されたメソッドをc()として実行すればwindowオブジェクトが参照される。
スポンサーサイト