是否有任何非评估方法来创建具有运行时确定名称的函数?

是否有任何非评估方法来创建具有运行时确定名称的函数?,第1张

是否有任何非评估方法来创建具有运行时确定名称函数

ECMAscript 2015+(又称“ ES6”)的答案

是的
。从ES2015开始,由分配给对象属性的匿名函数表达式创建的函数采用该对象属性的名称。尽管Edge和Safari不在堆栈跟踪中使用该名称,但所有现代浏览器均实现了此功能。我们可以将其与另一个ES2015功能(计算的属性名称)结合使用,以命名不带

newFunction
或的函数
eval

在ES2015中,这将创建一个名为“ foo ###”的函数,其中###为1-3位数字:

const dynamicName = "foo" + Math.floor(Math.random() * 1000);const obj = {  [dynamicName]() {    throw new Error();  }};const f = obj[dynamicName];// See its `name` propertyconsole.log("Function's `name` property: " + f.name + " (see compatibility note)");// We can see whether it has a name in stack traces via an exceptiontry {  f();} catch (e) {  console.log(e.stack);}

它也可以使用

[dynamicName]: function() { }
,不需要方法语法,函数语法也可以。如果您想以这种方式创建构造函数,那么这很方便:

const dynamicName = "Foo" + Math.floor(Math.random() * 1000);const obj = {    [dynamicName]: function(throwError = false) {        if (throwError) { throw new Error();        }    }};const F = obj[dynamicName];// See its `name` propertyconsole.log("Function's `name` property: " + F.name + " (see compatibility note)");// We can see whether it has a name in stack traces via an exceptiontry {  new F(true);} catch (e) {  console.log(e.stack);}// And we can see it works as a constructor:const inst = new F();console.log(inst instanceof F); // true

当然,这是ES2015 +,因此您也可以

class
用来创建构造函数
[dynamicName]: class { }

const dynamicName = "Foo" + Math.floor(Math.random() * 1000);const obj = {    [dynamicName]: class {        constructor(throwError = false) { if (throwError) {     throw new Error(); }        }    }};const F = obj[dynamicName];// See its `name` propertyconsole.log("Function's `name` property: " + F.name + " (see compatibility note)");// We can see whether it has a name in stack traces via an exceptiontry {  new F(true);} catch (e) {  console.log(e.stack);}// And we can see it works as a constructor:const inst = new F();console.log(inst instanceof F); // true

ECMAscript 5的答案 (从2012年开始)

不。没有构造函数

eval
或其表亲,您将无法做到这一点
Function
。您的选择是:

  1. 改用匿名函数。现代引擎可以帮助您进行调试。

  2. 使用

    eval

  3. 使用

    Function
    构造函数。

细节:

  1. 改用匿名函数。许多现代引擎会显示一个有用的名称(例如,在调用栈和这样的),如果你有一个很好的,明确的

    var name = function() { ... };
    表达(显示变量的名称),即使 在技术上 的函数没有名字。在ES6中,如果可以从上下文中推断出函数,则实际上以这种方式创建的函数将具有名称。但是,无论哪种方式,如果您想要一个真正的运行时定义的名称(一个来自变量的名称),您都将陷入困境。

  2. 使用

    eval
    当您可以避免时
    eval
    是邪恶的,但是使用字符串,您可以完全控制自己,并且在自己控制的范围内,并且了解成本(您正在启动Javascript解析器),否则 就无法 做其他事情( (在这种情况下),只要您确实需要执行此 *** 作即可。但是,如果您无法控制字符串或范围,或者您不希望花费任何费用,则必须使用匿名函数。

下面是如何

eval
选择的样子:

    var name = ;var f = eval(    "(function() {n" +    "   function " + name + "() {n" +    "       console.log('Hi');n" +    "   }n" +    "   return " + name + ";n" +    "})();");

这样就可以创建一个具有我们在运行时想出的名称的函数,而不会将该名称泄漏到包含的作用域中(也不会触发IE8及更早版本中对命名函数表达式的错误处理),并将对该函数的引用分配给

f
。(它很好地格式化了代码,因此在调试器中单步调试很容易。)

令人惊讶的是,这在旧版本的Firefox中并没有正确地分配名称。从Firefox 29中的Javascript引擎的当前版本开始,它可以。

因为使用

eval
,您创建的函数可以访问其创建范围,如果您是一个整洁的编码器,并且避免使用全局符号,那么这很重要。因此,例如:

    (function() {    function display(msg) {        var p = document.createElement('p');        p.innerHTML = String(msg);        document.body.appendChild(p);    }    var name = ;    var f = eval(        "(function() {n" +        "   function " + name + "() {n" +        "       display('Hi');n" +         // <=== Change here to use the        "   }n" +    //      function above        "   return " + name + ";n" +        "})();"    );})();
  1. 使用
    Function
    构造函数,如MarcosCáceres在本文中所演示的:
    var f = new Function("return function " + name + "() {n" +"    display('Hi!');n" +"    debugger;n" +"};"

    )();

在这里,我们创建了一个临时的匿名函数(通过

Function
构造函数创建的函数)并调用它;该临时匿名函数使用命名函数表达式创建命名函数。这
触发IE8和更早版本中命名函数表达式的错误句柄,但这并不重要,因为其副作用仅限于临时函数。

它比

eval
版本短,但是有一个问题:通过
Function
构造函数创建的函数无法访问其创建范围。因此,上面的示例
display
将失败,因为它
display
不在所创建函数的作用域内。因此,不是让整洁的编码器避免使用全局符号的一种选择,但是对于那些希望将生成的函数与生成函数的范围解除关联的情况很有用。



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5639223.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存