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

JavaScript/constructor

JavaScript/constructor

JavaScript / constructor
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"というメソッドを実行しているようです。

"Construct"のようにECMA262の仕様書で二重角括弧で囲まれた語句は、正確には、説明用の便宜上のラベルとなっています。つまり実際に"Construct"という名前のメソッドが内部実装で存在するわけではなくて、仕様的に、このような動作をする機能を作って下さいね、という意味合いになります。
二重角括弧はWiki書式と衝突するので、今後は " (ダブルクォート)で囲って表記します。

コンストラクタの中で 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" で、その他のケースは一切考慮してません。そこまで追いつめてないです、スミマセン。



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