JavaScript函数能接受的参数数量是否有限制?

100
我知道JavaScript函数可以接受“任意”数量的参数。
function f(){};
f(1,2,3,4 /*...*/);

但我在想,是否实际上有“任何”数量的限制呢?

例如,假设我向f()传递一百万个参数。 这会起作用吗? 还是解释器会崩溃?

我猜最大值要么是(a)特定于实现的,要么是(b)(2^32)-1,因为arguments对象类似于数组。

我没有在语言规范中看到这一点,但我可能没有联系到某些方面。


3
任何类似这样的大量数据序列都应该作为单个数组参数传递,而不是分开作为多个参数,这样在调用函数时就不需要制作整个大型数据集的副本。这必须是一个学术练习,而不是在良好的开发实践中会遇到的事情。 - jfriend00
1
@jfriend00,不要忘记 Function.prototype.apply。https://dev59.com/InM_5IYBdhLWcg3wcCrc - Paul Draper
@PaulDraper - 我知道.apply(),但它也会复制输入数组。关键是任何期望接受大量参数的接口都不应该声明需要将它们作为普通参数传递。更有效的方法是传递一个数组,这样就不需要复制数据了。 - jfriend00
6
@IngoBürk 这不是一个理论问题!即使您从未明确编写过这样的函数,如果您使用ES2015扩展运算符和像Math.maxArray.prototype.concat(...arrayOfArrays)这样的函数(用于平坦化数组),则可以动态地达到该限制,而无需编写庞大的函数。jfriend00提到的开销并不总是重要的-代码的清晰比几个字节的内存或CPU周期更重要,而扩展语法有时可能是在这方面最好的选择。请查看“平铺数组”的替代方案,以替换我的短代码... - Mörre
@IngoBürk 我理解这一点,我现在发表评论是因为它现在很相关。SO的答案和评论不仅适用于那些发布者 :-) - Mörre
显示剩余3条评论
8个回答

119

虽然规范中没有具体限制参数的理论最大数量(正如thefortheye的回答所指出的)。但是,当然存在实际限制。这些限制完全取决于实现,并且很可能还取决于您调用函数的方式。


我创建了 这个 fiddle 作为一个实验。

function testArgs() {
    console.log(arguments.length);
}

var argLen = 0;
for (var i = 1; i < 32; i++) {
    argLen = (argLen << 1) + 1;
    testArgs.apply(null, new Array(argLen));
}

以下是我的测试结果:

  • Chrome 33.0.1750.154 m:最后一次成功的测试是65535个参数。之后失败了,错误提示为:

    Uncaught RangeError: Maximum call stack size exceeded

  • Firefox 27.0.1:最后一次成功的测试是262,143个参数。之后失败了,错误提示为:

    RangeError: arguments array passed to Function.prototype.apply is too large

  • Internet Explorer 11:最后一次成功的测试是131,071个参数。之后失败了,错误提示为:

    RangeError: SCRIPT28: Out of stack space

  • Opera 12.17:最后一次成功的测试是1,048,576个参数。之后失败了,错误提示为:

    Error: Function.prototype.apply: argArray is too large

当然,这里可能还有其他因素影响测试结果,你的情况可能不同。


这里有一个使用 eval 创建的替代 fiddle。同样的,你可能会得到不同的结果。

Chrome 33.0.1750.154 m: 最后一个成功的测试是32,767个参数。之后它失败了,并显示以下消息:

Uncaught SyntaxError:函数调用中的参数过多(仅允许32766个)

这个特别有趣,因为Chrome本身似乎对实际允许的参数数量感到困惑。

Firefox 27.0.1:最后一个成功的测试是32,767个参数。之后它失败了,并显示以下消息:

脚本太大

Internet Explorer 11:最后一个成功的测试是32,767个参数。之后它失败了,并显示以下消息:

RangeError:SCRIPT7:内存不足

Opera 12.17:最后一个成功的测试是4,194,303个参数。之后它失败了,并显示以下消息:

内存不足;脚本已终止。


