Como posso converter o objeto "argumentos" em uma matriz em JavaScript?

432

O argumentsobjeto em JavaScript é uma verruga estranha - ele atua como uma matriz na maioria das situações, mas na verdade não é um objeto de matriz. Desde que é realmente algo completamente diferente , ele não tem as funções úteis de Array.prototypecomo forEach, sort, filter, e map.

É trivialmente fácil construir uma nova matriz a partir de um objeto de argumentos com um loop for simples. Por exemplo, esta função classifica seus argumentos:

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

No entanto, isso é algo lamentável a ser feito simplesmente para obter acesso às funções extremamente úteis da matriz JavaScript. Existe uma maneira interna de fazer isso usando a biblioteca padrão?

Andrew Coleson
fonte
estreitamente relacionado: como o Array.prototype.slice.call () funciona?
Bergi 5/08/2015

Respostas:

724

ES6 usando parâmetros de descanso

Se você puder usar o ES6, poderá usar:

Parâmetros de repouso

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

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

Como você pode ler no link

A sintaxe do parâmetro rest nos permite representar um número indefinido de argumentos como uma matriz.

Se você está curioso sobre a ...sintaxe, ela se chama Operador de propagação e pode ler mais aqui .

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 basta converter objetos do tipo Array ou Iterable em instâncias do Array.



ES5

Na verdade, você pode simplesmente usar Arraya slicefunção de s em um objeto de argumentos e ele será convertido em uma matriz JavaScript padrão. Você apenas terá que referenciá-lo manualmente através do protótipo do Array:

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

Por que isso funciona? Bem, aqui está um trecho da própria documentação do ECMAScript 5 :

NOTA : A slicefunção é intencionalmente genérica; não exige que esse valor seja um objeto Array. Portanto, ele pode ser transferido para outros tipos de objetos para uso como método. Se a slicefunção pode ser aplicada com êxito a um objeto host depende da implementação.

Portanto, slicefunciona em qualquer coisa que tenha uma lengthpropriedade, o que argumentsconvenientemente faz.


Se Array.prototype.slicefor demais para você, você pode abreviá-lo um pouco usando literais de matriz:

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

No entanto, tenho a impressão de que a versão anterior é mais explícita, então prefiro. O abuso da notação literal da matriz parece estranho e parece estranho.

Jonathan Fingland
fonte
5
Nas versões recentes do Firefox (2.0?) E ECMAScript 5, existe um método Array.slice que torna isso um pouco mais simples. Você poderia chamar assim: var arge = Array.slice (argumentos, 0) ;.
Matthew Crumley
9
Para que serve 0? Como não há argumentos que você queira passar, por que você não pode simplesmente usar var args = Array.prototype.slice.call(arguments);? [ A página de argumentos do MDC ] ( developer.mozilla.org/en/JavaScript/Reference/… ) sugere o método sem o 0.
quer
3
@ Barney - a classificação é necessária porque a pergunta original deseja classificar os argumentos. Se você apenas deseja convertê-lo em uma matriz, não é necessário.
Krease
17
"Você não deve cortar argumentos porque impede otimizações nos mecanismos JavaScript (V8 por exemplo)." - do MDN developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/…
frameworkninja
3
Não tenho certeza se está respondendo à pergunta original. Tempo gasto por. Com ES6 experimentou no Firefox e Chrome, o parâmetro resto está se tornando uma boa alternativa - function sortArgs(...argsToSort) { return argsToSort.sort(); }de MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
Kir Kanos
40

Também vale a pena fazer referência a esta página wiki da biblioteca de promessas do Bluebird, que mostra como gerenciar o argumentsobjeto na matriz, de maneira a otimizar a função no mecanismo JavaScript da V8 :

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 é usado em favor de var args = [].slice.call(arguments);. O autor também mostra como uma etapa de construção pode ajudar a reduzir a verbosidade.

jbmusso
fonte
2
+1 e obrigado pelo link para a página do Bluebird Optimization Killer. PS: Vi recentemente essa otimização usada no projeto node-thunkify .
Yves M.
29
function sortArgs(){ return [].slice.call(arguments).sort() }

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

Alguns métodos de matriz são feitos intencionalmente para não exigir que o objeto de destino seja uma matriz real. Eles exigem apenas que o destino tenha uma propriedade chamada length e índices (que devem ser zero ou maiores números inteiros).

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

Usar:

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

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

Array(str1) retorna [str1]

Array(num1)retorna uma matriz que possui num1elementos

Você deve verificar o número de argumentos!

Array.slice versão (mais lenta):

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

Array.push versão (mais lenta, mais rápida que a fatia):

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

Mover versão (mais lenta, mas o tamanho pequeno é mais rápido):

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

Array.concat versão (mais lenta):

