まず、関数を使ってオブジェクトを生成する典型的なサンプルを以下に示します。
function Klass(prop) { this.prop = prop; this.func = function() { return 'prop is ' + this.prop; }; } obj = new Klass('abc'); alert(obj.func()); // alerts 'prop is abc' for (i in obj) { alert(i); // alerts 'prop', 'func' (順不同) }
で、気になったのが「関数の中のthisが何でオブジェクトになるんだろう?」とか、「なんで只の関数呼び出しに newを付けると、オブジェクト生成になるんだろう?」とか、そんな感じ。
と言うわけで例によりECMA262を見てみます。
11.2.2 The new Operator ... The production NewExpression : new NewExpression is evaluated as follows: 1. Evaluate NewExpression. 2. Call GetValue(Result(1)). 3. If Type(Result(2)) is not Object, throw a TypeError exception. 4. If Result(2) does not implement the internal [[Construct]] method, throw a TypeError exception. 5. Call the [[Construct]] method on Result(2), providing no arguments (that is, an empty list of arguments). 6. Return Result(5).
頑張って訳してみます。
言語実装の詳細っぽくて難しそうですが、JavaScriptでいう「オブジェクト」って、裏側では色々な内部メソッド(プロパティ?)を持っているらしくって、その内の"Construct"というメソッドを実行しているようです。本当に言語実装の裏側の話になってしまいます。JavaScriptのコードから"Construct"というメソッドを呼び出せる、という意味ではなくて、JavaScriptの実行エンジンが内部で管理する世界の話です。
では"Construct"という内部メソッドが呼ばれる時は何が起こるのでしょうか?
ECMA262の"13 Function Definition"の中に、ズバリ、関数オブジェクトの"Construct"プロパティが呼ばれた時のステップが記述されています。
13.2.2 [[Construct]] ... When the [[Construct]] property for a Function object F is called, the following steps are taken: 1. Create a new native ECMAScript object. 2. Set the [[Class]] property of Result(1) to "Object". 3. Get the value of the prototype property of the F. 4. If Result(3) is an object, set the [[Prototype]] property of Result(1) to Result(3). 5. If Result(3) is not an object, set the [[Prototype]] property of Result(1) to the original Object prototype object as described in 15.2.3.1. 6. Invoke the [[Call]] property of F, providing Result(1) as the this value and providing the argument list passed into [[Construct]] as the argument values. 7. If Type(Result(6)) is Object then return Result(6). 8. Return Result(1).
頑張って訳してみます。
ようやく突き止めました。関数をコンストラクタとしてnewで使う時に"this"が使えるのは、言語実装が裏側でECMAscriptオブジェクトというオブジェクトを生成して、それをthisに割り当ててくれているからでした。また、newの結果として返されるのはそのECMAscriptオブジェクトでした。(コンストラクタがObjectを返す場合はそちらが優先)
なんでこんな疑問を持ったかというと、自分、てっきり「this」がprototypeを指すものとどこかで勘違いしていたからです。それでちょっと実験中に「アレ?」となって、確かめた次第です。
なお今回注目した "this" はnew演算子を付けた関数呼び出し中での "this" で、その他のケースは一切考慮してません。そこまで追いつめてないです、スミマセン。