Jak przekonwertować obiekt „arguments” na tablicę w JavaScript?


433

argumentsObiektu w JavaScript jest nieparzystą brodawka-to działa podobnie jak tablicy w większości sytuacji, ale to nie jest faktycznie przedmiotem tablicą. Ponieważ jest to naprawdę coś zupełnie innego , to nie ma użytecznych funkcji z Array.prototypeniczym forEach, sort, filter, i map.

Łatwo jest zbudować nową tablicę z obiektu argumentów za pomocą prostej pętli for. Na przykład ta funkcja sortuje argumenty:

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

Jest to jednak dość żałosne, aby zrobić to, aby uzyskać dostęp do niezwykle użytecznych funkcji tablicy JavaScript. Czy istnieje wbudowany sposób, aby to zrobić przy użyciu standardowej biblioteki?


Odpowiedzi:


725

ES6 przy użyciu parametrów odpoczynku

Jeśli możesz korzystać z ES6, możesz użyć:

Parametry odpoczynku

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

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

Jak można przeczytać w linku

Składnia parametru reszty pozwala nam reprezentować nieokreśloną liczbę argumentów jako tablicę.

Jeśli ciekawi Cię ...składnia, nazywa się ona Spread Operator i możesz przeczytać więcej tutaj .

ES6 przy użyciu Array.from ()

Za pomocą 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 po prostu przekształć obiekty podobne do macierzy lub iterowalne w instancje macierzy.



ES5

Rzeczywiście można po prostu użyć Array„s slicefunkcję obiektu argumenty i będzie przekształcić go w standardowej tablicy JavaScript. Musisz tylko odwołać się do niego ręcznie za pomocą prototypu Array:

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

Dlaczego to działa? Cóż, oto fragment z samej dokumentacji ECMAScript 5 :

UWAGA : sliceFunkcja jest celowo ogólna; nie wymaga tego wartość była obiektem Array. Dlatego może być przenoszony na inne rodzaje obiektów w celu użycia jako metoda. To, czy slicefunkcja może zostać pomyślnie zastosowana do obiektu hosta, zależy od implementacji.

Dlatego slicedziała na wszystkim, co malength właściwość, co argumentswygodnie robi.


Gdyby Array.prototype.slice jest to dla Ciebie zbyt duże, możesz je skrócić, używając literałów tablicowych:

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

Jednak wydaje mi się, że poprzednia wersja jest bardziej wyraźna, więc wolałbym ją zamiast tego. Nadużywanie tablicowej notacji dosłownie wydaje się dziwne i wygląda dziwnie.


5
W najnowszych wersjach Firefoksa (2.0?) I ECMAScript 5 istnieje metoda Array.slice, która upraszcza to nieco. Nazwałbyś to tak: var arge = Array.slice (arguments, 0) ;.
Matthew Crumley,

9
Co to jest 0za? Ponieważ nie ma argumentów, które chcesz przekazać, dlaczego nie możesz po prostu użyć var args = Array.prototype.slice.call(arguments);? [ Strona argumentów MDC ] ( developer.mozilla.org/en/JavaScript/Reference/… ) sugeruje metodę bez 0.
Peter Ajtai

3
@Barney - sortowanie jest konieczne, ponieważ oryginalne pytanie chce posortować argumenty. Jeśli chcesz po prostu przekonwertować go na tablicę, nie jest to konieczne.
Krease

17
„Nie należy kroić argumentów, ponieważ uniemożliwia to optymalizację w silnikach JavaScript (na przykład V8).” - od MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
frameworkninja

3
Nie jestem pewien, czy odpowiada na pierwotne pytanie. Czas przeminął. Po eksperymentowaniu z ES6 w Firefoksie i Chrome parametr reszty staje się dobrą alternatywą - function sortArgs(...argsToSort) { return argsToSort.sort(); }od MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Kir Kanos

40

Warto również odwołać się do strony wiki obiecującej biblioteki Bluebird, która pokazuje, jak zarządzać argumentsobiektem w tablicy w sposób, który umożliwia optymalizację funkcji w silniku JavaScript V8 :

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

