home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

JavaScript/constructor (v1)

JavaScript/constructor (v1)

JavaScript / constructor (v1)
id: 164 所有者: msakamoto-sf    作成日: 2009-02-08 16:16:32
カテゴリ: JavaScript 

まず、関数を使ってオブジェクトを生成する典型的なサンプルを以下に示します。

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を付けると、オブジェクト生成になるんだろう?」とか、そんな感じ。

なんで関数呼び出しに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).

頑張って訳してみます。

  1. "NewExpression"を評価します。
  2. GetValue(1.の結果) を実行します。
  3. 2.の結果の型が Object で無い場合は、TypeError 例外を投げます。
  4. 2.の結果が、"Construct"という内部メソッドを実装していない場合は、 TypeError 例外を投げます。
  5. 引数無しで 2.の結果の"Construct"という内部メソッドを実行します。
  6. 5. の結果を返します。

言語実装の詳細っぽくて難しそうですが、JavaScriptでいう「オブジェクト」って、裏側では色々な内部メソッド(プロパティ?)を持っているらしくって、その内の"Construct"というメソッドを実行しているようです。本当に言語実装の裏側の話になってしまいます。JavaScriptのコードから"Construct"というメソッドを呼び出せる、という意味ではなくて、JavaScriptの実行エンジンが内部で管理する世界の話です。

コンストラクタの中で this が(newして返される)オブジェクトを指すのは本当?

では"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).

頑張って訳してみます。

  1. nativeなECMAscriptオブジェクトを生成します。(nativeって・・・?)
  2. 1.の結果の"Class"プロパティに "Object" をセットします。
  3. 関数オブジェクトFの prototype プロパティの値を取得します。
  4. 3.の結果がオブジェクトの場合は、1.の結果の"Prototype"プロパティに3.の結果をセットします。
  5. 3.の結果がオブジェクトでない場合は、15.2.3.1 で記述されているようなObject.prototypeを1.の結果の"Prototype"プロパティにセットします。
  6. 1.の結果をthisにセットして関数オブジェクトの "Call" プロパティを実行します。引数には"Construct"に渡された引数のリストを渡します。
  7. 6.の結果がObject型であれば、それを返します。
  8. 1.の結果を返します。

ようやく突き止めました。関数をコンストラクタとしてnewで使う時に"this"が使えるのは、言語実装が裏側でECMAscriptオブジェクトというオブジェクトを生成して、それをthisに割り当ててくれているからでした。また、newの結果として返されるのはそのECMAscriptオブジェクトでした。(コンストラクタがObjectを返す場合はそちらが優先)

なんでこんな疑問を持ったかというと、自分、てっきり「this」がprototypeを指すものとどこかで勘違いしていたからです。それでちょっと実験中に「アレ?」となって、確かめた次第です。

なお今回注目した "this" はnew演算子を付けた関数呼び出し中での "this" で、その他のケースは一切考慮してません。そこまで追いつめてないです、スミマセン。



プレーンテキスト形式でダウンロード
表示中のバージョン : 1
現在のバージョン : 2
更新者: msakamoto-sf
更新日: 2009-02-14 12:50:02
md5:15f49851feaee46519780be7d6f3f893
sha1:ff32498c7384517a663e2606be662789a847c38f
コメント
コメントを投稿するにはログインして下さい。