检查对象是否为构造函数-IsConstructor

i

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

ECMAScript定义了IsConstructor,它确实做到了这一点,但这是一个内部操作。

所以我想模仿一下。我曾考虑过尝试在try语句内实例化或子类化,但都无法在所有情况下可靠地工作。

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中,也许在某些草案或提议的功能中?

i

这是基于Jason Orendorffesdicuss发布的代码

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中

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

  • 按照9.5.14的规定设置P的[[Construct]]内部方法

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

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

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

对于基元,这是可以的,因为它们不能是构造函数。但是,已撤销的代理可以是构造函数,也可以不是构造函数,并且我们无法正确测试它们。

您可能想检测该值是否是已撤销的代理,以便以不同的方式处理这种情况。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章