¿Cómo puedo convertir el objeto "argumentos" en una matriz en JavaScript?

432

El argumentsobjeto en JavaScript es una verruga extraña: actúa como una matriz en la mayoría de las situaciones, pero en realidad no es un objeto de matriz. Ya que es realmente algo completamente distinto , que no tiene las funciones útiles de Array.prototypecomo forEach, sort, filter, y map.

Es trivialmente fácil construir una nueva matriz a partir de un objeto de argumentos con un bucle for simple. Por ejemplo, esta función ordena sus argumentos:

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

Sin embargo, esto es algo lamentable que debe hacerse simplemente para obtener acceso a las funciones de matriz de JavaScript extremadamente útiles. ¿Hay una forma integrada de hacerlo usando la biblioteca estándar?

Andrew Coleson
fuente
estrechamente relacionado: ¿cómo funciona Array.prototype.slice.call ()?
Bergi

Respuestas:

724

ES6 utilizando parámetros de descanso

Si puede usar ES6, puede usar:

Parámetros de descanso

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

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

Como puedes leer en el enlace

La sintaxis del parámetro rest nos permite representar un número indefinido de argumentos como una matriz.

Si tiene curiosidad sobre la ...sintaxis, se llama Spread Operator y puede leer más aquí .

ES6 usando Array.from ()

Usando 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 simplemente convierta objetos Array o Iterable en instancias de Array.



ES5

En realidad, solo puede usar Arrayla slicefunción de un objeto de argumentos, y lo convertirá en una matriz estándar de JavaScript. Solo tendrá que hacer referencia a él manualmente a través del prototipo de Array:

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

¿Por qué funciona esto? Bueno, aquí hay un extracto de la documentación de ECMAScript 5 :

NOTA : La slicefunción es intencionalmente genérica; no requiere que este valor sea un objeto Array. Por lo tanto, se puede transferir a otros tipos de objetos para usarlos como método. Si la slicefunción se puede aplicar con éxito a un objeto host depende de la implementación.

Por lo tanto, slicefunciona en todo lo que tiene una lengthpropiedad, que argumentsconvenientemente tiene.


Si Array.prototype.slicees demasiado bocado para usted, puede abreviarlo ligeramente utilizando literales de matriz:

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

Sin embargo, tiendo a sentir que la versión anterior es más explícita, por lo que preferiría en su lugar. Abusar de la notación literal de matriz se siente hacky y se ve extraño.

Jonathan Fingland
fuente
55
En las versiones recientes de Firefox (2.0?) Y ECMAScript 5, hay un método Array.slice que lo hace un poco más simple. Lo llamaría así: var arge = Array.slice (argumentos, 0) ;.
Matthew Crumley
99
¿Qué es la 0de? Dado que no hay argumentos que desee pasar, ¿por qué no puede simplemente usar var args = Array.prototype.slice.call(arguments);? [ La página de argumentos de MDC ] ( developer.mozilla.org/en/JavaScript/Reference/… ) sugiere el método sin el 0.
Peter Ajtai
3
@Barney: el orden es necesario porque la pregunta original quiere ordenar los argumentos. Si solo desea convertirlo en una matriz, no es necesario.
Krease
17
"No debe dividir los argumentos porque evita las optimizaciones en los motores de JavaScript (V8, por ejemplo)". - de MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
frameworkninja
3
No estoy seguro de que eso responda a la pregunta original. El tiempo pasó. Con ES6 experimentado en Firefox y Chrome, el parámetro rest se está convirtiendo en una buena alternativa: function sortArgs(...argsToSort) { return argsToSort.sort(); }de MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Kir Kanos
40

También vale la pena hacer referencia a esta página wiki de la biblioteca Bluebird promete que muestra cómo administrar el argumentsobjeto en una matriz de manera que la función sea optimizable en el motor V8 JavaScript :

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

Este método se utiliza a favor de var args = [].slice.call(arguments);. El autor también muestra cómo un paso de compilación puede ayudar a reducir la verbosidad.