这很棒。虽然如果您正在使用apply并将参数作为Array发送,则根据规范的理论最大值将始终为4,294,967,295个元素,无论实际限制是什么(?)。 - GladstoneKeep
2
你的示例测试了.apply()的最大值,而不是像问题中所显示的传递函数参数。它们不一定相同。这里有一个演示,我使用Function构造函数从字符串创建了一个函数。该函数调用了你的testArgs()函数,并传递了100,000个参数。仅仅创建该函数就会失败,报错为SyntaxError: too many function arguments - cookie monster
@cookiemonster 你说得对。有许多不同的因素可能限制最大数量,而调用它的不同方法可能会产生不同的结果。我想我应该更加强调第一句话。 - p.s.w.g
是的,这完全取决于OP的想法。我无法想象要为一个函数键入100,000个参数,所以.apply()似乎更有可能。但他没有提到.apply(),所以也许他计划做一些eval操作。 - cookie monster
@GladstoneKeep 正确。我的测试是为了找出在特定浏览器中的近似实际极限。 - p.s.w.g

25

ECMAScript 5.1,第8.8部分

List类型用于解释new表达式、函数调用和其他需要简单值列表的算法中的参数列表求值(参见11.2.4)......这些序列可以是任意长度。

所以标准没有限制。当然,如果你的代码在现实世界中运行,而不是在标准的世界中,显然存在一些限制(例如宇宙中的粒子数量)。

有两种方法可以将参数传递给函数。

  • “字面量”: f(a, b, c)
  • 使用apply(): f.apply(null, [a, b, c])

对于大型参数列表,后者是更现实的情况。

请访问JSFiddle查看当前浏览器的每个选项的限制。

我自己试过几个浏览器:

            | apply() | literal
 -----------------------------
Chrome 14   | 131155  |  32767
Chrome 35   | 126213  |  65535
Chrome 106  | 125625  |  62803
Firefox 30  | 500001  |  65535
Firefox 106 | 500001  |  65535
IE 9        | 254335  |  65533
IE 10       | 253667  |  65533
IE 11       | 252447  |  65533
Opera 22    | 126063  |  65535
Safari 4    | 524215  | 524205
Safari 7    |  65537  | 522159

我在不同的机器上看到了这些数字之间的差异,因此我相信除了浏览器(操作系统?)之外还有其他因素在起作用。


这是一个真正的问题,而不仅仅是一些琐事。 一个现实世界的例子:

Google Closure Library 定义了以下函数。

goog.crypt.byteArrayToString = function(array) {
  return String.fromCharCode.apply(null, array);
};

只有当字符串长度在浏览器限制的参数数量范围内时,此方法才有效。使用Function.prototype.apply函数传递参数的数量也要在该限制范围内。

后来对该函数进行了修补,使得该函数变得更加复杂。


顺便提一下,2012年3月份提交的Webkit issue中讨论了参数限制问题。


7
根据ECMA Script 5.1标准规范List

List类型用于解释在新表达式、函数调用和其他需要简单值列表的算法中对参数列表(见11.2.4)的求值。 List类型的值只是有序的值序列。这些序列可以是任意长度。

因此,该标准没有任何关于参数数量的限制,仅受内存限制。

3

电脑无知
> 来自 Computer Stupidities

就我而言,限制是“足够大”的!


7
限制是“足够大”! 除非它不是。 https://bugs.webkit.org/show_bug.cgi?id=80797 - Paul Draper
这个回答会让下面 p.s.w.g 的回答变得不那么明显。当你动态地提供参数时,“足够大”的描述并没有什么帮助,例如 String.fromCharCode(...big_array_of_char_codes) - diachedelic

1

这完全取决于客户端的性能。

如果要传递数百万个参数,浏览器将失去其内存。

每个变量都占用一定的内存。因此,浏览器分配给每个变量一些内存是没有问题的,直到它自己没有可运行的内存为止。


1
这是我检查它的方法。
let args = [];

while(true){
    try{
        (() => {})(...args);
        args.push(0);
    }catch(e){
        console.log(e.message);
        console.log(`Number of arguments: ${args.length}`);
        break;
    }
}

1

只需将一个 map (就是一个对象) 作为单个参数使用。您仍然可以使用参数名称,并且可以拥有任意数量的参数。它保证您可以传递理论上无限数量的参数,但显然仍然受堆空间的限制。

let infiniteParameters = (w) => console.table(w);

infiniteParameters({
  name1 : "value1",
  name2 : "value2",
  // ...
});

这并没有回答问题。问题是“JavaScript函数可以接受的最大参数数量是多少?”而不是“我如何将理论上无限数量的参数传递给JavaScript函数?” - Matthew W.
@MatthewW。作为问题被问到的可能原因,它仍然是一个有用的答案。它还可以帮助其他可能正在寻找这种解决方案的人。 - Jacob Riches

-1

3
该部分已被删除。也许是不正确的。 - Drenai

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