Hatena::ブログ(Diary)

Islands in the byte stream

2012-02-23

JavaScriptのthisの扱いが難しすぎる件

[再々追記]

(o.f = o.f)()の結果は右辺値・左辺値というルールで説明できるようです。コメント欄参照のこと。

[/再々追記]

[再追記]

これの解釈はどうすれば…。

[/再追記]

[追記]

([o.f][0])() -> o.f は ([o.f, "a"][0])() とすると分かりやすいが、単にレシーバがArrayになっているだけらしい。

また (o.f)()と(tmp = o.f)の違いはブコメで指摘されているとおり、 o.f が (C++的な意味での)参照を返すと考えると理解できる。

[/追記]

だれかこの現象を説明できる人はいませんか。私には難しすぎます。

In node:

#!/usr/bin/env node
var o = {}, tmp;

o.f = function() { console.log(this.toString()) };

o.toString = function() { return "o" };
o.f.toString = function() { return "o.f" };
(function(){ return this })().toString = function() {
    return "global";
};


o.f(); // o
(o.f)(); // o

([o.f][0])(); // o.f
(new Array(o.f)[0])(); // o.f
([tmp  = o.f][0])(); // o.f

(tmp = o.f)(); // global
(function(){ return o.f })()(); // global

({x: o.f, toString: function(){ return "t" }}.x)(); // t

kazuhookukazuhooku 2012/02/24 11:41 仕様に頼らずに平易に説明すると、o.f は左辺値で、これは「o というオブジェクトの f というプロパティ」という意味です。

これに対して、(o.f = o.f) の結果は = 演算の値ですが、これは右辺値なので、「o.f の値」です。

JavaScript の関数呼び出しはレシーバ (左辺値を保持するオブジェクト) を this とするので、前者を関数呼び出しした場合は this が o を指します。後者を保持するオブジェクトはないので (なぜなら左辺値じゃないから)、this はグローバル (もしくは strict モードの場合は null だっけ) を指します。

左辺値として、(オブジェクト, プロパティ名) の組をもつという設計は LL だと普通だと思います。たとえば Perl の TIEHASH をみると、セッターがオブジェクトとプロパティ名を受け取るようになっていますが、これは Perl の内部構造として、左辺値が JavaScript 同様の実装になっていることの傍証ではないでしょうか。少なくとも、JavaScript の設計が特殊ではないということは理解できると思います。

gfxgfx 2012/02/24 11:52 解説ありがとうございます。

代入文(a = b)が左辺値を返すか右辺値を返すかは言語の(あるいはoperator=の実装者の)「決め」だと思いますが、JavaScriptの場合は右辺値だということですね。

そう考えると(o.f = o.f)() (-> global)、(1, o.f)() (-> global)、(o.f)() (->o) のそれぞれの結果は納得できます。

これで全ての疑問が解決できました!

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証