检查对象是否为构造函数-IsConstructor [英] Check if object is a constructor - IsConstructor

查看:44
本文介绍了检查对象是否为构造函数-IsConstructor的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想检查JavaScript值是否是构造函数,即,它是否具有[[Construct]]内部方法.

I want to check if a JavaScript value is a constructor, that is, if it has a [[Construct]] internal method.

ECMAScript定义了 IsConstructor ,它正是这样做的,但这是内部操作.

ECMAScript defines IsConstructor, which does exactly this, but it's an internal operation.

所以我想模仿它.我考虑过尝试在 try 语句中进行实例化或子类化,但是在所有情况下都无法可靠地工作.

So I want to emulate it. I thought about attempting instantiation or subclassing inside a try statement, but neither does work reliably for all cases.

function isConstructor(value) {
  try {
    return new value(), true;
  } catch(err) {
    return false;
  }
}

function isConstructor(value) {
  try {
    return new value(), true;
  } catch(err) {
    return false;
  }
}
var tests = 0,
    failed = 0;  
function test(value, expected, msg) {
  ++tests;
  try {
    var result = isConstructor(window.eval(value));
  } catch(err) {
    result = err;
  }
  if(result !== expected) {
    ++failed;
    console.log('Testing: ' + value + '\nMessage: ' + msg + '\nResult: ' + result + '\nExpected: ' + expected);
  }
}
function testEnd() {
  console.log(failed + ' out of ' + tests + ' tests failed.');
}
test('undefined', false, 'undefined is not a constructor');
test('null', false, 'null is not a constructor');
test('true', false, 'booleans are not constructors');
test('0', false, 'numbers are not constructors');
test('"abc"', false, 'strings are not constructors');
test('Symbol()', false, 'symbols are not constructors');
test('({})', false, '{} is not a constructor');
test('[]', false, 'arrays are not constructors');
test('(function(){})', true, 'normal functions are constructors');
test('(function(){throw TypeError()})', true, 'normal functions are constructors');
test('(function(){}.bind())', true, 'bounded normal functions are constructors');
test('() => {}', false, 'arrow functions are not constructors');
test('((() => {}).bind())', false, 'bounded arrow functions are not constructors');
test('(function*(){})', false, 'generator functions are not constructors');
test('(function*(){}.bind())', false, 'bounded generator functions are not constructors');
test('(class{})', true, 'classes are constructors');
test('(class extends function(){}{})', true, 'classes are constructors');
test('new Proxy([],{})', false, 'proxies whose target is not constructor are not constructors');
test('new Proxy(function(){},{})', true, 'proxies whose target is a constructor are constructors');
test('new Proxy(function(){},{get:()=>{throw TypeError()}})', true, 'proxies whose target is a constructor are constructors');
test('new Proxy(function(){},{construct:()=>{throw TypeError()}})', true, 'proxies whose target is a constructor are constructors');
test('var r1 = Proxy.revocable([],{}); r1.proxy', false, 'revocable proxies whose target is not a constructor are notconstructors');
test('r1.revoke(); r1.proxy', false, 'revoked proxies whose target was not a constructor are not constructors');
test('var r2 = Proxy.revocable(function(){},{}); r2.proxy', true, 'revocable proxies whose target is a constructor are constructors');
test('r2.revoke(); r2.proxy', true, 'revoked proxies whose target was a constructor are constructors');
testEnd();

function isConstructor(value) {
  if(value === null) return false;
  try {
    return class extends value {}, true;
  } catch(err) {
    return false;
  }
}

function isConstructor(value) {
  if(value === null) return false;
  try {
    return class extends value {}, true;
  } catch(err) {
    return false;
  }
}
var tests = 0,
    failed = 0;  
function test(value, expected, msg) {
  ++tests;
  try {
    var result = isConstructor(window.eval(value));
  } catch(err) {
    result = err;
  }
  if(result !== expected) {
    ++failed;
    console.log('Testing: ' + value + '\nMessage: ' + msg + '\nResult: ' + result + '\nExpected: ' + expected);
  }
}
function testEnd() {
  console.log(failed + ' out of ' + tests + ' tests failed.');
}
test('undefined', false, 'undefined is not a constructor');
test('null', false, 'null is not a constructor');
test('true', false, 'booleans are not constructors');
test('0', false, 'numbers are not constructors');
test('"abc"', false, 'strings are not constructors');
test('Symbol()', false, 'symbols are not constructors');
test('({})', false, '{} is not a constructor');
test('[]', false, 'arrays are not constructors');
test('(function(){})', true, 'normal functions are constructors');
test('(function(){throw TypeError()})', true, 'normal functions are constructors');
test('(function(){}.bind())', true, 'bounded normal functions are constructors');
test('() => {}', false, 'arrow functions are not constructors');
test('((() => {}).bind())', false, 'bounded arrow functions are not constructors');
test('(function*(){})', false, 'generator functions are not constructors');
test('(function*(){}.bind())', false, 'bounded generator functions are not constructors');
test('(class{})', true, 'classes are constructors');
test('(class extends function(){}{})', true, 'classes are constructors');
test('new Proxy([],{})', false, 'proxies whose target is not constructor are not constructors');
test('new Proxy(function(){},{})', true, 'proxies whose target is a constructor are constructors');
test('new Proxy(function(){},{get:()=>{throw TypeError()}})', true, 'proxies whose target is a constructor are constructors');
test('new Proxy(function(){},{construct:()=>{throw TypeError()}})', true, 'proxies whose target is a constructor are constructors');
test('var r1 = Proxy.revocable([],{}); r1.proxy', false, 'revocable proxies whose target is not a constructor are notconstructors');
test('r1.revoke(); r1.proxy', false, 'revoked proxies whose target was not a constructor are not constructors');
test('var r2 = Proxy.revocable(function(){},{}); r2.proxy', true, 'revocable proxies whose target is a constructor are constructors');
test('r2.revoke(); r2.proxy', true, 'revoked proxies whose target was a constructor are constructors');
testEnd();

