Як я можу перетворити об’єкт "аргументи" на масив у JavaScript?


433

argumentsОб'єкт в JavaScript є непарною бородавка-он діє так само , як масив в більшості випадків, але це не на самому ділі об'єкт масиву. Так як це на самому справі що - то зовсім інше , це не має корисні функції від Array.prototypeяк forEach, sort, filter, і map.

Побудувати новий масив з об’єкта аргументів просто, для циклу просто непросто. Наприклад, ця функція сортує свої аргументи:

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

Однак це досить жалюгідна річ, щоб зробити доступ до надзвичайно корисних функцій масиву JavaScript. Чи існує вбудований спосіб зробити це за допомогою стандартної бібліотеки?


Відповіді:


725

ES6 з використанням параметрів спокою

Якщо ви можете використовувати ES6, ви можете використовувати:

Параметри відпочинку

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

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

Як ви можете прочитати за посиланням

Синтаксис параметра «решта» дозволяє представляти невизначене число аргументів як масив.

Якщо вам цікаво ...синтаксис, він називається Opead Spread, і ви можете прочитати більше тут .

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Функція навмисно загальна; не потрібно, щоб його це значення було об'єктом масиву. Тому його можна перенести на інші види об’єктів для використання в якості методу. Від того, чи sliceможе функція успішно застосовуватися до хост-об'єкта, залежить від реалізації.

Тому sliceпрацює над усім, що має lengthвластивість, що argumentsзручно робити.


Якщо у вас Array.prototype.sliceзанадто багато в рот, ви можете скоротити його, використовуючи літерали масиву:

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

Однак я схильний вважати, що колишня версія більш чітка, тому я вважаю за краще її. Зловживання буквеними позначеннями масиву відчувається хитким і виглядає дивно.


5
В останніх версіях Firefox (2.0?) Та ECMAScript 5 є метод Array.slice, який робить це трохи простіше. Ви б назвали це так: var arge = Array.slice (аргументи, 0) ;.
Меттью Крамлі

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

3
@Barney - сортування необхідне, оскільки оригінальне запитання хоче сортувати аргументи. Якщо ви просто хочете перетворити його в масив, це не обов'язково.
Креаза

17
"Ви не повинні нарізати аргументи, оскільки це запобігає оптимізації в двигунах JavaScript (наприклад, V8)." - від MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Frameworkninja

3
Я не впевнений, що це відповідь на початкове запитання. Час минув. Із експериментом ES6 у Firefox та Chrome параметр відпочинку стає гарною альтернативою - 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: Я нещодавно бачив цю оптимізацію, що використовується в проекті tunkify .
Ів М.

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

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

Деякі методи масиву навмисно зроблені, щоб не вимагати, щоб цільовий об'єкт був фактичним масивом. Вони вимагають лише, щоб ціль містила властивість з назвою довжина та індекси (які повинні бути нульовими чи більшими цілими числами).

[].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 він прийме аргументи масиву. сортування Аргументи (2, [3,0], 1) повертає 0,1,2,3.
Хафтор

9

Якщо ви використовуєте jQuery, на мою думку, запам'ятати наступне:

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

1
Це, по суті, те, що я шукав у чистому JS, але +1 для ґрунтовного прикладу того, як jQuery робить JS трохи простішим.
Ендрю Коулсон

2
"Якщо не включений ще один тег для рамки / бібліотеки, очікується чистий відповідь JavaScript."
Michał Perłakowski

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у Firefox у 6 разів швидше, але в останньому Chrome вдвічі повільніше, порівняно з Array.apply.
joeytwiddle

1
Оскільки Array.prototype.slice застарілий, оскільки він перешкоджає оптимізації двигунів JS V8, я вважаю це найкращим рішенням, поки ES6 не стане широко доступним +1
Саша,

1
Більше того, застарілі методи Array.slice () також застаріли
Саша,

4

Я рекомендую використовувати оператор розповсюдження ECMAScript 6 , який прив’яже параметри трейлінгу до масиву. За допомогою цього рішення вам не потрібно чіпати argumentsоб’єкт, і ваш код буде спрощений. Мінусом цього рішення є те, що воно не працює в більшості браузерів, тому замість цього вам доведеться використовувати компілятор JS, такий як Babel. Під капотом 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

Лодаш:

var args = _.toArray(arguments);

в дії:

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

виробляє:

[2,3]

6
Чому ні _.toArray?
Меніксатор

5
Але _.toArrayє більш доречним.
Меніксатор

Я б заперечив, що _.slice є більш доречним, оскільки ви часто хочете відмовитись від деяких основних аргументів.
Хафтор

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

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?
Луї

@Louis оновлений відповідь з поясненнями для кожного підходу. Питання в ОП видається таким: "Чи існує вбудований спосіб зробити це за допомогою стандартної бібліотеки?" , а не "чому слід використовувати будь-який із вбудованих методів" ? Як будь-який користувач, який опублікував відповідь, може точно визначити, чи розміщені рішення "правильні" для ОП? Здавалося б, це залежить від ОП? Власне, ця частина сповіщення зацікавила цього користувача: "поясніть, чому ваша відповідь правильна" . Чи будь-який із відповідей, наданих на це запитання, зазначає: "Ця відповідь є" правильною ", оскільки: x, y, z"?
гість271314

Я пояснив, як загалом працює SO. Незалежно від того, чи хоче ОП дізнатися, чому одне рішення краще, ніж інше, абсолютно не має значення, оскільки питання, задані для ПЗ, стосуються не в першу чергу ОП, а для сотень і тисяч людей, які згодом прочитають питання та його відповіді. Крім того, люди, які голосують за відповіді, не зобов'язані голосувати відповідно до того, чим може бути задоволена ОП.
Луї

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)і викликає результат[,,]
неважливо

@ всякий раз, коли я добре розумію, я редагую, ваша версія краще;) Але я не розумію, dummyFn(3)що це? цієї функції не існує ...
Матриця

Ну, не маю на увазі, лише на прикладі хочу показати, як функція викликається лише одним цілим аргументом і викликає порожній масив фіксованого розміру, вибачте за мою англійську.
ніколи не

0

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

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


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

Об'єкт Аргументи доступний лише всередині функції функції. Хоча ви можете проіндексувати об’єкт аргументів як масив, він не є масивом. У нього немає властивостей масиву, крім довжини.

// 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.

Існує багато способів отримати доступ до об'єкта аргументів всередині функції функції, і це:

  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.