jbmusso
fuente
2
+1 y gracias por el enlace a la página de Bluebird Optimization Killer. PD: Recientemente he visto esta optimización utilizada en el proyecto node-thunkify .
Yves M.
29
function sortArgs(){ return [].slice.call(arguments).sort() }

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

Algunos métodos de matriz se hacen intencionalmente para no requerir que el objeto de destino sea una matriz real. Solo requieren que el destino tenga una propiedad denominada longitud e índices (que deben ser enteros cero o mayores).

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

Utilizar:

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

Array(arg1, arg2, ...) devoluciones [arg1, arg2, ...]

Array(str1) devoluciones [str1]

Array(num1) devuelve una matriz que tiene num1 elementos

¡Debe verificar la cantidad de argumentos!

Array.slice versión (más lenta):

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

Array.push versión (más lenta, más rápida que rebanada):

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

Mover versión (más lenta, pero el tamaño pequeño es más rápido):

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

Array.concat versión (más lenta):

function sortArguments() {
  return Array.prototype.concat.apply([], arguments).sort();
}
LightSpeedC Kazuaki Nishizawa
fuente
1
Tenga en cuenta que debido al comportamiento de aplanamiento en Array.concat, aceptará argumentos de matriz. sortArguments (2, [3,0], 1) devuelve 0,1,2,3.
Hafthor
9

Si está utilizando jQuery, lo siguiente es mucho más fácil de recordar en mi opinión:

function sortArgs(){
  return $.makeArray(arguments).sort();
}
puntilla
fuente
1
Eso es esencialmente lo que estaba buscando en JS puro, pero +1 para el ejemplo sólido de cómo jQuery hace que JS sea un poco más fácil de asimilar.
Andrew Coleson
2
"A menos que también se incluya otra etiqueta para un marco / biblioteca, se espera una respuesta pura de JavaScript".
Michał Perłakowski
6

En ECMAScript 6 no hay necesidad de usar hacks feos como Array.prototype.slice(). En su lugar, puede usar la sintaxis spread ( ...) .

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

Puede parecer extraño, pero es bastante simple. Simplemente extrae argumentslos elementos y los vuelve a colocar en la matriz. Si aún no entiende, vea estos ejemplos:

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

Tenga en cuenta que no funciona en algunos navegadores antiguos como IE 11, por lo que si desea admitir estos navegadores, debe usar Babel .

Michał Perłakowski
fuente
5

Aquí hay un punto de referencia de varios métodos que convierten argumentos en una matriz.

En cuanto a mí, la mejor solución para una pequeña cantidad de argumentos es:

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

Para otros casos:

function sortArgs (){ return Array.apply(null, arguments).sort(); }
Gheljenor
fuente
+1 para puntos de referencia. Actualmente, los gráficos muestran que tu sortArgses 6 veces más rápido en Firefox, pero el doble de lento en el último Chrome, en comparación con Array.apply.
joeytwiddle
1
Dado que Array.prototype.slice está en desuso porque impide la optimización del motor JS V8, considero que esta es la mejor solución hasta que ES6 esté ampliamente disponible +1
Sasha
1
Además, los métodos genéricos de Array como Array.slice () también están en desuso
Sasha
4

Recomiendo usar el operador de expansión ECMAScript 6 , que vinculará los parámetros finales a una matriz. Con esta solución no necesita tocar el argumentsobjeto y su código se simplificará. La desventaja de esta solución es que no funciona en la mayoría de los navegadores, por lo que deberá utilizar un compilador JS como Babel. Bajo el capó, Babel se transforma argumentsen una matriz con un bucle for.

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

Si no puede usar un ECMAScript 6, le recomiendo ver algunas de las otras respuestas, como @Jonathan Fingland

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

Lodash

var args = _.toArray(arguments);

en acción:

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

produce:

[2,3]
Zane Hooper
fuente
66
¿Por qué no _.toArray?
Menixator
55
Pero _.toArrayes más apropiado.
Menixator
Yo diría que _.slice es más apropiado ya que a menudo quieres descartar algunos argumentos principales.
Hafthor
4

