Come posso convertire l'oggetto "argomenti" in un array in JavaScript?


432

L' argumentsoggetto in JavaScript è una strana verruca: si comporta proprio come un array nella maggior parte delle situazioni, ma in realtà non è un oggetto array. Dal momento che è davvero qualcosa di completamente diverso , non ha le funzioni utili da Array.prototypecome forEach, sort, filter, e map.

È banalmente facile costruire un nuovo array da un oggetto argomenti con un semplice ciclo for. Ad esempio, questa funzione ordina i suoi argomenti:

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

Tuttavia, questa è una cosa piuttosto pietosa da fare semplicemente per ottenere l'accesso alle funzioni dell'array JavaScript estremamente utili. Esiste un modo integrato per farlo utilizzando la libreria standard?


Risposte:


724

ES6 utilizzando i parametri di riposo

Se sei in grado di usare ES6 puoi usare:

Parametri di riposo

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

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

Come puoi leggere nel link

La sintassi del parametro rest ci consente di rappresentare un numero indefinito di argomenti come una matrice.

Se sei curioso della ...sintassi, si chiama Spread Operator e puoi leggere di più qui .

ES6 utilizzando Array.from ()

Utilizzando 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 converti semplicemente oggetti simili a array o Iterable in istanze di array.



ES5

In realtà puoi semplicemente usare Arrayla slicefunzione su un oggetto argomenti e lo convertirà in un array JavaScript standard. Dovrai solo fare riferimento manualmente tramite il prototipo di Array:

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

Perché funziona? Bene, ecco un estratto dalla documentazione stessa di ECMAScript 5 :

NOTA : la slicefunzione è intenzionalmente generica; non richiede che questo valore sia un oggetto Array. Pertanto può essere trasferito ad altri tipi di oggetti per essere utilizzato come metodo. Se la slicefunzione può essere applicata correttamente a un oggetto host dipende dall'implementazione.

Pertanto, slicefunziona su tutto ciò che ha una lengthproprietà, che lo argumentsfa comodamente.


Se per te Array.prototype.sliceè troppo un boccone, puoi abbreviarlo leggermente usando array letterali:

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

Tuttavia, tendo a ritenere che la versione precedente sia più esplicita, quindi preferirei invece. Abusare della notazione letterale array sembra confuso e sembra strano.


5
Nelle recenti versioni di Firefox (2.0?) E ECMAScript 5, esiste un metodo Array.slice che lo rende un po 'più semplice. Lo chiameresti così: var arge = Array.slice (argomenti, 0) ;.
Matthew Crumley,

9
A cosa serve 0? Dal momento che non ci sono argomenti che vuoi passare, perché non puoi semplicemente usare var args = Array.prototype.slice.call(arguments);? [ La pagina degli argomenti MDC ] ( developer.mozilla.org/en/JavaScript/Reference/… ) suggerisce il metodo senza il 0.
Peter Ajtai,

3
@ Barney: l'ordinamento è necessario perché la domanda originale vuole ordinare gli argomenti. Se vuoi solo convertirlo in un array, non è necessario.
Krease,

17
"Non dovresti tagliare gli argomenti perché impedisce le ottimizzazioni nei motori JavaScript (V8 per esempio)." - da MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
frameworkninja

3
Non sono sicuro che risponda alla domanda originale. Il tempo è passato. Con ES6 sperimentato in Firefox e Chrome, il parametro restante sta diventando una buona alternativa - function sortArgs(...argsToSort) { return argsToSort.sort(); }da MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Kir Kanos

40

Vale anche la pena fare riferimento a questa pagina wiki della libreria Bluebird che mostra come gestire l' argumentsoggetto in array in modo da rendere la funzione ottimizzabile con il motore JavaScript V8 :

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

Questo metodo è utilizzato a favore di var args = [].slice.call(arguments);. L'autore mostra anche come un passo di costruzione può aiutare a ridurre la verbosità.


2
+1 e grazie per il link alla pagina Killer di ottimizzazione Bluebird. PS: Di recente ho visto questa ottimizzazione utilizzata nel progetto node-thunkify .
Yves M.

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

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

Alcuni metodi di array sono intenzionalmente creati per non richiedere che l'oggetto target sia un array reale. Richiedono solo che la destinazione abbia una proprietà denominata lunghezza e indici (che devono essere zero o numeri interi più grandi).

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

12

Uso:

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

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

Array(str1) ritorna [str1]

Array(num1)restituisce un array che ha num1elementi

Devi controllare il numero di argomenti!

Array.slice versione (più lenta):

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

Array.push versione (più lenta, più veloce della fetta):

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

Sposta versione (più lento, ma le dimensioni ridotte sono più veloci):

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

Array.concat versione (più lenta):

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