Ta metoda jest używana na korzyść var args = [].slice.call(arguments);. Autor pokazuje również, w jaki sposób krok kompilacji może pomóc w zmniejszeniu szczegółowości.


2
+1 i dziękuję za link do strony Zabójca optymalizacji Bluebird. PS: Ostatnio widziałem tę optymalizację stosowaną w projekcie node-thunkify .
Yves M.

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

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

Niektóre metody tablicowe są celowo tworzone tak, aby obiekt docelowy nie był rzeczywistą tablicą. Wymagają jedynie, aby cel miał właściwość o nazwie długość i indeksy (które muszą być zerowymi lub większymi liczbami całkowitymi).

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

12

Posługiwać się:

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

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

Array(str1) zwroty [str1]

Array(num1) zwraca tablicę, która ma num1 elementy

Musisz sprawdzić liczbę argumentów!

Array.slice wersja (wolniejsza):

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

Array.push wersja (wolniejsza, szybsza niż plasterek):

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

Przenieś wersję (wolniej, ale mały rozmiar jest szybszy):

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

Array.concat wersja (najwolniejsza):

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

1
Zauważ, że z powodu spłaszczania w Array.concat zaakceptuje argumenty tablicowe. sortArguments (2, [3,0], 1) zwraca 0,1,2,3.
Hafthor

9

Jeśli używasz jQuery, moim zdaniem o wiele łatwiej jest zapamiętać:

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

1
Zasadniczo tego szukałem w czystym JS, ale +1 za solidny przykład tego, jak jQuery sprawia, że ​​JS jest nieco łatwiejszy do opanowania.
Andrew Coleson

2
„O ile nie jest zawarty inny tag dla frameworka / biblioteki, oczekuje się czystej odpowiedzi JavaScript”.
Michał Perłakowski

6

W ECMAScript 6 nie trzeba używać takich brzydkich hacków jak Array.prototype.slice(). Zamiast tego możesz użyć spread syntax ( ...) .

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

Może to wyglądać dziwnie, ale jest dość proste. Po prostu wyodrębnia argumentselementy i umieszcza je z powrotem w tablicy. Jeśli nadal nie rozumiesz, zapoznaj się z tymi przykładami:

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

Pamiętaj, że nie działa w niektórych starszych przeglądarkach, takich jak IE 11, więc jeśli chcesz obsługiwać te przeglądarki, powinieneś użyć Babel .


5

Oto punkt odniesienia dla kilku metod przekształcających argumenty w tablicę.

Jeśli chodzi o mnie, najlepszym rozwiązaniem dla małej liczby argumentów jest:

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

W innych przypadkach:

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

+1 za testy porównawcze. Obecnie wykresy pokazują, że Twój sortArgsFirefox jest 6 razy szybszy w Firefoksie, ale dwa razy wolniejszy w najnowszym Chrome, w porównaniu do Array.apply.
joeytwiddle

1
Ponieważ Array.prototype.slice jest przestarzały, ponieważ uniemożliwia optymalizację silnika JS V8, uważam to za najlepsze rozwiązanie, dopóki ES6 nie będzie powszechnie dostępne +1
Sasha

1
Ponadto ogólne metody Array, takie jak Array.slice (), są również przestarzałe
Sasha

4

Polecam użycie operatora rozprzestrzeniania ECMAScript 6 , który powiąże końcowe parametry z tablicą. Dzięki temu rozwiązaniu nie musisz dotykać argumentsobiektu, a Twój kod zostanie uproszczony. Minusem tego rozwiązania jest to, że nie działa w większości przeglądarek, więc zamiast tego będziesz musiał użyć kompilatora JS, takiego jak Babel. Pod maską Babel przekształca argumentssię w tablicę z pętlą for.

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

Jeśli nie możesz użyć ECMAScript 6, polecam przyjrzeć się innym odpowiedziom, takim jak @Jathanathan Fingland

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

