Как я могу преобразовать объект «arguments» в массив в JavaScript?


432

argumentsОбъект в JavaScript является нечетной бородавка-он действует так же , как массив в большинстве случаев, но это не на самом деле объект массива. Так как это на самом деле что - то совсем другое , это не имеет полезные функции от Array.prototypeкак forEach, sort, filter, и map.

Тривиально легко создать новый массив из объекта аргументов с простым циклом for. Например, эта функция сортирует свои аргументы:

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

Однако это довольно жалкая вещь, которую нужно сделать, просто чтобы получить доступ к чрезвычайно полезным функциям массива JavaScript. Есть ли встроенный способ сделать это с помощью стандартной библиотеки?


Ответы:


724

ES6 с использованием остальных параметров

Если вы можете использовать ES6, вы можете использовать:

Остальные параметры

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

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

Как вы можете прочитать в ссылке

Синтаксис остальных параметров позволяет нам представлять неопределенное количество аргументов в виде массива.

Если вам интересен ...синтаксис, он называется Spread Operator, и вы можете прочитать больше здесь .

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 просто преобразуйте объекты типа Array или Iterable в экземпляры Array.



ES5

Вы действительно можете просто использовать Array«s sliceфункцию на объекте аргументов, и он преобразует его в стандартный массив JavaScript. Вам просто нужно ссылаться на него вручную через прототип Array:

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

Почему это работает? Ну, вот выдержка из самой документации ECMAScript 5 :

ПРИМЕЧАНИЕ . sliceФункция специально сделана не привязанной к типу. это не требует, чтобы его значение this было объектом Array. Поэтому его можно передавать другим объектам для использования в качестве метода. Возможность sliceуспешного применения функции к хост-объекту зависит от реализации.

Поэтому sliceработает на все, что имеет lengthсвойство, которое argumentsудобно делает.


Если Array.prototype.sliceдля вас это слишком много, вы можете немного его сократить, используя литералы массива:

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

Однако я склонен считать, что предыдущая версия более явная, поэтому я бы предпочел ее. Злоупотребление массивом буквенной нотации кажется хакерским и выглядит странно.


5
В последних версиях Firefox (2.0?) И ECMAScript 5 есть метод Array.slice, который делает это немного проще. Вы бы назвали это так: var arge = Array.slice (arguments, 0) ;.
Мэтью Крамли

9
Для чего 0? Поскольку нет аргументов, которые вы хотите передать, почему вы не можете просто использовать var args = Array.prototype.slice.call(arguments);? [ Страница аргументов MDC ] ( developer.mozilla.org/en/JavaScript/Reference/… ) предлагает метод без 0.
Питер Айтай

3
@ Барни - сортировка необходима, потому что оригинальный вопрос хочет отсортировать аргументы. Если вы просто хотите преобразовать его в массив, это не обязательно.
Krease

17
«Вы не должны срезать аргументы, потому что это предотвращает оптимизацию в механизмах JavaScript (например, V8)». - от MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
frameworkninja

3
Я не уверен, что отвечает на оригинальный вопрос. Время прошло. С ES6, экспериментированной в Firefox и Chrome, параметр rest становится хорошей альтернативой - function sortArgs(...argsToSort) { return argsToSort.sort(); }от MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Кир Канос,

40

Стоит также обратиться к этой вики-странице библиотеки обещаний Bluebird, которая показывает, как управлять argumentsобъектом в массив таким образом, чтобы сделать функцию оптимизируемой под механизм JavaScript V8 :

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);. Автор также показывает, как шаг сборки может помочь уменьшить детализацию.


2
+1 и спасибо за ссылку на страницу убийцы оптимизации Bluebird. PS: я недавно видел эту оптимизацию, используемую в проекте node-thunkify .
Ив М.

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

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

Некоторые методы массива специально сделаны так, чтобы не требовать, чтобы целевой объект был фактическим массивом. Они только требуют, чтобы у цели было свойство с именем length и indexes (которые должны быть нулевыми или большими целыми числами).

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

12

Использование:

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 версия (медленнее, быстрее, чем ломтик):

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

1
Обратите внимание, что из-за поведения выравнивания в Array.concat он будет принимать аргументы массива. sortArguments (2, [3,0], 1) возвращает 0,1,2,3.
Hafthor

9

Если вы используете jQuery, то, на мой взгляд, гораздо легче запомнить следующее:

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

1
По сути, это то, что я искал в чистом JS, но +1 для убедительного примера того, как jQuery делает JS немного проще в поиске.
Эндрю Колсон

2
«Если не включен еще один тег для фреймворка / библиотеки, ожидается чистый ответ JavaScript».
Михал Перлаковский

6

В ECMAScript 6 нет необходимости использовать такие уродливые хаки, как Array.prototype.slice(). Вместо этого вы можете использовать синтаксис распространения ( ...) .

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

Это может выглядеть странно, но это довольно просто. Он просто извлекает argumentsэлементы и помещает их обратно в массив. Если вы все еще не понимаете, посмотрите эти примеры:

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

Обратите внимание, что он не работает в некоторых старых браузерах, таких как IE 11, поэтому, если вы хотите поддерживать эти браузеры, вы должны использовать Babel .


5

Вот тест нескольких методов, преобразующих аргументы в массив.

Как по мне, лучшее решение для небольшого количества аргументов:

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

+1 за отметки. В настоящее время графики показывают, что вы sortArgsв 6 раз быстрее в Firefox, но в два раза медленнее в последнем Chrome, по сравнению с Array.apply.
Joeytwiddle

1
Поскольку Array.prototype.slice устарел, поскольку он препятствует оптимизации движка JS V8, я считаю, что это лучшее решение, пока ES6 не станет широко доступным +1
Саша

