Function类是如何工作的?

4
我正在阅读 Airbnb JavaScript Style Guide,看到了此处提到的以下函数:
var subtract = Function('a', 'b', 'return a - b');

我在Chrome调试器控制台中测试过这个方法,只需要输入上述一行代码并执行:

subtract(7,3)

它返回了4,我非常惊讶它实际上起作用了。

样式指南提到不建议这样做,但这让我思考这个函数语法。我从未见过一个没有主体的函数返回正确的结果。

这是如何/为什么工作的?它存在多久了?使用它的指南/最佳实践是什么?


1
"return a - b" 是 functionBody;请参阅 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function。 - guest271314
“它存在多久了?” 至少从1997年的第一个ECMAScript规范开始就有了。 “使用它的指南/最佳实践是什么?” 除非必须动态生成函数,否则不要使用它。 - Felix Kling
它自ECMA3以来就存在了,它并不是"不好的想法",而是一种对于某些特殊情况而言的专业工具,这种情况比"普通"函数更少见。如果使用不当,动态函数会通过削减底层优化来抢占性能,从而使编译器的假设变得不可信,因此需要避免。另一方面,一个精心制作的动态函数实际上可以提高执行速度,有时甚至可以显著地提高执行速度,就像在mustache.js中最初使用Function时所做的那样,在遵循CSP的前提下将其编码。 - dandavis
尽管你的例子“可行”,但为了保持代码的清晰,正确的语法是 new Function(...) - mferly
@Marcus:对于任何核心 JS 构造函数都不需要 new,实际上,它不会像通常导致的 new 一样产生一个对象,所以省略它不应该让你发疯 ;) - dandavis
@dandavis - 是的,因此才有了“保持理智” ;) 但是 new Function() 才是实际语法。不过无论如何都可以工作。 - mferly
3个回答

3
这将使用Function对象而非“正常”的function语法来创建函数。请注意大写的“F”。
var subtract = Function('a', 'b', 'return a - b');

这意味着您使用参数ab定义一个函数,并使用体return a - b。它等效于

var subtract = function (a, b) { return a - b; };

通常情况下,在调用构造函数时您需要使用new,但是即使不使用new,它也可以正常工作;只是对于人类来说稍微难以阅读而已。
根据Mozilla开发者网络的解释: Function 构造函数 创建一个新的 Function 对象。在 JavaScript 中,每个函数实际上都是一个 Function 对象。

语法

new Function ([arg1[, arg2[, ...argN]],] functionBody)

参数

arg1, arg2, ... argN
函数中要使用的形式参数名。每个参数必须是一个字符串,对应一个有效的 JavaScript 标识符或这样的字符串列表,用逗号分隔;例如 "x"、"theValue" 或 "a,b"。

functionBody
包含组成函数定义的 JavaScript 语句的字符串。

...

Function 构造函数作为函数调用(不使用 new 操作符)与将其作为构造函数调用具有相同的效果。


好的,这很有道理。我没有意识到:1)那是内置功能,因为 'F' 大写;2)函数对象的最后一个参数是函数体。这很有趣。谢谢! - user2363207
@BarrettNashville 很高兴能帮忙!请记得选择一个答案! :) - elixenide
来自MDN的另一个有趣观点:将Function构造函数作为函数调用(而不使用new运算符)与将其作为构造函数调用具有相同的效果。 - Domino
是的,抱歉。花了几分钟时间。每次我选择答案时都会弹出一个窗口,上面写着“您需要在X分钟后才能选择答案”。我以前从未见过这种情况。现在完成了。 - user2363207

2

这段内容来自MDN

Function 构造函数创建一个新的 Function 对象。在 JavaScript 中,每个函数实际上都是一个 Function 对象。

在你的示例中,

var subtract = Function('a', 'b', 'return a - b');

ab是传递给函数体的参数,函数体为return a - b

最后一个参数始终是函数体。

因此,如果你像这样传递它:

var subtract = Function('a', 'b');
// Reference error b is not defined

这个示例实际上等同于:
var subtract = Function('a, b', 'return a - b');

或者在其他版本中,
var subtract = function(a, b) { return a - b; };

或者

var subtract = (a, b) => a - b;

0

构造函数

var subtract = Function('a', 'b', 'return a - b');

或者

var subtract = new Function('a', 'b', 'return a - b');

var result = subtract(10, 5);   // result : 5

这是使用内置的JavaScript函数constructor,称为Function()来定义JavaScript函数。这与使用表达式定义匿名函数的结果相同:

使用表达式定义匿名函数:

var subtract = function(a, b) { return a - b); }

var result = subtract(10, 5);   // result : 5

构造函数语法:

new Function ([arg1[, arg2[, ...argN]],] functionBody)

所有参数(字符串)均可选,但必须有必需的functionBody(字符串)。它使用eval()函数来评估字符串。它返回一个新的函数对象,该对象用于调用函数。 应避免使用带有构造函数的函数,因为:
  1. 它使用函数eval()来评估字符串,因此速度较慢
  2. 在这样的函数中调试代码行更加困难
  3. eval()可以执行任何javascript代码,因此存在安全漏洞

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接