4

Lodash:

var args = _.toArray(arguments);

w akcji:

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

produkuje:

[2,3]

6
Dlaczego nie _.toArray?
Menixator,

5
Ale _.toArrayjest bardziej odpowiednie.
Menixator,

Twierdziłbym, że _.slice jest bardziej odpowiedni, ponieważ często chcesz porzucić niektóre główne argumenty.
Hafthor

4

Użyj Array.from(), która przyjmuje argumentsjako argument obiekt podobny do tablicy (np. ) I konwertuje go na tablicę:

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

Pamiętaj, że nie działa w niektórych starszych przeglądarkach, takich jak IE 11, więc jeśli chcesz obsługiwać te przeglądarki, powinieneś użyć 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

Kolejna odpowiedź.

Użyj zaklęć Czarnej Magii:

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

Firefox, Chrome, Node.js, IE11 są w porządku.


6
To zdecydowanie najlepsze rozwiązanie ... jeśli chcesz, aby Twój kod był całkowicie nieczytelny. Jest również __proto__przestarzałe. Zobacz dokumenty MDN .
Michał Perłakowski

1

Spróbuj użyć Object.setPrototypeOf()

Objaśnienia: Ustaw prototypeod argumentsdoArray.prototype

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

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


Objaśnienie: Weź każdy indeks arguments, umieść element w tablicy pod odpowiednim indeksem tablicy.

może alternatywnie użyć Array.prototype.map()

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

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


Objaśnienie: Weź każdy indeks arguments, umieść element w tablicy pod odpowiednim indeksem tablicy.

for..of pętla

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

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


lub Object.create()

Objaśnienie: Utwórz obiekt, ustaw właściwości obiektu na elementy przy każdym indeksie arguments; zestaw prototypeutworzonego obiektu doArray.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]]]))


Zostałaś pytać ludzi , czy spełnia Twoje odpowiedzieć na ostrzeżenie pod pytaniem o „długich odpowiedzi”. Wydaje mi się, że głównym problemem z twoją odpowiedzią jest to, że nie wyjaśniasz, dlaczego należy zastosować którąkolwiek z metod, które tu pokazujesz. Nie ma mowy, żebym skorzystał z któregoś z zaproponowanych przez ciebie rozwiązań, ponieważ wszystkie są gorsze niż rozwiązania z zaakceptowanej odpowiedzi. Na przykład odniesienie do dokumentu Object.setPrototypeOfostrzega, że ​​jest to „bardzo wolna operacja”. Dlaczego, u licha, miałbym to wykorzystać Array.prototype.slice.call?
Louis

@Louis Zaktualizowana odpowiedź z wyjaśnieniami dla każdego podejścia. Pytanie w OP wydaje się brzmieć „Czy istnieje wbudowany sposób, aby to zrobić przy użyciu standardowej biblioteki?” , a nie „dlaczego warto korzystać z którejkolwiek z metod”, które są wbudowane? Jak każdy użytkownik, który opublikował odpowiedź, mógł dokładnie określić, czy opublikowane rozwiązania są „odpowiednie” dla OP? Wydaje się, że to zależy od OP? W rzeczywistości ta część powiadomienia zainteresowała tego użytkownika: „wyjaśnij, dlaczego Twoja odpowiedź jest poprawna” . Czy którakolwiek z odpowiedzi udzielonych na to pytanie brzmi: „Ta odpowiedź jest„ prawidłowa ”, ponieważ: x, y, z”?
guest271314,

Wyjaśniłem, jak ogólnie działa SO. To, czy PO chce wiedzieć, dlaczego jedno rozwiązanie jest lepsze od drugiego, jest całkowicie nieistotne, ponieważ pytania zadane dla SO nie są przede wszystkim PO, ale setek tysięcy ludzi, którzy przeczytają pytanie i odpowiedzi później. Ponadto osoby głosujące na odpowiedzi nie muszą głosować zgodnie z tym, co PO może być zadowolony.
Louis

1