1
Более того, методы универсальных массивов, такие как Array.slice (), также устарели
Саша

4

Я рекомендую использовать оператор распространения ECMAScript 6 , который будет привязывать конечные параметры к массиву. С этим решением вам не нужно прикасаться к argumentsобъекту, и ваш код будет упрощен. Недостатком этого решения является то, что оно не работает в большинстве браузеров, поэтому вместо этого вам придется использовать JS-компилятор, такой как 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();
}

4

Lodash:

var args = _.toArray(arguments);

в действии:

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

производит:

[2,3]

6
Почему нет _.toArray?
Menixator

5
Но _.toArrayэто более уместно.
Мениксатор

Я бы сказал, что _.slice более уместен, поскольку вы часто хотите отбросить некоторые ведущие аргументы.
Hafthor

4

Использование Array.from(), которое принимает массивоподобный объект (такой как arguments) в качестве аргумента и преобразует его в массив:

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

Обратите внимание, что он не работает в некоторых старых браузерах, таких как IE 11, поэтому, если вы хотите поддерживать эти браузеры, вы должны использовать 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

Еще один ответ.

Используйте чёрные магические заклинания:

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

Firefox, Chrome, Node.js, IE11 в порядке.


6
Это определенно лучшее решение ... если вы хотите сделать свой код полностью нечитаемым. Кроме того, __proto__не рекомендуется. Смотрите MDN документы .
Михал Перлаковский

1

Попробуйте использовать Object.setPrototypeOf()

Объяснение: Набор prototypeиз argumentsкArray.prototype

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

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


Объяснение: Взять каждый индекс arguments, поместить элемент в массив с соответствующим индексом массива.

может альтернативно использовать Array.prototype.map()

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

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


Объяснение: Взять каждый индекс arguments, поместить элемент в массив с соответствующим индексом массива.

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

Объяснение: Создать объект, установить свойства объекта для элементов с каждым индексом 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]]]))


Вы спрашивали людей , удовлетворяет ли ваш ответ предупреждению по вопросу о «длинных ответах». Мне кажется, основная проблема с вашим ответом состоит в том, что вы не объясняете, почему следует использовать какой-либо из методов, которые вы здесь показываете. Я бы ни за что не использовал ни одно из предложенных вами решений, потому что все они хуже решений в принятом ответе. Например, ваша ссылка на документ Object.setPrototypeOfпредупреждает, что это «очень медленная операция». С какой стати я использовал это снова Array.prototype.slice.call?
Луи

@ Луис Обновленный ответ с объяснениями для каждого подхода. Вопрос в OP, кажется, "Есть ли встроенный способ сделать это, используя стандартную библиотеку?" , а не «почему следует использовать какой-либо из методов», которые являются встроенными? Как мог любой пользователь, опубликовавший ответ, точно определить, являются ли опубликованные решения «правильными» для ОП? Казалось бы, это зависит от ОП, чтобы определить это? Собственно, именно эта часть уведомления и заинтересовала этого пользователя: «объясните, почему ваш ответ правильный» . Есть ли в ответах на этот вопрос состояние: «Этот ответ« правильный », потому что: x, y, z»?
guest271314

То, что я объяснил, так это то, как SO обычно работает. То, хочет ли OP узнать, почему одно решение лучше другого, совершенно не имеет значения, потому что вопросы, размещенные для SO, предназначены в первую очередь не для OP, а для сотен и тысяч людей, которые будут читать вопрос и его ответы позже. Кроме того, люди, которые голосуют за ответы, не обязаны голосовать в соответствии с тем, чем может быть удовлетворен ФП.
Луи

1

Вот чистое и краткое решение:

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

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

Object.values( )будет возвращать значение объекта в виде массива, а так как argumentsэто объект, он будет существенно преобразовать argumentsв массив, таким образом , предоставляя вам все вспомогательные функции на массиве, такие как map, forEach, filterи т.д.


0

Беншмарк 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

Наслаждайтесь !


1
Хотел бы предложить изменить что-то вроде, arguments.length == 1?[arguments[0]]:Array.apply(null, arguments);чтобы предотвратить вызов функции только с одним целочисленным аргументом dummyFn(3)и вызывает результат[,,]
важно

@ nevermind хорошая работа, я редактирую, ваша версия лучше;) Но я не понимаю, dummyFn(3)что это? эта функция не существует ...
Матрица

Э-э ... не бери в голову, просто в качестве примера хочу показать, как функция вызывается только с одним целочисленным аргументом и вызывает пустой массив фиксированного размера, извините за мой английский.
важно


0

Хотя остальные параметры работают хорошо, если вы хотите продолжить использовать 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()

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

Я попробовал простую технику разрушения


0

Объект 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 не является массивом и не имеет никаких других свойств, кроме length.

Вы можете преобразовать объект arguments в массив, после чего вы сможете получить доступ к объекту Arguments.

Есть много способов получить доступ к объекту arguments внутри тела функции, и они включают в себя:

  1. Вы можете вызвать метод Array.prototoype.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. Вы можете использовать Отдых ...

function giveMeArgs(...args){

return args;

}

console.log(giveMeArgs(1,2))

  1. Вы можете использовать распространение [...]

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


0

Вы можете создать функцию многократного использования, чтобы сделать это с любыми аргументами, самый простой из них:

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или перебирая аргументы и назначая их новому массиву ...


-2

Это очень старый вопрос, но я думаю, что у меня есть решение, которое немного легче набрать, чем предыдущие решения, и не зависит от внешних библиотек:

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

2
Array.applyне будет работать, если у вас есть только один положительный целочисленный аргумент. Это потому, что new Array(3)создает массив длиной 3. Если быть более конкретным, то результат будет [undefined, undefined, undefined]и нет [3].
inf3rno
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.