Use Array.from(), que toma un objeto tipo matriz (como arguments) como argumento y lo convierte en matriz:

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

Tenga en cuenta que no funciona en algunos navegadores antiguos como IE 11, por lo que si desea admitir estos navegadores, debe usar Babel .

Michał Perłakowski
fuente
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));

Abk
fuente
1

Otra respuesta

Usa hechizos de magia negra:

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

Firefox, Chrome, Node.js, IE11 están bien.

LightSpeedC Kazuaki Nishizawa
fuente
66
Esta es definitivamente la mejor solución ... si desea que su código sea completamente ilegible. Además, __proto__está en desuso. Ver documentos de MDN .
Michał Perłakowski
1

Intenta usar Object.setPrototypeOf()

Explicación: Conjunto prototypede argumentsaArray.prototype

function toArray() {
  return Object.setPrototypeOf(arguments, Array.prototype)
} 

console.log(toArray("abc", 123, {def:456}, [0,[7,[14]]]))


Explicación: Tome cada índice de arguments, coloque el elemento en una matriz en el índice correspondiente de la matriz.

alternativamente podría usar Array.prototype.map()

function toArray() {
  return [].map.call(arguments, (_,k,a) => a[k])
} 

console.log(toArray("abc", 123, {def:456}, [0,[7,[14]]]))


Explicación: Tome cada índice de arguments, coloque el elemento en una matriz en el índice correspondiente de la matriz.

for..of lazo

function toArray() {
 let arr = []; for (let prop of arguments) arr.push(prop); return arr
} 

console.log(toArray("abc", 123, {def:456}, [0,[7,[14]]]))


o Object.create()

Explicación: Crear objeto, establecer propiedades de objeto a elementos en cada índice de arguments; conjunto prototypede objetos creados paraArray.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]]]))

invitado271314
fuente
Le ha estado preguntando a la gente si su respuesta satisface la advertencia bajo la pregunta sobre "respuestas largas". Me parece que el problema principal con su respuesta es que no explica por qué uno debería usar cualquiera de los métodos que muestra aquí. No hay forma de que use cualquiera de las soluciones que propone porque son todas peores que las soluciones en la respuesta aceptada. Por ejemplo, su referencia al documento Object.setPrototypeOfadvierte que es una "operación muy lenta". ¿Por qué demonios usaría esto Array.prototype.slice.call?
Louis
Respuesta actualizada de @Louis con explicaciones para cada enfoque. La pregunta en OP parece ser "¿Hay una forma integrada de hacerlo usando la biblioteca estándar?" , no "¿por qué uno debería usar alguno de los métodos" que están incorporados? ¿Cómo podría un usuario que publicó una Respuesta determinar con precisión si las soluciones publicadas son "correctas" para OP? Parece que depende de OP determinar esto? En realidad, esta parte de la notificación es lo que le interesa a este usuario: "explique por qué su respuesta es correcta" . ¿Alguna de las respuestas proporcionadas a esta pregunta dice: "Esta respuesta es" correcta "porque: x, y, z"?
invitado271314
Lo que he explicado es cómo funciona SO en general. Si el OP quiere o no saber por qué una solución es mejor que la otra es totalmente irrelevante, porque las preguntas publicadas para SO no son principalmente para el OP sino para las cientos y miles de personas que leerán la pregunta y sus respuestas más adelante. Además, las personas que votan las respuestas no están obligadas a votar de acuerdo con lo que el OP pueda estar satisfecho.
Louis
1

Aquí hay una solución limpia y concisa:

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

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

Object.values( ) devolverá los valores de un objeto como una matriz, y desde arguments es un objeto, será esencialmente convertir argumentsen una matriz, lo que le proporciona todas las funciones de ayuda de una matriz tales como map, forEach, filter, etc.

Místico
fuente
0

Benshmarck 3 métodos:

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

¿RESULTADO?

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

¡A disfrutar!

