new 演算子と Construct 内部メソッド

var o1 = [],
    f1 = function(){ return o1; };
(new f1()) === o1; // true

var o2 = 1,
    f2 = function(){ return o2; };
(new f2()) === o2; // false

これってどういうことだってばよ? という話。

例によって仕様から解説してみようかと。

new 演算子

new 演算子自体は実は大したことはしていない。

  • 対象が function であること
  • 対象の function に [[Construct]] 内部メソッドがあること
  • [[Construct]]を呼び出した結果を返す

要するに[[Construct]]が肝である。

[[Construct]] 内部メソッド

  1. 空オブジェクト obj を作成
  2. obj の [[Prototype]] にコンストラクタのprototypeプロパティをセット
  3. obj が this となるようにコンストラクタをcallし結果を result にセット
  4. result が Object なら result を返す
  5. obj を返す

だいたいこんな流れ。最後の条件分岐が今回の肝。

最初のコードに戻ると、f1 では Arrayオブジェクト(typeof [] === "object")を返すのでそれを返し、f2 ではプリミティブなnumberを返すので、それは無視されて作成したobjを返すという仕組になっている。

コードにしてみる

言葉で書くと、分かりにくいかもしれないのでコードにしてみる。

Function.prototype.new = function () {
  var obj = Object.create(this.prototype);
  var result = this.apply(obj, arguments);
  if (typeof result === "object")
    return result;

  return obj;
};

こんな感じ。