【问题标题】:How can I convert the "arguments" object to an array in JavaScript?如何将“参数”对象转换为 JavaScript 中的数组?
【发布时间】:2010-10-31 22:40:24
【问题描述】:

JavaScript 中的arguments 对象是一个奇怪的缺陷——它在大多数情况下就像一个数组,但它实际上不是一个数组对象。由于它是really something else entirely,它没有Array.prototype 中的有用功能,如forEachsortfiltermap

使用简单的 for 循环从 arguments 对象构造一个新数组非常容易。例如,此函数对其参数进行排序:

function sortArgs() {
    var args = [];
    for (var i = 0; i < arguments.length; i++)
        args[i] = arguments[i];
    return args.sort();
}

然而,仅仅为了访问极其有用的 JavaScript 数组函数而不得不这样做是一件相当可怜的事情。有没有使用标准库的内置方法?

【问题讨论】:

标签: javascript arrays sorting arguments variadic-functions


【解决方案1】:

ES6 使用剩余参数

如果你会使用 ES6,你可以使用:

Rest Parameters

function sortArgs(...args) {
  return args.sort(function (a, b) { return a - b; });
}

document.body.innerHTML = sortArgs(12, 4, 6, 8).toString();

正如您在link 中看到的那样

其余参数语法允许我们将不定数量的参数表示为一个数组。

如果您对... 语法感到好奇,它被称为Spread Operator,您可以阅读更多here

ES6 使用 Array.from()

使用 Array.from

function sortArgs() {
  return Array.from(arguments).sort(function (a, b) { return a - b; });
}

document.body.innerHTML = sortArgs(12, 4, 6, 8).toString();

Array.from 只需将类 Array 或 Iterable 对象转换为 Array 实例。



ES5

您实际上可以在参数对象上使用Arrayslice 函数,它会将其转换为标准的JavaScript 数组。您只需通过 Array 的原型手动引用它:

function sortArgs() {
    var args = Array.prototype.slice.call(arguments);
    return args.sort();
}

为什么会这样?好吧,这是ECMAScript 5 documentation itself的摘录:

注意slice 函数是故意通用的;它不要求其 this 值是 Array 对象。因此,它可以转移到其他类型的对象中用作方法。 slice 函数能否成功应用于宿主对象取决于实现。

因此,slice 适用于任何具有 length 属性的东西,arguments 很方便。


如果Array.prototype.slice 对你来说太多了,你可以使用数组文字稍微缩写它:

var args = [].slice.call(arguments);

但是,我倾向于觉得以前的版本更明确,所以我更喜欢它。滥用数组字面量表示法会让人觉得很奇怪,而且看起来很奇怪。

【讨论】:

  • 在最近的 Firefox 版本(2.0?)和 ECMAScript 5 中,有一个 Array.slice 方法可以让这更简单一些。你可以这样称呼它:var arge = Array.slice(arguments, 0);.
  • 0 有什么用?既然没有要传递的参数,为什么不能只使用 var args = Array.prototype.slice.call(arguments); ? [ MDC 参数页面 ](developer.mozilla.org/en/JavaScript/Reference/…) 建议没有0 的方法。
  • @Barney - 排序是必要的,因为原始问题想要对参数进行排序。如果只是想转成数组,没必要。
  • “你不应该对参数进行切片,因为它会妨碍 JavaScript 引擎(例如 V8)中的优化。” - 来自 MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • 我不确定这是对原始问题的回答。时间过去了。随着 ES6 在 Firefox 和 Chrome 中的试验,rest 参数 正在成为一个不错的选择 - function sortArgs(...argsToSort) { return argsToSort.sort(); } 来自 MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
【解决方案2】:

还值得参考this Bluebird promises library wiki page,它展示了如何将arguments 对象管理到数组中,以使函数在V8 JavaScript 引擎下可优化

function doesntLeakArguments() {
    var args = new Array(arguments.length);
    for(var i = 0; i < args.length; ++i) {
        args[i] = arguments[i];
    }
    return args;
}

此方法用于支持var args = [].slice.call(arguments);。作者还展示了构建步骤如何帮助减少冗长。