1
Si noti che a causa del comportamento appiattimento in Array.concat, accetterà gli argomenti dell'array. sortArguments (2, [3,0], 1) restituisce 0,1,2,3.
Hafthor,

9

Se stai usando jQuery, secondo me è molto più facile ricordare a mio avviso:

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

1
Questo è essenzialmente quello che stavo cercando in JS puro, ma +1 per il solido esempio di come jQuery rende JS un po 'più facile da grok.
Andrew Coleson,

2
"A meno che non sia incluso un altro tag per un framework / libreria, è prevista una risposta JavaScript pura."
Michał Perłakowski,

6

In ECMAScript 6 non c'è bisogno di usare brutti hack come Array.prototype.slice(). Puoi invece usare la sintassi diffusa ( ...) .

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

Può sembrare strano, ma è abbastanza semplice. Estrae semplicemente argumentsgli elementi e li rimette nella matrice. Se ancora non capisci, vedi questi esempi:

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

Nota che non funziona in alcuni browser meno recenti come IE 11, quindi se vuoi supportare questi browser, dovresti usare Babel .


5

Ecco il benchmark di diversi metodi che convertono gli argomenti in array.

Per quanto mi riguarda, la migliore soluzione per piccole quantità di argomenti è:

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

Per altri casi:

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

+1 per i benchmark. Attualmente i grafici mostrano che sortArgssei Firefox 6 volte più veloce, ma due volte più lento nell'ultimo Chrome, rispetto a Array.apply.
joeytwiddle,

1
Poiché Array.prototype.slice è obsoleto perché impedisce l'ottimizzazione del motore JS V8, ritengo che questa sia la soluzione migliore fino a quando ES6 sarà ampiamente disponibile +1
Sasha,

1
Inoltre i metodi generici di array come Array.slice () sono anch'essi deprecati
Sasha,

4

Consiglio di utilizzare l' operatore di diffusione ECMAScript 6 , che vincolerà i parametri finali a un array. Con questa soluzione non è necessario toccare l' argumentsoggetto e il codice sarà semplificato. L'aspetto negativo di questa soluzione è che non funziona nella maggior parte dei browser, quindi dovrai usare un compilatore JS come Babel. Sotto il cofano Babel si trasforma argumentsin un array con un ciclo for.

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

Se non puoi usare un ECMAScript 6, ti consiglio di guardare alcune delle altre risposte come @Jonathan Fingland

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

4

Lodash:

var args = _.toArray(arguments);

in azione:

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

produce:

[2,3]

6
Perché no _.toArray?
Menixator,

5
Ma _.toArrayè più appropriato.
Menixator,

Direi che _.slice è più appropriato poiché spesso si desidera eliminare alcuni argomenti principali.
Hafthor,

4

Usa Array.from(), che accetta un oggetto simile a un array (come arguments) come argomento e lo converte in array:

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

Nota che non funziona in alcuni browser meno recenti come IE 11, quindi se vuoi supportare questi browser, dovresti usare Babel .


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


1

Un'altra risposta

Usa gli incantesimi di magia nera:

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

Firefox, Chrome, Node.js, IE11 sono OK.


6
Questa è sicuramente la soluzione migliore ... se vuoi rendere il tuo codice completamente illeggibile. Inoltre, __proto__è deprecato. Vedi i documenti MDN .
Michał Perłakowski,

1

Prova a usare Object.setPrototypeOf()

Spiegazione: Set prototypeof argumentstoArray.prototype

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

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


Spiegazione: Prendere ogni indice di arguments, posizionare l'elemento in un array nel corrispondente indice di array.

potrebbe in alternativa utilizzare Array.prototype.map()

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

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


Spiegazione: Prendere ogni indice di arguments, posizionare l'elemento in un array nel corrispondente indice di array.

for..of ciclo continuo

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

Spiegazione: Creare un oggetto, impostare le proprietà dell'oggetto su elementi in ciascun indice di arguments; set prototypedi oggetti creati suArray.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]]]))


Hai chiesto alle persone se la tua risposta soddisfa l'avvertimento sotto la domanda "risposte lunghe". Mi sembra che il problema principale con la tua risposta sia che non spieghi perché dovresti usare uno dei metodi che mostri qui. Non avrei mai usato nessuna delle soluzioni che proponevi perché sono tutte peggiori delle soluzioni nella risposta accettata. Ad esempio, il tuo riferimento al documento di Object.setPrototypeOfavverte che si tratta di "un'operazione molto lenta". Perché mai dovrei usarlo Array.prototype.slice.call?
Louis,