Matriz
fuente
1
Me gustaría sugerir que se modifique a algo así como arguments.length == 1?[arguments[0]]:Array.apply(null, arguments);para evitar que la función se llame con un solo argumento entero dummyFn(3)y cause un resultado[,,]
importa el
@nevermind buen trabajo, edito, tu versión es mejor;) ¿Pero no entiendo dummyFn(3)qué es esto? esta función no existe ...
Matrix
Uh ... no importa, solo un ejemplo quiere mostrar cómo una función llamada con un solo argumento entero y causa una matriz vacía de tamaño fijo, lo siento por mi inglés.
importa el
0

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

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

wsc
fuente
0

Aunque los parámetros de descanso funcionan bien, si desea continuar usándolo argumentspor alguna razón, considere

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

[...arguments] puede considerarse una especie de alternativa a Array.from(arguments) , que también funciona perfectamente bien.

Una alternativa ES7 es una comprensión de matriz:

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

Esto podría ser más fácil si desea procesar o filtrar los argumentos antes de ordenar:

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

fuente
0
 function x(){
   var rest = [...arguments]; console.log(rest);return     
   rest.constructor;
 };
 x(1,2,3)

Intenté una técnica de destrucción simple

Gaurav
fuente
0

El objeto Argumentos solo está disponible dentro de un cuerpo de función. Aunque puede indexar el Objeto Argumentos como una matriz, no es una matriz. No tiene ninguna propiedad de matriz que no sea la longitud.

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

Aunque puede indexarse ​​como una matriz, como puede ver en este ejemplo:

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

Sin embargo, el objeto Argumentos no es una matriz y no tiene ninguna otra propiedad que no sea la longitud.

Puede convertir el objeto de argumentos en una matriz en cuyo punto puede acceder al objeto Argumentos.

Hay muchas formas de acceder al objeto de argumentos dentro del cuerpo de una función, y estas incluyen:

  1. puede llamar al método Array.prototoype.slice.call.

Array.prototype.slice.call (argumentos)

function giveMeArgs(arg1,arg2){

var args = Array.prototype.slice.call(arguments);
return args
}

console.log( giveMeArgs(1,2));

  1. puedes usar la matriz literal

[] .slice.call (argumentos).

function giveMeArgs(arg1,arg2){

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

return args;

}

console.log( giveMeArgs(1,2) );

  1. puedes usar Rest ...

function giveMeArgs(...args){

return args;

}

console.log(giveMeArgs(1,2))

  1. puedes usar spread [...]

function giveMeArgs(){

var args = [...arguments];
return args;

}

console.log(giveMeArgs(1,2));

  1. puedes usar Array.from ()

function giveMeArgs(){

var args = Array.from(arguments);

return args;

}

console.log(giveMeArgs(1,2));

Almiar
fuente
0

Puede crear una función reutilizable para hacerlo con cualquier argumento, el más simple es algo como esto:

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

sortArgs('ali', 'reza', 1, 2, 'a'); //[1, 2, "a", "ali", "reza"];

La sintaxis extendida se puede usar en ES6 y superior ...

Pero si desea usar algo compatible con ES5 y versiones posteriores, puede usarlo Array.prototype.slice.call, de modo que su código se verá así:

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

sortArgs('ali', 'reza', 1, 2, 'a'); //[1, 2, "a", "ali", "reza"];

También hay algunas otras formas de hacerlo, por ejemplo, usando Array.fromo recorriendo los argumentos y asignándolos a una nueva matriz ...

Alireza
fuente
-2

Esta es una pregunta muy antigua, pero creo que tengo una solución que es un poco más fácil de escribir que las soluciones anteriores y no se basa en bibliotecas externas:

function sortArguments() {
  return Array.apply(null, arguments).sort();
}
HandyAndyShortStack
fuente
2
Array.applyno funcionará si solo tiene un argumento entero positivo. Es porque new Array(3)crea una longitud de matriz de 3. Para ser más específico, el resultado será [undefined, undefined, undefined]y no [3].
inf3rno