1. ホーム
  2. javascript

[解決済み] 関数がコンストラクタとして呼び出されているかどうかを検出するには?

2022-09-09 13:15:32

質問

ある関数が与えられた。

function x(arg) { return 30; }

呼び方は2通りあります。

result = x(4);
result = new x(4);

前者は30を返し、後者はオブジェクトを返します。

関数がどちらの方法で呼ばれたかを検出するにはどうしたらよいでしょうか 関数自体の内部で ?

あなたの解決策が何であれ、それは次の呼び出しでも同様に動作しなければなりません。

var Z = new x(); 
Z.lolol = x; 
Z.lolol();

現在、すべてのソリューションが Z.lolol() がコンストラクタとしてそれを呼び出していると考えています。

どのように解決するのですか?

注意:ES2015以降で可能になりました。参照 Daniel Weinerの回答 .

私は、あなたが望むことが[ES2015より前に]可能であるとは思いません。単に、信頼できる推論を行うために関数内で利用可能な十分な情報がありません。

ECMAScript第3版の仕様を見ると、以下のような手順で new x() が呼び出されたときに行われるステップは、基本的に

  • 新しいオブジェクトを作成する
  • のprototypeプロパティにその内部[[Prototype]]プロパティを割り当てます。 x
  • 呼び出す x を通常通り呼び出し、新しいオブジェクトを this
  • への呼び出しが x がオブジェクトを返した場合はそれを返し、そうでない場合は新しいオブジェクトを返します。

関数がどのように呼び出されたかについて、実行中のコードでは何も役に立ちませんので、内部でテストできるのは x の中でテストできるのは this の値であり、ここでの回答はすべてそうなっています。あなたが観察したように、新しいインスタンス*である x を呼び出すと x をコンストラクタとして呼び出した場合、既存の x として渡される this を呼び出したとき x を関数として呼び出す場合。 でない限り によって作られたすべての新しいオブジェクトにプロパティを割り当てます。 x によって作られるすべての新しいオブジェクトにプロパティを割り当てます。

function x(y) {
    var isConstructor = false;
    if (this instanceof x // <- You could use arguments.callee instead of x here,
                          // except in in EcmaScript 5 strict mode.
            && !this.__previouslyConstructedByX) {
        isConstructor = true;
        this.__previouslyConstructedByX = true;
    }
    alert(isConstructor);
}

によって構築されたすべてのオブジェクトに余計なプロパティがついてしまうので、これは明らかに理想的ではありません。 x によって構築されたすべてのオブジェクトに、上書きされる可能性のある無用なプロパティが追加されることになるので、これは明らかに理想的ではありませんが、私はこれがあなたができる最善の方法だと思います。

(*) のインスタンスというのは不正確な表現ですが、十分に近いですし、 をコンストラクタとして呼び出すことで作成されたオブジェクトです。