@Louis Risposta aggiornata con spiegazioni per ciascun approccio. La domanda in OP sembra essere "Esiste un modo integrato per farlo usando la libreria standard?" , non "perché si dovrebbe usare uno qualsiasi dei metodi" che sono integrati? Come può un utente che ha pubblicato una risposta determinare con precisione se le soluzioni pubblicate sono "giuste" per OP? Sembrerebbe che spetti a OP determinare questo? In realtà, questa parte della notifica è ciò che ha interessato questo utente: "spiega perché la tua risposta è giusta" . Qualcuno delle risposte fornite a questa domanda indica: "Questa risposta è" giusta "perché: x, y, z"?
ospite271314

Quello che ho spiegato è come SO generalmente funziona. Indipendentemente dal fatto che l'OP voglia sapere perché una soluzione è migliore dell'altra è del tutto irrilevante, perché le domande inviate per SO non sono principalmente per l'OP ma per le centinaia e migliaia di persone che leggeranno la domanda e le sue risposte in seguito. Inoltre, le persone che votano sulle risposte non sono tenute a votare in base a ciò che l'OP potrebbe essere soddisfatto.
Louis,

1

Ecco una soluzione pulita e concisa:

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

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

Object.values( )restituirà i valori di un oggetto come una matrice, e poiché argumentsè un oggetto, sarà essenzialmente converte argumentsin una matrice, così ti fornisce tutte le funzioni di supporto di una matrice come map, forEach, filter, etc.


0

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

RISULTATO?

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

Godere !


1
Vorrei suggerire di apportare modifiche a qualcosa di simile arguments.length == 1?[arguments[0]]:Array.apply(null, arguments);per impedire che la funzione venga chiamata con un solo argomento intero dummyFn(3)e causi il risultato[,,]
importa il

@nevermind buon lavoro, modifico, la tua versione è migliore;) Ma non capisco di dummyFn(3)cosa si tratta? questa funzione non esiste ...
Matrix,

Uh ... non importa, solo un esempio vuole mostrare come una funzione chiamata con un solo argomento intero e causa un array vuoto di dimensioni fisse, scusate il mio inglese.
dimenticare il

0

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

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


0

Sebbene i parametri di riposo funzionino bene, se si desidera continuare a utilizzare argumentsper qualche motivo, considerare

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

[...arguments]può essere considerata una sorta di alternativa a Array.from(arguments), che funziona anche perfettamente.

Un'alternativa ES7 è una comprensione dell'array:

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

Questo potrebbe essere più semplice se si desidera elaborare o filtrare gli argomenti prima dell'ordinamento:

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

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

Ho provato una semplice tecnica di distruzione


0

L'oggetto Arguments è disponibile solo all'interno di un corpo di funzione. Sebbene sia possibile indicizzare l'oggetto Argomenti come un array, non è un array. Non ha proprietà dell'array diverse dalla lunghezza.

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

Sebbene possa essere indicizzato come un array, come puoi vedere in questo esempio:

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

Tuttavia, l'oggetto Arguments non è un array e non ha altre proprietà oltre alla lunghezza.

È possibile convertire l'oggetto argomenti in un array a quel punto è possibile accedere all'oggetto Arguments.

Esistono molti modi per accedere all'oggetto argomenti all'interno di un corpo di funzione e questi includono:

  1. puoi chiamare il metodo Array.prototoype.slice.call.

Array.prototype.slice.call (argomenti)

function giveMeArgs(arg1,arg2){

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

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

  1. puoi usare l'array letterale

[] .slice.call (argomenti).

function giveMeArgs(arg1,arg2){

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

return args;

}

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

  1. puoi usare Rest ...

function giveMeArgs(...args){

return args;

}

console.log(giveMeArgs(1,2))

  1. puoi usare spread [...]

function giveMeArgs(){

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

}

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

  1. puoi usare Array.from ()

function giveMeArgs(){

var args = Array.from(arguments);

return args;

}

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


0

Puoi creare una funzione riutilizzabile per farlo con qualsiasi argomento, il più semplice è qualcosa del genere:

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

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

La sintassi diffusa può essere utilizzata in ES6 e versioni successive ...

Ma se desideri utilizzare qualcosa di compatibile con ES5 e versioni precedenti , puoi utilizzare Array.prototype.slice.call, quindi il codice è simile al seguente:

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

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

Esistono anche altri modi per farlo, ad esempio utilizzando Array.fromo scorrere gli argomenti e assegnarli a un nuovo array ...


-2

Questa è una domanda molto vecchia, ma penso di avere una soluzione leggermente più semplice da digitare rispetto alle soluzioni precedenti e che non si basa su librerie esterne:

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

2
Array.applynon funzionerà se hai solo un singolo argomento intero positivo. È perché new Array(3)crea una lunghezza di array di 3. Per essere più specifici il risultato sarà [undefined, undefined, undefined]e non [3].
inf3rno,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.