Oto czyste i zwięzłe rozwiązanie:

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

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

Object.values( )zwróci wartości obiektu jako tablicę, a ponieważ argumentsjest obiektem, zasadniczo się skonwertujearguments do tablicy, tym samym umożliwiając korzystanie z wszystkich funkcji pomocniczych tablicy, takich jak map, forEach, filter, itd.


0

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

WYNIK?

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

Cieszyć się !


1
Chciałbym zasugerować zmianę na coś takiego, arguments.length == 1?[arguments[0]]:Array.apply(null, arguments);aby zapobiec wywołaniu funkcji z tylko jednym argumentem liczby całkowitej dummyFn(3)i powoduje wynik[,,]
nieważne

@nevermind dobra robota, edytuję, twoja wersja jest lepsza;) Ale nie rozumiem, dummyFn(3)co to jest? ta funkcja nie istnieje ...
Matryc

Uh ... nieważne, tylko przykład chce pokazać, jak funkcja wywoływana jest tylko jednym argumentem liczby całkowitej i powoduje pustą tablicę o stałym rozmiarze, przepraszam za mój angielski.
nieważne

0

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

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


0

Chociaż parametry odpoczynku działają dobrze, jeśli chcesz nadal używać arguments z jakiegoś powodu , rozważ to

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

[...arguments]można uznać za rodzaj alternatywy dla Array.from(arguments), która również działa doskonale.

Alternatywą dla ES7 jest rozumienie tablic:

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

Może to być najłatwiejsze, jeśli chcesz przetwarzać lub filtrować argumenty przed sortowaniem:

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

Próbowałem prostej techniki niszczenia


0

Obiekt Arguments jest dostępny tylko w treści funkcji. Chociaż można indeksować obiekt argumentów jak tablicę, nie jest to tablica. Nie ma żadnych właściwości tablicy innych niż długość.

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

Chociaż może być indeksowany jak tablica, jak widać w tym przykładzie:

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

Jednak obiekt Arguments nie jest tablicą i nie ma żadnych innych właściwości poza długością.

Możesz przekonwertować obiekt argumentów na tablicę, w której możesz uzyskać dostęp do obiektu Arguments.

Istnieje wiele sposobów dostępu do obiektu argumentów w ciele funkcji, a są to:

  1. możesz wywołać metodę Array.prototoype.slice.call.

Array.prototype.slice.call (argumenty)

function giveMeArgs(arg1,arg2){

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

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

  1. możesz użyć literału tablicowego

[] .slice.call (argumenty).

function giveMeArgs(arg1,arg2){

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

return args;

}

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

  1. możesz użyć Rest ...

function giveMeArgs(...args){

return args;

}

console.log(giveMeArgs(1,2))

  1. możesz użyć rozprzestrzeniania [...]

function giveMeArgs(){

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

}

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

  1. możesz użyć Array.from ()

function giveMeArgs(){

var args = Array.from(arguments);

return args;

}

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


0

Możesz utworzyć funkcję wielokrotnego użytku, aby zrobić to z dowolnymi argumentami, najprostsza jest taka:

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

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

Rozkładaną składnię można zastosować w ES6 i nowszych ...

Ale jeśli chcesz użyć czegoś zgodnego z ES5 i niższymi wersjami , możesz użyć Array.prototype.slice.call, więc kod wygląda następująco:

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

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

Istnieje również kilka innych sposobów, aby to zrobić, na przykład używając Array.fromlub zapętlając argumenty i przypisując je do nowej tablicy ...


-2

To bardzo stare pytanie, ale myślę, że mam rozwiązanie, które jest nieco łatwiejsze do pisania niż poprzednie rozwiązania i nie opiera się na bibliotekach zewnętrznych:

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

2
Array.applynie będzie działać, jeśli masz tylko jeden argument dodatniej liczby całkowitej. To dlatego, że new Array(3)tworzy tablicę o długości 3. Mówiąc ściślej, wynik będzie [undefined, undefined, undefined]i nie [3].
inf3rno
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.