是否可以向JavaScript函数发送可变数量的参数?


169

是否可以从数组向JavaScript函数发送可变数量的参数?

var arr = ['a','b','c']

var func = function()
{
    // debug 
    alert(arguments.length);
    //
    for(arg in arguments)
        alert(arg);
}

func('a','b','c','d'); // prints 4 which is what I want, then 'a','b','c','d'
func(arr); // prints 1, then 'Array'

我最近写了很多Python,能够接受并发送varargs是一个很棒的模式。例如

def func(*args):
   print len(args)
   for i in args:
       print i

func('a','b','c','d'); // prints 4 which is what I want, then 'a','b','c','d'
func(*arr) // prints 4 which is what I want, then 'a','b','c','d'

是否有可能在JavaScript发送待处理数组作为参数数组?


4
请注意,将for - in循环与arguments对象一起使用不是一个好主意- length而是应使用“正常”的循环遍历属性的方法
Yi Jiang

2
从来都不是问题,您能不能解释为什么会这样?arguments对象几乎总是很小,使用agruments [0..length-1]版本的性能几乎可以忽略不计。
火鸦


1
我认为参数不是实际的数组,而是一个特殊的对象,因此“ in”语法可能不起作用,具体取决于JS实现。
奥罗尼

Answers:


206

更新:从ES6开始,您可以在调用函数时简单地使用传播语法

func(...arr);

由于ES6还希望将参数视为数组,因此也可以在参数列表中使用传播语法,例如:

function func(...args) {
  args.forEach(arg => console.log(arg))
}

const values = ['a', 'b', 'c']
func(...values)
func(1, 2, 3)

并且可以将其与常规参数结合使用,例如,如果要分别接收前两个参数,而其余的则作为数组接收:

function func(first, second, ...theRest) {
  //...
}

也许对您有用,您可以知道一个函数需要多少个参数:

var test = function (one, two, three) {}; 
test.length == 3;

但是无论如何,您可以传递任意数量的参数...

apply与不需要this在函数调用中设置值的情况相比,spread语法更短,更“甜” ,这就是方法。

这是一个应用示例,它是以前的实现方法:

var arr = ['a','b','c'];

function func() {
  console.log(this); // 'test'
  console.log(arguments.length); // 3

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

};

func.apply('test', arr);

如今,我只建议apply仅在需要从数组传递任意数量的参数设置this值时使用。applytake是this值作为第一个参数,将在函数调用中使用,如果我们null在非严格代码中使用,则该this关键字将func在严格模式下(显式使用'use strict时)引用其中的Global对象(窗口)。或在ES模块null中使用。

还要注意,该arguments对象并不是真正的数组,可以通过以下方式将其转换:

var argsArray = Array.prototype.slice.call(arguments);

在ES6中:

const argsArray = [...arguments] // or Array.from(arguments)

但是arguments由于传播语法的原因,如今您很少直接使用该对象。


1
谢谢,套用了把戏,在这种情况下调用不起作用。那是写错了吗?
Fire Crow,

如果函数实际上是类的成员,该怎么办?这是正确的吗?theObject.member.apply(theObject,arguments);
Baxissimo

4
Array.prototype.slice.call(arguments);阻止V8之类的浏览器JS优化。这里的细节
Dheeraj Bhaskar 2014年

2
1.显然,arguments已计划不推荐使用此方法,以利于传播操作员。我没有消息来源,但是我在某处听到了。2. MDN指出slicearguments对象上使用会阻止对V8等引擎进行优化。他们建议使用“指定数组构造器” Array.from(arguments)[...arguments]。您是否愿意为ES2015更新答案?
Braden Best,

NB:我继续写了自己的答案
布雷登·贝斯特

75

实际上,您可以根据需要将任意多个值传递给任何javascript函数。显式命名的参数将获得前几个值,但所有参数将存储在arguments数组中。

要以“未打包”形式传递参数数组,可以像下面这样使用apply(cf Functional Javascript):

var otherFunc = function() {
   alert(arguments.length); // Outputs: 10
}

var myFunc = function() {
  alert(arguments.length); // Outputs: 10
  otherFunc.apply(this, arguments);
}
myFunc(1,2,3,4,5,6,7,8,9,10);

23

图示传播运营商ES6,使用Javascript的计划下一个版本的一部分。到目前为止,只有Firefox支持它们。此代码在FF16 +中有效:

var arr = ['quick', 'brown', 'lazy'];

var sprintf = function(str, ...args)
{
    for (arg of args) {
        str = str.replace(/%s/, arg);
    }
    return str;
}

sprintf.apply(null, ['The %s %s fox jumps over the %s dog.', ...arr]);
sprintf('The %s %s fox jumps over the %s dog.', 'slow', 'red', 'sleeping');

注意传播的awkard语法。通常的语法sprintf('The %s %s fox jumps over the %s dog.', ...arr);不被支持。您可以在此处找到ES6兼容性表

还请注意的使用for...of,这是ES6的另一项补充。使用for...in数组不是一个好主意


Firefox和Chrome现在支持传播的常规语法
用户

8

apply函数有两个参数。该对象this将被绑定到,参数将以数组表示。

some_func = function (a, b) { return b }
some_func.apply(obj, ["arguments", "are", "here"])
// "are"

8

截至ES2015,有两种方法。

arguments对象

该对象内置于函数中,并且适当地引用了函数的参数。但是,从技术上讲,它不是数组,因此无法对其执行典型的数组操作。建议的方法是使用Array.from或散布运算符从中创建一个数组。

我看过使用提及其他答案slice。不要那样做 它阻止了优化(来源:MDN)。

Array.from(arguments)
[...arguments]

但是,我认为这arguments是有问题的,因为它隐藏了函数接受作为输入的内容。一个arguments函数通常是这样写的:

function mean(){
    let args = [...arguments];
    return args.reduce((a,b)=>a+b) / args.length;
}

有时,函数标头的编写方式如下所示,以类似C的方式记录了参数:

function mean(/* ... */){ ... }

但这是罕见的。

至于为什么有问题,以C为例。C向后兼容古老的ANSI之前的K&R C语言。K&R C允许函数原型具有空参数列表。

int myFunction();
/* This function accepts unspecified parameters */

ANSI C为varargs...)提供了一种工具,并void指定了一个空的arguments-list。

