JavaScript可变参数个数


531

有没有办法允许JavaScript中的函数使用“无限”的var?

例:

load(var1, var2, var3, var4, var5, etc...)
load(var1)



1
@卢克不,不是。该问题询问如何使用数组中的参数调用具有任意数量的参数的函数。这询问如何处理这样的呼叫。
S脚的'17

1
为了便于搜索,此函数称为“可变函数”。
gschenk

Answers:


814

当然,只需使用arguments对象即可。

function foo() {
  for (var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);
  }
}

1
Tnx。这对于将字符串从android本机代码解析为Webview中的javascript非常有用。
2013年

4
此解决方案最适合我。谢谢。有关参数关键字HERE的更多信息。
User2 2014年

26
arguments是一个特殊的“类数组”对象,这意味着它具有长度,但是没有其他数组函数。有关更多信息,请参见developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/…,此答案:stackoverflow.com/a/13145228/1766230
Luke

4
有趣的是,Javascript中的Array方法已经定义为可以在具有length属性的任何对象上使用。这包括arguments对象。知道了这一点,并且该方法concat返回被调用的“数组” 的副本,我们可以轻松地将arguments对象转换为真正的数组,如下所示:var args = [].concat.call(arguments)。有人喜欢Array.prototype.concat.call代替使用,但我喜欢[],它们又短又甜!
Stijn de Witt

2
@YasirJan使用[...arguments].join()
Willian D. Andrade

178

在(大多数)最新的浏览器中,您可以使用以下语法接受数量可变的参数:

function my_log(...args) {
     // args is an Array
     console.log(args);
     // You can pass this array as parameters to another function
     console.log(...args);
}

这是一个小例子:

function foo(x, ...args) {
  console.log(x, args, ...args, arguments);
}

foo('a', 'b', 'c', z='d')

=>

a
Array(3) [ "b", "c", "d" ]
b c d
Arguments
    0: "a"
    1: "b"
    2: "c"
    3: "d"
    length: 4

文档和更多示例在这里:https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters


27
仅供参考,它被称为“其余参数语法”:developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/…–
tanguy_k

4
+1这是一种优雅干净的解决方案。特别适用于将一长串参数传递到另一个函数调用中,并且这些变量参数可能位于任意位置。
haxpor


2
下表显示了浏览器支持-caniuse.com/#feat=rest-parameters
Brian Burns

1
这么好的答案!
Ashish

113

另一种选择是在上下文对象中传递参数。

function load(context)
{
    // do whatever with context.name, context.address, etc
}

像这样使用

load({name:'Ken',address:'secret',unused:true})

这样做的好处是,您可以根据需要添加任意数量的命名参数,并且函数可以根据需要使用(或不使用)它们。


4
这样会更好,因为它消除了参数顺序的耦合。松散耦合的接口是一种良好的标准做法……
Jonas Schubert Erlandsson

9
当然,在某些情况下会更好。但是,可以说各个参数之间并没有真正的联系,或者所有参数都具有相同的含义(例如数组元素)。那么OP的方法是最好的。
rvighne

1
这也很好,因为如果需要,您可以context使用代码构建参数,并在使用参数之前将其传递。
Nate CK 2014年

46

我同意Ken的回答是最有活力的,并且我想更进一步。如果您使用不同的参数多次调用该函数-我使用Ken的设计,但随后添加了默认值:

function load(context) {

    var defaults = {
        parameter1: defaultValue1,
        parameter2: defaultValue2,
        ...
    };

    var context = extend(defaults, context);

    // do stuff
}

这样,如果您有许多参数,但不必每次调用该函数都进行设置,则只需指定非默认值即可。对于extend方法,您可以使用jQuery的extend方法($.extend()),自己编写或使用以下方法:

function extend() {
    for (var i = 1; i < arguments.length; i++)
        for (var key in arguments[i])
            if (arguments[i].hasOwnProperty(key))
                arguments[0][key] = arguments[i][key];
    return arguments[0];
}

这将合并上下文对象和默认值,并使用默认值填充对象中所有未定义的值。


3
+1。好招 节省很多样板来定义每个参数,默认还是其他。
尼尔

1
Underscore的 _.defaults()方法是合并指定参数和默认参数的很好的替代方法。
mbeasley

18

是的,就像这样:

function load()
{
  var var0 = arguments[0];
  var var1 = arguments[1];
}

load(1,2);

18

如Ramast所指出的,最好使用rest参数语法。

function (a, b, ...args) {}

我只想添加... args参数的一些不错的属性

  1. 它是一个数组,而不是类似参数的对象。这使您可以应用地图或直接排序等功能。
  2. 它不包括所有参数,而仅包括从其传递过来的参数。例如,函数(a,b,... args)在这种情况下args包含arguments.length的参数3

