是否有任何非eval方法来创建具有运行时确定名称的函数? [英] Is there any non-eval way to create a function with a runtime-determined name?

查看:102
本文介绍了是否有任何非eval方法来创建具有运行时确定名称的函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有办法创建一个具有真实姓名的函数,该函数在运行时确定而不使用 eval ,并且只使用纯JavaScript? (因此,没有生成脚本元素,因为这些元素特定于浏览器环境[并且在很多方面将是 eval 无论如何都是伪装的;不使用某个特定JavaScript引擎的非标准功能等。)

Is there any way to create a function with a real name that's determined at runtime without using eval, and using only pure JavaScript? (So, no generated script elements, as those are specific to the browser environment [and in many ways would be eval in disguise anyway]; no using non-standard features of one particular JavaScript engine, etc.)

请注意,我特意询问关于由具有名称的变量或属性引用的匿名函数,例如:

Note that I'm specifically not asking about anonymous functions referenced by variables or properties that have names, e.g.:

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

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

There, while the object property has a name, the function does not. Anonymous functions are fine for lots of things, but not what I'm looking for here. I want the function to have a name (e.g., to show up in call stacks in debuggers, etc.).

推荐答案

ECMAScript 2015的答案(又名ES6)

。从ES2015开始,由分配给对象属性的匿名函数表达式创建的函数将获取该对象属性的名称。 当我在2015年5月11日写这篇文章时,除了微软的Windows 10预览版Project Spartan之外,没有任何JavaScript引擎支持这个(是的,你读得对,M $在Mozilla 或 Google),但它符合规范并且实施将迎头赶上。 更新:截至2016年10月,最新版本的Chrome实施函数#name 以及分配名称的各种规则; Firefox仍然没有,但它们会到达那里。

Yes. As of ES2015, the function created by an anonymous function expression assigned to an object property takes the name of that object property. As I write this on 11th May 2015, no JavaScript engine in the wild other than Microsoft's "Project Spartan" for Windows 10 Preview supports this (yes, you read that right, M$ got there before Mozilla or Google), but it is in the spec and implementations will catch up. Update: As of October 2016, recent versions of Chrome implement Function#name and the various rules for assigning names; Firefox still does not, but they'll get there.

例如,在ES2015中,这会创建一个名为foo ###的函数,其中###是1 -3位数:

So for instance, in ES2015 this creates a function named "foo###" where ### is 1-3 digits:

const dynamicName = "foo" + Math.floor(Math.random() * 1000);
const obj = {
  [dynamicName]() {
    throw new Error();
  }
};
const f = obj[dynamicName];
// See its `name` property (Edge and Chrome for now, eventually Firefox will get it)
console.log("Function's `name` property: " + f.name + " (see compatibility note)");
// We can see that it truly has a name (even in Firefox which doesn't have the `name` property yet) via an exception:
try {
  f();
} catch (e) {
  console.log(e.stack);
}

新的ES2015评估属性名称和新方法语法来创建函数并为其提供动态名称,然后在 f 中获取对它的引用。 (它也适用于 [dynamicName]:function(){} ,不需要方法语法,函数语法也没问题。)

That uses the new ES2015 evaluated property name and new method syntax to create the function and give it a dynamic name, then gets a reference to it in f. (It would also work with [dynamicName]: function() { }, method syntax isn't required, function syntax is fine.)

ECMAScript 5的答案 (自2012年起)

没有。如果没有 eval 或其表兄 Function 构造函数,则不能这样做。您的选择是:

No. You cannot do that without eval or its cousin the Function constructor. Your choices are:


  1. 改为使用匿名函数。现代引擎可以帮助调试它们。

  1. Live with an anonymous function instead. Modern engines do things to help debugging with those.

使用 eval

使用函数构造函数。

详细信息:


  1. 使用匿名函数代替。如果你有一个很好的,明确的 var name = function(){...}; 表达式,许多现代引擎将显示一个有用的名称(例如,在调用堆栈等) (显示变量的名称),即使技术上该函数没有名称。在ES6中,如果可以从上下文中推断出,那么以这种方式创建的函数实际上将具有名称。但是,无论哪种方式,如果你想要一个真正的运行时定义的名称(来自变量的名称),你就会陷入困境。

  1. Live with an anonymous function instead. Many modern engines will show a useful name (e.g., in call stacks and such) if you have a nice, unambiguous var name = function() { ... }; expression (showing the name of the variable), even though technically the function doesn't have a name. In ES6, functions created that way will actually have names if they can be inferred from the context. Either way, though, if you want a truly runtime-defined name (a name coming from a variable), you're pretty much stuck.

使用 EVAL eval 是邪恶的,当你可以避免它,但是在你控制的范围内你可以完全控制你的字符串了解成本(你正在启动一个JavaScript解析器),做你不能做的事情(就像在这种情况下一样),如果你真的需要做那件事就没关系。但是如果你不能控制字符串或范围,或者你不想要成本,你就必须使用匿名函数。

Use eval. eval is evil when you can avoid it, but with strings you're in total control of, in a scope you control, with an understanding of the costs (you're firing up a JavaScript parser), to do something you cannot do otherwise (as in this case), it's fine provided you really need to do that thing. But if you're not in control of the string or scope, or you don't want the cost, you'll have to live with an anonymous function.

这里是 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 。 (并且它可以很好地格式化代码,因此在调试器中单步执行它很容易。)

That creates a function with the name we come up with at runtime without leaking the name into the containing scope (and without triggering the flawed handling of named function expressions in IE8 and earlier), assigning a reference to that function to f. (And it formats the code nicely so single-stepping through it in a debugger is easy.)

这不用于在较旧版本中正确指定名称(令人惊讶) Firefox版本。它在Firefox 29中的JavaScript引擎的当前版本中,确实如此。

This didn't used to correctly assign the name (surprisingly) in older versions of Firefox. As of the current version of their JavaScript engine in Firefox 29, it does.

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

Because that uses eval, the function you create has access to the scope in which it was created, which is important if you're a tidy coder who avoids global symbols. So this works, for instance:

(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" +
        "})();"
    );
})();


  • 使用函数构造函数,如MarcosCáceres撰写的这篇文章所示:

    var f = new Function(
        "return function " + name + "() {\n" +
        "    display('Hi!');\n" +
        "    debugger;\n" +
        "};"
    )();
    

    实例 | 直播来源

    我们创建了一个临时匿名函数(通过函数构造函数创建的函数)并调用它;临时匿名函数使用命名函数表达式创建命名函数。 在IE8及更早版本中触发命名函数表达式的有缺陷句柄,但这并不重要,因为它的副作用仅限于临时函数。

    There we create a temporary anonymous function (the one created via the Function constructor) and call it; that temporary anonymous function creates a named function using a named function expression. That will trigger the flawed handle of named function expressions in IE8 and earlier, but it doesn't matter, because the side-effects of that are limited to the temporary function.

    这比 eval 版本短,但有一个问题:通过函数创建的函数构造函数可以访问创建它们的范围。因此,使用 display 的上述示例将失败,因为 display 将不在创建函数的范围内。 (这是一个失败的例子来源)。所以不是整理编码器避免全局符号的选项,但对于希望将生成的函数与生成函数的范围解除关联时有用。

    This is shorter than the eval version, but has an issue: Functions created via the Function constructor do not have access to the scope in which they were created. So the example above using display would fail, because display wouldn't be in-scope for the created function. (Here's an example of it failing. Source). So not an option for tidy coders avoiding global symbols, but useful for those times when you want to disassociate the generated function from the scope in which you're generating it.

    这篇关于是否有任何非eval方法来创建具有运行时确定名称的函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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