int myFunction(void);
/* This function accepts no parameters */

当许多人期望函数接受零个参数时,会无意中使用unspecified参数列表(int myfunction();)声明函数。从技术上讲这是一个错误,因为该函数接受参数。任意数量。

varargsC语言中的适当函数采用以下形式:

int myFunction(int nargs, ...);

JavaScript实际上确实与此类似。

传播算子 / 剩余参数

实际上,我已经向您展示了传播算子。

...name

它非常通用,也可以在函数的参数列表(“剩余参数”)中使用,以很好地记录的方式指定varargs:

function mean(...args){
    return args.reduce((a,b)=>a+b) / args.length;
}

或作为lambda表达式:

((...args)=>args.reduce((a,b)=>a+b) / args.length)(1,2,3,4,5); // 3

我非常喜欢传播算子。这是干净的自记录文件。


2
感谢您提供最新的答案,该帖子早于ES 2015,所以我很高兴您填写了更多最新选项
Fire Crow

5

它称为splat运算符。您可以使用JavaScript在JavaScript中进行操作apply

var arr = ['a','b','c','d'];
var func = function() {
    // debug 
    console.log(arguments.length);
    console.log(arguments);
}
func('a','b','c','d'); // prints 4 which is what I want, then 'a','b','c','d'
func(arr); // prints 1, then 'Array'
func.apply(null, arr); 

4

使用ES6,您可以将rest参数用于varagrs。这将使用参数列表并将其转换为数组。

function logArgs(...args) {
    console.log(args.length)
    for(let arg of args) {
        console.log(arg)
    }
}

2

这是一个示例程序,用于计算变量参数的整数和和整数数组。希望这可以帮助。

var CalculateSum = function(){
    calculateSumService.apply( null, arguments );
}

var calculateSumService = function(){
    var sum = 0;

    if( arguments.length === 1){
        var args = arguments[0];

        for(var i = 0;i<args.length; i++){
           sum += args[i]; 
        }
    }else{
        for(var i = 0;i<arguments.length; i++){
           sum += arguments[i]; 
        }        
    }

     alert(sum);

}


//Sample method call

// CalculateSum(10,20,30);         

// CalculateSum([10,20,30,40,50]);

// CalculateSum(10,20);

1
一些注意事项:1. var声明一个function scope变量。new letconst关键字(ES 2015)声明block scope变量。但是,在ES 2015之前,根据实际情况,这些变量应该仍被提升到函数的顶部。2.因为你的错误接受的阵列作为一个参数的给定1的整数值时的功能中断(问题询问关于可变参数;不接受数组作为参数): CalculateSum(6) -> 0。(下一篇文章继续)
Braden Best,

1
3.请勿使用调试功能alert。实际上,只是不使用alert/prompt/confirm,句号。console.log调试时使用代替。4.该函数应该是return一个值,而不是alert它。5.避免PascalCase除非它涉及数据的“类型”。即一堂课。使用camelCaseunder_score代替。4.我不喜欢您声明函数的方式。var f = function(){ ... }表示您希望它在某个时候进行更改,这可能不是您的意图。请改用常规function name(){ ... }语法。
Braden Best

为了演示所有这些批评点,这里是您答案中函数的改进版本
Braden Best

2
(function(window) {
  var proxied = window.alert;
  window.alert = function() {
    return proxied.apply(window, arguments);
  };
}(this));

alert(13, 37);

1

对于那些在这里从将可变数量的参数从一个函数传递到另一个函数的人(不应其标记为该问题的重复):

如果您尝试将可变数量的参数从一个函数传递给另一个函数,那么从JavaScript 1.8.5开始,您可以简单地调用apply()第二个函数并传递arguments参数:

var caller = function()
{
    callee.apply( null, arguments );
}

var callee = function()
{
    alert( arguments.length );
}

caller( "Hello", "World!", 88 ); // Shows "3".

注意:第一个参数是this要使用的参数。传递null将从全局上下文中调用函数,即作为全局函数而不是某些对象的方法。

根据该文档,ECMAScript 5规范重新定义了该apply()方法以采用任何“通用数组状对象”,而不是严格使用数组。因此,您可以将arguments列表直接传递给第二个函数。

在Chrome 28.0,Safari 6.0.5和IE 10中进行了测试。使用此JSFiddle进行尝试。


0

是的,您可以传递变量号。函数的参数集。您可以使用它apply来实现。

例如:

var arr = ["Hi","How","are","you"];

function applyTest(){
    var arg = arguments.length;
    console.log(arg);
}

applyTest.apply(null,arr);

0

您是否希望函数对数组参数或变量参数做出反应?如果是后者,请尝试:

var func = function(...rest) {
  alert(rest.length);

  // In JS, don't use for..in with arrays
  // use for..of that consumes array's pre-defined iterator
  // or a more functional approach
  rest.forEach((v) => console.log(v));
};

但是如果您想处理数组参数

var fn = function(arr) {
  alert(arr.length);

  for(var i of arr) {
    console.log(i);
  }
};
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.