function sortArguments() {
  return Array.prototype.concat.apply([], arguments).sort();
}
LightSpeedC Kazuaki Nishizawa
fonte
1
Observe que, devido ao comportamento de nivelamento em Array.concat, ele aceita argumentos de matriz. sortArguments (2, [3,0], 1) retorna 0,1,2,3.
Hafthor 26/01/19
9

Se você estiver usando jQuery, a seguir, é muito mais fácil lembrar na minha opinião:

function sortArgs(){
  return $.makeArray(arguments).sort();
}
Brad
fonte
1
Isso é essencialmente o que eu estava procurando em JS puro, mas +1 no exemplo sólido de como o jQuery torna o JS um pouco mais fácil de entender.
Andrew Coleson
2
"A menos que outra tag para uma estrutura / biblioteca também esteja incluída, é esperada uma resposta JavaScript pura."
Michał Perłakowski
6

No ECMAScript 6, não há necessidade de usar hacks feios como Array.prototype.slice(). Você pode usar a sintaxe de propagação ( ...) .

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

Pode parecer estranho, mas é bastante simples. Ele apenas extrai argumentsos elementos e os coloca de volta na matriz. Se você ainda não entende, veja estes exemplos:

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

Observe que ele não funciona em alguns navegadores mais antigos, como o IE 11; portanto, se você deseja oferecer suporte a esses navegadores, use o Babel .

Michał Perłakowski
fonte
5

Aqui está uma referência de vários métodos para converter argumentos em array.

Quanto a mim, a melhor solução para uma pequena quantidade de argumentos é:

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

Para outros casos:

function sortArgs (){ return Array.apply(null, arguments).sort(); }
Gheljenor
fonte
+1 para referências. Atualmente, os gráficos mostram que você sortArgsé 6 vezes mais rápido no Firefox, mas duas vezes mais lento no Chrome mais recente, em comparação com o Array.apply.
Joeytwiddle 18/07/2013
1
Desde Array.prototype.slice está obsoleta porque evitar JS V8 engine optimization Eu considero esta a melhor solução até ES6 estará amplamente disponível +1
Sasha
1
Além disso genéricos de matriz métodos como Array.slice () também são preteridos
Sasha
4

Eu recomendo usar o operador de propagação ECMAScript 6 , que vinculará os parâmetros finais a uma matriz. Com esta solução, você não precisa tocar no argumentsobjeto e seu código será simplificado. A desvantagem desta solução é que ela não funciona na maioria dos navegadores; portanto, você precisará usar um compilador JS como o Babel. Sob o capô, Babel se transforma argumentsem uma matriz com um loop for.

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

Se você não pode usar o ECMAScript 6, recomendo examinar algumas das outras respostas, como @ Jonathan Fingland

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

Lodash:

var args = _.toArray(arguments);

em ação:

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

produz:

[2,3]
Zane Hooper
fonte
6
Por que não _.toArray?
Menixator 5/10
5
Mas _.toArrayé mais apropriado.
Menixator # 6/16
Eu diria que _.slice é mais apropriado, já que muitas vezes você deseja descartar alguns argumentos principais.
Hafthor 30/01/19
4

Use Array.from(), que pega um objeto do tipo matriz (como arguments) como argumento e o converte em matriz:

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

Observe que ele não funciona em alguns navegadores mais antigos, como o IE 11; portanto, se você deseja oferecer suporte a esses navegadores, use o Babel .

Michał Perłakowski
fonte
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
fonte
1

Outra resposta.

Use Magias Negras:

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

Firefox, Chrome, Node.js, IE11 estão OK.

LightSpeedC Kazuaki Nishizawa
fonte
6
Esta é definitivamente a melhor solução ... se você quiser tornar seu código completamente ilegível. Além disso, __proto__está obsoleto. Veja os documentos MDN .
Michał Perłakowski
1

Tente usar Object.setPrototypeOf()

Explicação: Ajuste daprototypeargumentsArray.prototype

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

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


Explicação: Pegue cada índice de arguments , coloque o item em uma matriz no índice correspondente da matriz.

alternativamente poderia usar Array.prototype.map()

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

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


Explicação: Pegue cada índice de arguments , coloque o item em uma matriz no índice correspondente da matriz.

for..of ciclo

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

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


ou Object.create()

Explicação: Crie um objeto, defina propriedades do objeto para itens em cada índice de arguments; conjunto prototypede objetos criados 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]]]))

