コンストラクタ内の [this] のインスタンス タイプを確認するのが方法です。問題は、これ以上面倒なことをしなくても、この方法ではエラーが発生しやすいことです。ただし、解決策があります。
関数 ClassA() を扱っているとしましょう。基本的なアプローチは次のとおりです。
function ClassA() {
if (this instanceof arguments.callee) {
console.log("called as a constructor");
} else {
console.log("called as a function");
}
}
上記の解決策が期待どおりに機能しない場合、いくつかの原因が考えられます。次の 2 つだけを考えてみましょう。
var instance = new ClassA;
instance.classAFunction = ClassA;
instance.classAFunction(); // <-- this will appear as constructor call
ClassA.apply(instance); //<-- this too
これらを克服するために、a) インスタンスのフィールドに「ConstructorFinished」などの情報を配置して再度確認するか、b) 構築されたオブジェクトをリストで追跡することを提案する人もいます。ClassA のすべてのインスタンスを変更することは、型に関連する機能が機能するにはあまりにも侵襲的で費用がかかるため、私は両方に不快感を覚えます。ClassA に多くのインスタンスがある場合、リスト内のすべてのオブジェクトを収集すると、ガベージ コレクションとリソースの問題が発生する可能性があります。
進むべき道は、ClassA 関数の実行を制御できるようにすることです。簡単なアプローチは次のとおりです。
function createConstructor(typeFunction) {
return typeFunction.bind({});
}
var ClassA = createConstructor(
function ClassA() {
if (this instanceof arguments.callee) {
console.log("called as a function");
return;
}
console.log("called as a constructor");
});
var instance = new ClassA();
これにより、[this] 値を使ったトリックの試みを効果的に防止できます。バインドされた関数は、 new演算子を使用して呼び出さない限り、元の [this] コンテキストを常に保持します。
高度なバージョンでは、コンストラクターを任意のオブジェクトに適用する機能が返されます。コンストラクターを型コンバーターとして使用したり、継承のシナリオで基本クラス コンストラクターの呼び出し可能なチェーンを提供したりできます。
function createConstructor(typeFunction) {
var result = typeFunction.bind({});
result.apply = function (ths, args) {
try {
typeFunction.inApplyMode = true;
typeFunction.apply(ths, args);
} finally {
delete typeFunction.inApplyMode;
}
};
return result;
}
var ClassA = createConstructor(
function ClassA() {
if (this instanceof arguments.callee && !arguments.callee.inApplyMode) {
console.log("called as a constructor");
} else {
console.log("called as a function");
}
});