有什么方法可以可靠地对其进行测试?如果不在ES6或ES7中,也许在某些草案或提议的功能中?

Is there some way to test it reliably? If not in ES6 or ES7, maybe in some draft or proposed feature?

推荐答案

这是基于 Jason Orendorff esdicuss ..>

This is based on the code posted by Jason Orendorff on esdicuss.

function isConstructor(value) {
  try {
    new new Proxy(value, {construct() { return {}; }});
    return true;
  } catch (err) {
    return false;
  }
}

function isConstructor(value) {
  try {
    new new Proxy(value, {construct() { return {}; }});
    return true;
  } catch (err) {
    return false;
  }
}
var tests = 0,
    failed = 0;
function test(value, expected, msg) {
  ++tests;
  try {
    var result = isConstructor(window.eval(value));
  } catch(err) {
    result = err;
  }
  if(result !== expected) {
    ++failed;
    console.log('Testing: ' + value + '\nMessage: ' + msg + '\nResult: ' + result + '\nExpected: ' + expected);
  }
}
function testEnd() {
  console.log(failed + ' out of ' + tests + ' tests failed.');
}
test('undefined', false, 'undefined is not a constructor');
test('null', false, 'null is not a constructor');
test('true', false, 'booleans are not constructors');
test('0', false, 'numbers are not constructors');
test('"abc"', false, 'strings are not constructors');
test('Symbol()', false, 'symbols are not constructors');
test('({})', false, '{} is not a constructor');
test('[]', false, 'arrays are not constructors');
test('(function(){})', true, 'normal functions are constructors');
test('(function(){throw TypeError()})', true, 'normal functions are constructors');
test('(function(){}.bind())', true, 'bounded normal functions are constructors');
test('() => {}', false, 'arrow functions are not constructors');
test('((() => {}).bind())', false, 'bounded arrow functions are not constructors');
test('(function*(){})', false, 'generator functions are not constructors');
test('(function*(){}.bind())', false, 'bounded generator functions are not constructors');
test('(class{})', true, 'classes are constructors');
test('(class extends function(){}{})', true, 'classes are constructors');
test('new Proxy([],{})', false, 'proxies whose target is not constructor are not constructors');
test('new Proxy(function(){},{})', true, 'proxies whose target is a constructor are constructors');
test('new Proxy(function(){},{get:()=>{throw TypeError()}})', true, 'proxies whose target is a constructor are constructors');
test('new Proxy(function(){},{construct:()=>{throw TypeError()}})', true, 'proxies whose target is a constructor are constructors');
test('var r1 = Proxy.revocable([],{}); r1.proxy', false, 'revocable proxies whose target is not a constructor are notconstructors');
test('r1.revoke(); r1.proxy', false, 'revoked proxies whose target was not a constructor are not constructors');
test('var r2 = Proxy.revocable(function(){},{}); r2.proxy', true, 'revocable proxies whose target is a constructor are constructors');
test('r2.revoke(); r2.proxy', true, 'revoked proxies whose target was a constructor are constructors');
testEnd();

仅当代理对象的初始目标是构造函数时,它们才是构造函数.来自 ProxyCreate

Proxy objects are only constructors if their initial target was a constructor. From ProxyCreate,

如果 target 具有[[Construct]]内部方法,则

If target has a [[Construct]] internal method, then

因此,代码创建了一个代理对象,其目标是我们要检查的值,并且其处理程序具有一个不会抛出的构造陷阱.

Therefore, the code creates a proxy object whose target is the value we want to check, and whose handler has a construct trap which doesn't throw.

这样,如果代理是构造函数(即测试值是构造函数),则在实例化时它将在陷阱中运行代码,而不是将操作重定向到目标,并且不会抛出异常.如果代理不是构造函数(即测试值都不是),则在实例化时它将引发错误.

This way, if the proxy is a constructor (i.e. the tested value is a constructor), when instantiated it will run the code in the trap instead of redirecting the operation to the target, and it won't throw. If the proxy is not a constructor (i.e. the tested value is neither), when instantiated it will throw an error.

但是有一个小问题.创建代理时,目标必须是非代理对象或非吊销的代理对象.否则,它将引发,因此上面的代码将其视为非构造函数.

There is a little problem, though. When creating a proxy, the target must be either a non-proxy object, or a non-revoked proxy object. Otherwise, it throws, and thus the code above considers it a non-constructor.

对于原语,这是可以的,因为它们不能是构造函数.但是,被撤销的代理可以是构造函数,也可以不是构造函数,我们无法对其进行正确的测试.

This is OK in case of primitives, because they can't be constructors. However, revoked proxies can be constructors or not, and we can't test them properly.

您可能希望检测该值是否是已撤销的代理,以便以不同的方式处理该情况.

You might want to detect if the value is a revoked proxy in order to handle that case differently.

这篇关于检查对象是否为构造函数-IsConstructor的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