guest271314
fonte
Você está perguntando às pessoas se sua resposta atende ao aviso da pergunta sobre "respostas longas". Parece-me que o principal problema com sua resposta é que você não explica por que se deve usar qualquer um dos métodos mostrados aqui. Não há como eu usar qualquer uma das soluções que você propõe, porque todas são piores que as soluções na resposta aceita. Por exemplo, sua referência ao documento de Object.setPrototypeOfalerta que é uma "operação muito lenta". Por que diabos eu usaria isso de novo Array.prototype.slice.call?
Louis
Resposta atualizada do @Louis com explicações para cada abordagem. A pergunta no OP parece ser "Existe uma maneira interna de fazer isso usando a biblioteca padrão?" , não "por que alguém deve usar algum dos métodos" incorporados? Como poderia um usuário que postou uma resposta determinar com precisão se as soluções postadas são "corretas" para o OP? Parece que cabe ao OP determinar isso? Na verdade, essa parte da notificação é o que interessa ao usuário: "explique por que sua resposta está certa" . Alguma das respostas fornecidas para esta pergunta declara: "Esta resposta está" correta "porque: x, y, z"?
guest271314
O que expliquei é como o SO geralmente funciona. Se o OP deseja ou não saber por que uma solução é melhor que a outra é totalmente irrelevante, porque as perguntas postadas para o SO não são principalmente para o OP, mas para as centenas e milhares de pessoas que lerão a pergunta e suas respostas posteriormente. Além disso, as pessoas que votam nas respostas não precisam votar de acordo com o que o OP possa estar satisfeito.
Louis
1

Aqui está uma solução limpa e concisa:

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

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

Object.values( )devolverá os valores de um objeto como uma matriz, e uma vez que argumentsé um objeto, ele essencialmente irá converter argumentsem uma matriz, proporcionando-lhe todas as funções auxiliares de uma matriz, tais como map, forEach, filter, etc.

Místico
fonte
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

Desfrutar !

Matriz
fonte
1
Gostaria de sugerir modificado para algo como arguments.length == 1?[arguments[0]]:Array.apply(null, arguments);impedir que a função seja chamada com apenas um argumento inteiro dummyFn(3)e cause resultado[,,]
deixa de lado
@nevermind bom trabalho, eu edito, sua versão é melhor;) Mas eu não entendo o dummyFn(3)que é isso? essa função não existe ...
Matrix
Uh ... não importa, apenas um exemplo quer mostrar como uma função chamada com apenas um argumento inteiro e causa uma matriz vazia de tamanho fixo, desculpe pelo meu inglês.
Nevermind
0

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

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

wsc
fonte
0

Embora os parâmetros de descanso funcionem bem, se você quiser continuar usando argumentspor algum motivo, considere

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

[...arguments] pode ser considerado uma espécie de alternativa ao Array.from(arguments) , que também funciona perfeitamente bem.

Uma alternativa do ES7 é uma compreensão de matriz:

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

Isso pode ser mais fácil se você quiser processar ou filtrar os argumentos antes da classificação:

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

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

Eu tentei uma técnica simples de destruição

Gaurav
fonte
0

O objeto Arguments está disponível apenas dentro de um corpo de função. Embora você possa indexar o objeto Arguments como uma matriz, ela não é uma matriz. Ele não possui outras propriedades da matriz além do comprimento.

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

Embora possa ser indexado como uma matriz, como você pode ver neste exemplo:

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

No entanto, o objeto Arguments não é uma matriz e não possui outras propriedades além do comprimento.

Você pode converter o objeto de argumentos em uma matriz em que você pode acessar o objeto Arguments.

Existem várias maneiras de acessar o objeto de argumentos dentro de um corpo de função, e incluem:

  1. você pode chamar o 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. você pode usar a matriz literal

[] .slice.call (argumentos).

function giveMeArgs(arg1,arg2){

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

return args;

}

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

  1. você pode usar o Rest ...

function giveMeArgs(...args){

return args;

}

console.log(giveMeArgs(1,2))

  1. você pode usar spread [...]

function giveMeArgs(){

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

}

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

  1. você pode usar Array.from ()

function giveMeArgs(){

var args = Array.from(arguments);

return args;

}

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

Rick
fonte
0

Você pode criar uma função reutilizável para fazê-lo com qualquer argumento, a mais simples é algo como isto:

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

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

A sintaxe de propagação pode ser usada no ES6 e acima ...

Mas se você deseja usar algo compatível com o ES5 e abaixo, pode usar Array.prototype.slice.call, para codificar o seguinte:

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

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

Existem também algumas outras maneiras de fazer isso, por exemplo, usando Array.fromou fazendo um loop através dos argumentos e atribuí-los a uma nova matriz ...

Alireza
fonte
-2

Essa é uma pergunta muito antiga, mas acho que tenho uma solução que é um pouco mais fácil de digitar do que as soluções anteriores e não depende de bibliotecas externas:

function sortArguments() {
  return Array.apply(null, arguments).sort();
}
HandyAndyShortStack
fonte
2
Array.applynão funcionará se você tiver apenas um único argumento inteiro positivo. Isso ocorre porque new Array(3)cria um comprimento de matriz de 3. Para ser mais específico, o resultado será [undefined, undefined, undefined]e não [3].
Inf3rno