すべての Javascript 関数がコンストラクターではないことに気付きました。
var obj = Function.prototype;
console.log(typeof obj === 'function'); //true
obj(); //OK
new obj(); //TypeError: obj is not a constructor
質問 1: 関数がコンストラクターであるかどうかを確認して、キーワードで呼び出せるようにするにはどうすればよいでしょうかnew
?
質問2: 関数を作成するときに、ないコンストラクタ?
ベストアンサー1
背景を少し説明します。
ECMAScript 6+では、呼び出し可能( なしでも呼び出せますnew
)構築可能な( ) 関数で呼び出すことができますnew
:
- 矢印関数構文またはクラスやオブジェクトリテラルのメソッド定義を介して作成された関数は、構築不可能。
- 構文で作成された関数
class
は呼び出し不可。 - その他の方法 (関数式/宣言、
Function
コンストラクター) で作成された関数は、呼び出し可能および構築可能です。 - 明示的に別途指定されない限り、組み込み関数は構築できません。
についてFunction.prototype
Function.prototype
いわゆる組み込み関数 それは構築不可能である仕様より:
[[Construct]]
コンストラクターとして識別されない組み込み関数オブジェクトは、特定の関数の説明で特に指定されない限り、内部メソッドを実装しません。
の値は、Function.prototype
ランタイム初期化の最初に作成されます。これは基本的に空の関数であり、構築可能であることは明示的に示されていません。
関数がコンストラクターであるかどうかを確認して、new で呼び出せるようにするにはどうすればよいでしょうか?
それを実行するための組み込みの方法はありません。try
を使用して関数を呼び出しnew
、エラーを検査するか、 を返すことができますtrue
。
function isConstructor(f) {
try {
new f();
} catch (err) {
// verify err is the expected error and then
return false;
}
return true;
}
ただし、関数には副作用がある可能性があるため、このアプローチはフェイルセーフではありません。そのため、 を呼び出した後f
、環境がどの状態にあるかはわかりません。
また、これは関数ができるコンストラクタとして呼び出されるのではなく、意図されましたコンストラクターとして呼び出されます。そのためには、ドキュメントまたは関数の実装を確認する必要があります。
注記:実稼働環境でこのようなテストを使用する理由はまったくありません。関数が で呼び出されるかどうかは、new
そのドキュメントから判別できる必要があります。
関数を作成するときに、それをコンストラクターにしないようにするにはどうすればよいですか?
関数を作成するのは本当に構築可能な矢印関数を使うことができます:
var f = () => console.log('no constructable');
矢印関数は定義上、構築できません。代わりに、関数をオブジェクトまたはクラスのメソッドとして定義することもできます。
それ以外の場合は、関数new
の値をチェックして、関数が (または同様のもの)で呼び出されているかどうかを確認しthis
、呼び出されている場合はエラーをスローします。
function foo() {
if (this instanceof foo) {
throw new Error("Don't call 'foo' with new");
}
}
もちろん、 の値を設定する方法は他にもあるためthis
、誤検知が発生する可能性があります。
例
function isConstructor(f) {
try {
new f();
} catch (err) {
if (err.message.indexOf('is not a constructor') >= 0) {
return false;
}
}
return true;
}
function test(f, name) {
console.log(`${name} is constructable: ${isConstructor(f)}`);
}
function foo(){}
test(foo, 'function declaration');
test(function(){}, 'function expression');
test(()=>{}, 'arrow function');
class Foo {}
test(Foo, 'class declaration');
test(class {}, 'class expression');
test({foo(){}}.foo, 'object method');
class Foo2 {
static bar() {}
bar() {}
}
test(Foo2.bar, 'static class method');
test(new Foo2().bar, 'class method');
test(new Function(), 'new Function()');