当前位置: 首页 > 面试题库 >

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

任伟
2023-03-14
问题内容

有什么方法可以创建一个在运行时确定的 真实名称
的函数,而无需使用eval,而仅使用纯JavaScript?(因此,不会生成任何script特定于浏览器环境的生成的元素(eval无论如何在很多方面都是伪装的;不会使用一个特定JavaScript引擎的非标准功能,等等)。

请注意,我特别 问有关的变量或有名称,如性质引用匿名函数:

// NOT this
var name = /* ...come up with the name... */;
var obj = {};
obj[name] = function() { /* ... */ };

那里,尽管对象属性有一个名称,但 函数
没有。匿名函数适用于很多事情,但不适用于我在这里寻找的东西。我希望函数具有名称(例如,显示在调试器的调用堆栈中,等等)。


问题答案:

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

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

console.log("Function's `name` property: " + f.name + " (see compatibility note)");

// We can see whether it has a name in stack traces via an exception

try {

  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` property

console.log("Function's `name` property: " + F.name + " (see compatibility note)");

// We can see whether it has a name in stack traces via an exception

try {

  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` property

console.log("Function's `name` property: " + F.name + " (see compatibility note)");

// We can see whether it has a name in stack traces via an exception

try {

  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 = /* ...come up with the 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 = /* ...come up with the 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不在所创建函数的作用域内。因此,不是让整洁的编码器避免使用全局符号的一种选择,但是对于那些希望将生成的函数与生成函数的范围解除关联的情况很有用。



 类似资料:
  • 问题内容: 我想知道JavaScript是否具有C#中的&&运算符之类的“短路”评估。如果没有,我想知道是否有一种合理的解决方法可以采用。 问题答案: 是的,JavaScript具有“短路”评估。

  • 问题内容: 给出以下代码: 如果返回,php解释器是否还会检查以后的条件,例如? 如果是这样,那么为什么它在不需要时会做额外的工作呢? 问题答案: 是的,PHP解释器是“惰性”的,这意味着它将尽可能少地进行比较以评估条件。 如果要验证,请尝试以下操作:

  • 问题内容: 一直困扰我的是Javascript中的方法多么不可预测。 根据我的经验,在很多情况下计时器都非常不准确。所谓不准确,是指实际延迟时间或多或少地相差250-500ms。尽管这不是很长的时间,但是使用它来隐藏/显示UI元素时,时间显然是显而易见的。 是否有任何技巧可以确保准确执行(无需借助外部API),或者这是一个失败的原因? 问题答案: 是否有任何技巧可以确保准确执行(无需借助外部API

  • 问题内容: 我知道Java在这种情况下具有智能/惰性评估: 但是关于: 即使返回true 也被调用? 问题答案: 在Java(和其他类似C的语言)中,这称为 短路评估 。* 是的,在第二个示例中总是被调用。也就是说,除非编译器/ JVM可以确定它没有可观察到的副作用,否则在这种情况下它可以选择进行优化,但是无论如何您都不会注意到它们之间的差异。 两者截然不同; 前者本质上是一种优化技术,而第二种则

  • 还有这个 UnsatisfiedDependencyException:创建名为“Client Service”的bean时出错:通过字段“Client Repository”表示的未满足的依赖关系;嵌套异常为org.springframework.beans.factory.nosuchbeandefinitionexception:没有“com.kopylov.repository.clien