我如何转换&;参数&;对象到一个数组在JavaScript?

arguments对象在JavaScript中是一个奇怪的东西——它在大多数情况下就像一个数组,但它不是实际上数组对象。由于它是完全是另一回事,它没有来自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数组函数,就必须这么做,这是一件相当可怜的事情。是否有一种内置的方法来使用标准库?

243912 次浏览

ES6使用rest参数

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

Rest Parameters . sh . sh

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


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

正如你可以在链接中读到的

rest形参语法允许我们将不确定数量的实参表示为数组。

如果你对...语法感到好奇,它被称为传播算子,你可以阅读更多在这里

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简单地将类数组或可迭代对象转换为数组实例。



ES5

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

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

为什么会这样?好吧,下面是ECMAScript 5文档本身的摘录:

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

因此,slice作用于任何具有length属性的对象,而arguments恰好可以做到这一点。


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

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

然而,我倾向于认为前一个版本更明确,所以我更喜欢它。滥用数组文字符号会让人觉得很俗气,看起来很奇怪。

如果你正在使用jQuery,在我看来,下面的代码更容易记住:

function sortArgs(){
return $.makeArray(arguments).sort();
}
function sortArgs(){ return [].slice.call(arguments).sort() }


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

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

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

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

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

这是基准的几个方法转换参数为数组。

对我来说,对于少量的争论,最好的解决办法是:

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(); }

使用:

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();
}

引用这个蓝鸟承诺图书馆wiki页面也是值得的,它展示了如何将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);。作者还展示了构建步骤如何帮助减少冗余。

另一个答案。

使用黑魔法咒语:

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

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

本什马克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

享受吧!

在ECMAScript 6中,没有必要使用像Array.prototype.slice()这样丑陋的hack。你可以使用扩展语法(...)代替。

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

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

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

注意,它在一些较老的浏览器(如IE 11)中不起作用,所以如果你想支持这些浏览器,你应该使用巴别塔

我建议使用ECMAScript 6 传播算子,它会将尾随参数绑定到数组。有了这个解决方案,你不需要触及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();
}

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

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

注意,它在一些较老的浏览器(如IE 11)中不起作用,所以如果你想支持这些浏览器,你应该使用巴别塔

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


document.body.innerHTML = sortArgs(1, 2, 3, 4).toString();

尝试使用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()

解释:创建object,将object的属性设置为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]]]))

虽然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()
 function x(){
var rest = [...arguments]; console.log(rest);return
rest.constructor;
};
x(1,2,3)

我尝试了简单的破坏技巧

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));

Lodash:

var args = _.toArray(arguments);

在行动:

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

生产:

[2,3]

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对象。

有很多方法可以访问函数体中的arguments对象,包括:

  1. 你可以调用array . prototype .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. 你可以用spread[…]

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));

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

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或循环参数并将它们分配给一个新数组…

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

function argsToArray() {
return Object.values(arguments);
}


// example usage
console.log(
argsToArray(1, 2, 3, 4, 5)
.map(arg => arg*11)
);

Object.values( )将以数组的形式返回对象的值,由于arguments是一个对象,它实际上会将arguments转换为一个数组,从而为你提供数组的所有帮助函数,如mapforEachfilter等。