15

如前所述,您可以使用 arguments对象检索可变数量的功能参数。

如果要使用相同的参数调用另一个函数,请使用apply。您甚至可以通过转换arguments为数组来添加或删除参数。例如,此函数在登录到控制台之前插入一些文本:

log() {
    let args = Array.prototype.slice.call(arguments);
    args = ['MyObjectName', this.id_].concat(args);
    console.log.apply(console, args);
}

将参数转换为数组的好方法。今天对我有帮助。
Dmytro Medvid '16

8

尽管我通常都同意命名实参方法是有用且灵活的(除非您关心顺序,否则实参最简单),但我确实担心mbeasley方法的成本(使用默认值和扩展)。拉取默认值要花费极高的成本。首先,默认值是在函数内部定义的,因此每次调用都会重新填充它们。其次,您可以轻松地读出命名值并使用||同时设置默认值。无需创建和合并另一个新对象即可获取此信息。

function load(context) {
   var parameter1 = context.parameter1 || defaultValue1,
       parameter2 = context.parameter2 || defaultValue2;

   // do stuff
}

这几乎是相同数量的代码(可能略多一些),但应该只是运行时成本的一小部分。


同意,尽管危害取决于值的类型或默认值本身。否则,(parameter1=context.parameter1)===undefined && (parameter1=defaultValue1)或者为减少代码量,提供了一个小辅助函数,例如:function def(providedValue, default) {return providedValue !== undefined ? providedValue : defaultValue;} var parameter1 = def(context.parameter1, defaultValue1)提供备用模式。但是,我的观点仍然是:为每个函数调用创建额外的对象并运行昂贵的循环以设置几个默认值是非常大的开销。
mcurland 2014年

7

虽然@roufamatic确实显示了arguments关键字的使用,而@Ken显示了使用对象的一个​​很好的例子,但我觉得既没有真正解决这种情况下发生的事情,也可能使未来的读者感到困惑或灌输一种错误的做法,因为未明确说明功能/ method旨在采用可变数量的参数/参数。

function varyArg () {
    return arguments[0] + arguments[1];
}

当另一个开发人员浏览您的代码时,很容易假设此函数不带参数。尤其是如果该开发人员不擅长使用arguments关键字。因此,遵循样式指南并保持一致是一个好主意。我将在所有示例中使用Google的。

让我们明确声明相同的函数具有可变参数:

function varyArg (var_args) {
    return arguments[0] + arguments[1];
}

对象参数VS var_args

有时可能需要一个对象,因为它是数据映射表中唯一被批准并认为是最佳实践的方法。联想数组不赞成使用,不鼓励使用。

旁白: arguments关键字实际上使用数字作为键返回一个对象。原型继承也是对象家族。请参阅答案结尾以了解JS中正确的数组用法

在这种情况下,我们也可以明确声明这一点。注意:此命名约定不是Google提供的,而是一个显式声明参数类型的示例。如果要在代码中创建更严格的类型化模式,这一点很重要。

function varyArg (args_obj) {
    return args_obj.name+" "+args_obj.weight;
}
varyArg({name: "Brian", weight: 150});

选择哪一个?

这取决于您的功能和程序的需求。例如,如果您只是想在所有传递的参数上基于迭代过程返回一个值,那么肯定可以使用arguments关键字。如果您需要定义参数和数据映射,那么对象方法就是您的理想之选。让我们看两个示例,然后完成!

参数用法

function sumOfAll (var_args) {
    return arguments.reduce(function(a, b) {
        return a + b;
    }, 0);
}
sumOfAll(1,2,3); // returns 6

对象使用

function myObjArgs(args_obj) {
    // MAKE SURE ARGUMENT IS AN OBJECT OR ELSE RETURN
    if (typeof args_obj !== "object") {
        return "Arguments passed must be in object form!";
    }

    return "Hello "+args_obj.name+" I see you're "+args_obj.age+" years old.";
}
myObjArgs({name: "Brian", age: 31}); // returns 'Hello Brian I see you're 31 years old

访问数组而不是对象(“ ... args” rest参数)

如答案顶部所述,arguments关键字实际上返回一个对象。因此,必须调用要用于数组的任何方法。一个例子:

Array.prototype.map.call(arguments, function (val, idx, arr) {});

为了避免这种情况,请使用rest参数:

function varyArgArr (...var_args) {
    return var_args.sort();
}
varyArgArr(5,1,3); // returns 1, 3, 5

5

arguments在函数内部使用该对象可以访问传入的所有参数。


3

请注意,按照Ken的建议传递带有命名属性的Object会增加分配和释放临时对象到每个调用的成本。通过值或引用传递普通参数通常是最有效的。对于许多应用程序而言,性能并不关键,但对于某些应用程序则可能至关重要。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.