【讨论】:

  • +1 感谢您提供指向 Bluebird Optimization Killer 页面的链接。 PS:我最近在node-thunkify 项目中看到了这种优化。
  • 对于 2021 年阅读的任何人,在链接页面上:“请不要将此处写的任何内容视为性能建议,这是出于历史原因。”
【解决方案3】:
function sortArgs(){ return [].slice.call(arguments).sort() }

// Returns the arguments object itself
function sortArgs(){ return [].sort.call(arguments) }

有些数组方法是故意不要求目标对象是一个实际的数组。它们只要求目标具有名为 length 的属性和索引(必须是零或更大的整数)。

[].sort.call({0:1, 1:0, length:2}) // => ({0:0, 1:1, length:2})

【讨论】:

    【解决方案4】:

    用途:

    function sortArguments() {
      return arguments.length === 1 ? [arguments[0]] :
                     Array.apply(null, arguments).sort();
    }
    

    Array(arg1, arg2, ...) 返回[arg1, arg2, ...]

    Array(str1) 返回[str1]

    Array(num1) 返回一个包含num1 元素的数组

    您必须检查参数的数量!

    Array.slice 版本(较慢):

    function sortArguments() {
      return Array.prototype.slice.call(arguments).sort();
    }
    

    Array.push 版本(比 slice 慢,快):

    function sortArguments() {
      var args = [];
      Array.prototype.push.apply(args, arguments);
      return args.sort();
    }
    

    移动版(速度较慢,但​​小尺寸更快):

    function sortArguments() {
      var args = [];
      for (var i = 0; i < arguments.length; ++i)
        args[i] = arguments[i];
      return args.sort();
    }
    

    Array.concat 版本(最慢):

    function sortArguments() {
      return Array.prototype.concat.apply([], arguments).sort();
    }
    

    【讨论】:

    • 请注意,由于 Array.concat 中的扁平化行为,它将接受数组参数。 sortArguments(2,[3,0],1) 返回 0,1,2,3。
    【解决方案5】:

    如果您使用的是 jQuery,我认为以下内容更容易记住:

    function sortArgs(){
      return $.makeArray(arguments).sort();
    }
    

    【讨论】:

    • 这基本上是我在纯 JS 中寻找的东西,但 +1 是 jQuery 如何让 JS 更容易理解的可靠示例。
    • “除非还包含框架/库的另一个标签,否则需要纯 JavaScript 答案。”
    【解决方案6】:

    在 ECMAScript 6 中,无需使用像 Array.prototype.slice() 这样的丑陋黑客。你可以改用spread syntax (...)

    (function() {
      console.log([...arguments]);
    }(1, 2, 3))

    它可能看起来很奇怪,但它相当简单。它只是提取arguments' 元素并将它们放回数组中。如果你还是不明白,看这个例子:

    console.log([1, ...[2, 3], 4]);
    console.log([...[1, 2, 3]]);
    console.log([...[...[...[1]]]]);

    注意它在IE 11等一些旧版浏览器中不起作用,所以如果你想支持这些浏览器,你应该使用Babel

    【讨论】:

      【解决方案7】:

      Here is benchmark 的几种将参数转换为数组的方法。

      就我而言,少量参数的最佳解决方案是:

      function sortArgs (){
        var q = [];
        for (var k = 0, l = arguments.length; k < l; k++){
          q[k] = arguments[k];
        }
        return q.sort();
      }
      

      对于其他情况:

      function sortArgs (){ return Array.apply(null, arguments).sort(); }
      

      【讨论】:

      • +1 用于基准测试。目前,图表显示您的sortArgs 在 Firefox 中的速度比 Array.apply 快 6 倍,但在最新的 Chrome 中慢两倍。
      • 由于 Array.prototype.slice 已被弃用,因为它阻止了 JS V8 引擎优化,我认为这是最好的解决方案,直到 ES6 被广泛使用 +1
      • 此外,Array.slice() 等数组泛型方法也已弃用
      【解决方案8】:

      我建议使用 ECMAScript 6 spread operator,它将尾随参数绑定到数组。使用此解决方案,您无需触摸arguments 对象,您的代码将得到简化。该解决方案的缺点是它不适用于大多数浏览器,因此您必须使用 JS 编译器,例如 Babel。在底层,Babel 将 arguments 转换为一个带有 for 循环的数组。

      function sortArgs(...args) {
        return args.sort();
      }
      

      如果您不能使用 ECMAScript 6,我建议您查看其他一些答案,例如 @Jonathan Fingland

      function sortArgs() {
          var args = Array.prototype.slice.call(arguments);
          return args.sort();
      }
      

      【讨论】:

        【解决方案9】:

        罗达什:

        var args = _.toArray(arguments);
        

        在行动:

        (function(){ console.log(_.toArray(arguments).splice(1)); })(1, 2, 3)
        

        产生:

        [2,3]
        

        【讨论】:

        • 为什么不_.toArray
        • 但是_.toArray更合适。
        • 我认为 _.slice 更合适,因为您经常想放弃一些主要论点。
        【解决方案10】:

        使用Array.from(),它将一个类数组对象(如arguments)作为参数并将其转换为数组:

        (function() {
          console.log(Array.from(arguments));
        }(1, 2, 3));

        注意它在IE 11等一些旧版浏览器中无法使用,所以如果你想支持这些浏览器,你应该使用Babel

        【讨论】:

          【解决方案11】:
          function sortArg(){
          var args = Array.from(arguments); return args.sort();
          }
          

           function sortArg(){
              var args = Array.from(arguments); 
              return args.sort();
              }
           
           console.log(sortArg('a', 'b', 1, 2, '34', 88, 20, '19', 39, 'd', 'z', 'ak', 'bu', 90));

          【讨论】:

            【解决方案12】:

            这是一个简洁明了的解决方案:

            function argsToArray() {
              return Object.values(arguments);
            }
            
            // example usage
            console.log(
              argsToArray(1, 2, 3, 4, 5)
              .map(arg => arg*11)
            );

            Object.values( ) 会将对象的值作为数组返回,由于arguments 是一个对象,它本质上会将arguments 转换为数组,从而为您提供数组的所有辅助函数,例如@ 987654326@、forEachfilter

            【讨论】:

              【解决方案13】:

              另一个答案。

              使用黑魔法:

              function sortArguments() {
                arguments.__proto__ = Array.prototype;
                return arguments.slice().sort();
              }
              

              Firefox、Chrome、Node.js、IE11 都可以。

              【讨论】:

              • 这绝对是最好的解决方案......如果你想让你的代码完全不可读。此外,__proto__ 已被弃用。见MDN docs
              【解决方案14】:

              尝试使用Object.setPrototypeOf()

              说明:将argumentsprototype设置为Array.prototype

              function toArray() {
                return Object.setPrototypeOf(arguments, Array.prototype)
              } 
              
              console.log(toArray("abc", 123, {def:456}, [0,[7,[14]]]))

              说明:取arguments的每个索引,将item放入数组中对应的数组索引处。

              也可以使用Array.prototype.map()

              function toArray() {
                return [].map.call(arguments, (_,k,a) => a[k])
              } 
              
              console.log(toArray("abc", 123, {def:456}, [0,[7,[14]]]))

              说明:取arguments的每个索引,将item放入数组中对应的数组索引处。

              for..of循环

              function toArray() {
               let arr = []; for (let prop of arguments) arr.push(prop); return arr
              } 
              
              console.log(toArray("abc", 123, {def:456}, [0,[7,[14]]]))

              Object.create()

              说明:创建对象,将对象的属性设置为arguments每个索引处的项;将创建对象的prototype 设置为Array.prototype

              function toArray() {
                var obj = {};
                for (var prop in arguments) {
                  obj[prop] = {
                    value: arguments[prop],
                    writable: true,
                    enumerable: true,
                    configurable: true
                  }
                } 
                return Object.create(Array.prototype, obj);
              }
              
              console.log(toArray("abc", 123, {def: 456}, [0, [7, [14]]]))

              【讨论】:

              • 你一直asking people你的答案是否满足“长答案”问题下的警告。在我看来,您回答的主要问题是您没有解释为什么应该使用您在此处显示的任何方法。我不可能使用您提出的任何解决方案,因为它们都比公认答案中的解决方案更糟糕。例如,您对Object.setPrototypeOf 文档的引用警告说这是一个“非常缓慢的操作”。我到底为什么要在Array.prototype.slice.call 上使用它?
              • @Louis 更新了答案,并对每种方法进行了解释。 OP 的问题似乎是 “是否有使用标准库的内置方法?” ,而不是 “为什么应该使用任何方法”是内置的吗?发布答案的任何用户如何准确确定发布的解决方案对于 OP 是否“正确”?似乎这取决于 OP 来确定?实际上,这部分通知是这个用户感兴趣的:“解释为什么你的答案是正确的”。提供给这个问题的任何答案是否表明:“这个答案是“正确的”,因为:x,y,z”?
              • 我解释的是 SO 通常是如何工作的。 OP 是否想知道为什么一种解决方案比另一种更好是完全不相关的,因为为 SO 发布的问题主要不是针对 OP,而是针对将要阅读的成千上万的人问题及其答案稍后。此外,对答案进行投票的人无需根据 OP 可能满意的内容进行投票。
              【解决方案15】:

              Benshmarck 3 方法:

              function test()
              {
                console.log(arguments.length + ' Argument(s)');
              
                var i = 0;
                var loop = 1000000;
                var t = Date.now();
                while(i < loop)
                {
                    Array.prototype.slice.call(arguments, 0); 
                    i++;
                }
                console.log(Date.now() - t);
              
              
                i = 0,
                t = Date.now();
                while(i < loop)
                {
                    Array.apply(null, arguments);
                    i++;
                }
                console.log(Date.now() - t);
              
                i = 0,
                t = Date.now();
                while(i < loop)
                {
                    arguments.length == 1 ? [arguments[0]] : Array.apply(null, arguments);
                    i++;
                }
                console.log(Date.now() - t);
              }
              
              test();
              test(42);
              test(42, 44);
              test(42, 44, 88, 64, 10, 64, 700, 15615156, 4654, 9);
              test(42, 'truc', 44, '47', 454, 88, 64, '@ehuehe', 10, 64, 700, 15615156, 4654, 9,97,4,94,56,8,456,156,1,456,867,5,152489,74,5,48479,89,897,894,894,8989,489,489,4,489,488989,498498);
              

              结果?

              0 Argument(s)
              256
              329
              332
              1 Argument(s)
              307
              418
              4
              2 Argument(s)
              375
              364
              367
              10 Argument(s)
              962
              601
              604
              40 Argument(s)
              3095
              1264
              1260
              

              享受吧!

              【讨论】:

              • 建议修改为arguments.length == 1?[arguments[0]]:Array.apply(null, arguments); 之类的内容,以防止仅使用一个整数参数dummyFn(3) 调用该函数并导致结果[,,]
              • @nevermind 干得好,我编辑,你的版本更好;)但我不明白dummyFn(3) 这是什么?这个函数不存在...
              • 呃...没关系,只是一个例子想展示一个函数如何只用一个整数参数调用并导致一个固定大小的空数组,对不起我的英语。
              【解决方案16】:

              function sortArgs(...args) {
                return args.sort(function (a, b) { return a - b; });
              }
              
              document.body.innerHTML = sortArgs(1, 2, 3, 4).toString();

              【讨论】:

                【解决方案17】:

                虽然rest参数效果很好,但是如果你出于某种原因想继续使用arguments,可以考虑

                function sortArgs() {
                  return [...arguments].sort()
                }
                

                [...arguments] 可以被认为是Array.from(arguments) 的一种替代方案,它也非常有效。

                ES7 的替代方案是数组推导:

                [for (i of arguments) i].sort()
                

                如果您想在排序之前处理或过滤参数,这可能是最简单的:

                [for (i of arguments) if (i % 2) Math.log(i)].sort()
                

                【讨论】:

                  【解决方案18】:
                   function x(){
                     var rest = [...arguments]; console.log(rest);return     
                     rest.constructor;
                   };
                   x(1,2,3)
                  

                  我尝试了简单的破坏技术

                  【讨论】:

                    【解决方案19】:

                    Arguments 对象仅在函数体内可用。尽管您可以像数组一样索引 Arguments 对象,但它不是数组。除了长度之外,它没有任何数组属性。

                    // function arguments length 5
                    function properties(a,b,c,d,e){
                    var function_name= arguments.callee.name
                    var arguments_length= arguments.length;
                    var properties_length=  properties.length; 
                    var function_from= properties.caller.name;
                    console.log('I am the function name: '+ function_name);
                    console.log('I am the function length, I am function spacific: '+ properties_length); 
                    console.log('I am the arguments length, I am context/excution spacific: '+ arguments_length);
                    console.log('I am being called From: '+  function_from );
                    }
                    
                    // arguments 3
                    function parent(){
                    properties(1,2,3);
                    }
                    
                    //arguments length 3 because execution spacific
                    parent();

                    虽然它可以像数组一样被索引,如您在此示例中所见:

                    function add(){
                    
                    var sum=0;
                    
                    for(var i=0; i< arguments.length;i++){
                    
                    sum = sum + arguments[i];
                    
                    }
                    
                    return sum;
                    
                    }
                    
                    console.log(add(1,2,3));

                    但是,Arguments 对象不是数组,除了长度之外没有任何其他属性。

                    您可以将 arguments 对象转换为数组,此时您可以访问 Arguments 对象。

                    您可以通过多种方式访问​​函数体内的参数对象,其中包括:

                    1. 您可以调用 Array.prototoype.slice.call 方法。

                    Array.prototype.slice.call(参数)

                    function giveMeArgs(arg1,arg2){
                    
                    var args = Array.prototype.slice.call(arguments);
                    return args
                    }
                    
                    console.log( giveMeArgs(1,2));
                    1. 你可以使用数组字面量

                    [].slice.call(参数).

                    function giveMeArgs(arg1,arg2){
                    
                    var args = [].slice.call(arguments);
                    
                    return args;
                    
                    }
                    
                    console.log( giveMeArgs(1,2) );
                    1. 您可以使用 Rest ...

                    function giveMeArgs(...args){
                    
                    return args;
                    
                    }
                    
                    console.log(giveMeArgs(1,2))
                    1. 您可以使用传播 [...]

                    function giveMeArgs(){
                    
                    var args = [...arguments];
                    return args;
                    
                    }
                    
                    console.log(giveMeArgs(1,2));
                    1. 你可以使用 Array.from()

                    function giveMeArgs(){
                    
                    var args = Array.from(arguments);
                    
                    return args;
                    
                    }
                    
                    console.log(giveMeArgs(1,2));

                    【讨论】:

                      【解决方案20】:

                      你可以创建一个可重用的函数来使用任何参数,最简单的是这样的:

                      function sortArgs() {
                        return [...arguments].sort();
                      }
                      
                      sortArgs('ali', 'reza', 1, 2, 'a'); //[1, 2, "a", "ali", "reza"];
                      

                      ES6及以上可以使用扩展语法...

                      但是如果你想使用兼容 ES5 及以下版本的东西,你可以使用Array.prototype.slice.call,所以你的代码看起来像这样:

                      function sortArgs() {
                        return Array.prototype.slice.call(arguments).sort();
                      }
                      
                      sortArgs('ali', 'reza', 1, 2, 'a'); //[1, 2, "a", "ali", "reza"];
                      

                      还有其他几种方法可以做到这一点,例如使用Array.from 或循环参数并将它们分配给新数组...

                      【讨论】:

                        【解决方案21】:

                        这是一个非常古老的问题,但我认为我有一个比以前的解决方案更容易键入并且不依赖外部库的解决方案:

                        function sortArguments() {
                          return Array.apply(null, arguments).sort();
                        }
                        

                        【讨论】:

                        • 如果您只有一个正整数参数,Array.apply 将不起作用。这是因为new Array(3) 创建了一个数组长度为3。更具体地说,结果将是 [undefined, undefined, undefined] 而不是 [3]
                        猜你喜欢
                        • 1970-01-01
                        • 2015-11-18
                        • 1970-01-01
                        • 2015-01-13
                        • 2011-04-21
                        • 2022-01-01
                        • 1970-01-01
                        • 2019-10-22
                        相关资源
                        最近更新 更多