Làm cách nào tôi có thể chuyển đổi đối số các đối số của người Viking thành một mảng trong JavaScript?


433

Đối argumentstượng trong JavaScript là một mụn cóc kỳ lạ, nó hoạt động giống như một mảng trong hầu hết các tình huống, nhưng thực tế nó không phải một đối tượng mảng. Vì nó là thực sự một cái gì đó khác hoàn toàn , nó không có các chức năng hữu ích từ Array.prototypenhư forEach, sort, filter, và map.

Thật dễ dàng để xây dựng một mảng mới từ một đối tượng đối số với một vòng lặp for đơn giản. Ví dụ, hàm này sắp xếp các đối số của nó:

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

Tuy nhiên, đây là một điều khá đáng tiếc phải làm chỉ đơn giản là để có quyền truy cập vào các hàm mảng JavaScript cực kỳ hữu ích. Có một cách tích hợp để làm điều đó bằng cách sử dụng thư viện tiêu chuẩn?


Câu trả lời:


725

ES6 sử dụng các tham số nghỉ ngơi

Nếu bạn có thể sử dụng ES6, bạn có thể sử dụng:

Thông số phần còn lại

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

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

Như bạn có thể đọc trong liên kết

Cú pháp tham số còn lại cho phép chúng ta biểu diễn số lượng đối số không xác định dưới dạng một mảng.

Nếu bạn tò mò về ...cú pháp, nó được gọi là Toán tử trải rộng và bạn có thể đọc thêm ở đây .

ES6 sử dụng Array.from ()

Sử dụng 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 chỉ cần chuyển đổi các đối tượng giống như Array hoặc Iterable thành các thể hiện Array.



ES5

Bạn có thể thực sự chỉ cần sử dụng Array's slicechức năng trên một đối tượng tranh cãi, và nó sẽ chuyển đổi nó thành một mảng JavaScript chuẩn. Bạn sẽ chỉ cần tham chiếu thủ công thông qua nguyên mẫu của Array:

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

Tại sao điều này làm việc? Chà, đây là một đoạn trích từ chính tài liệu ECMAScript 5 :

LƯU Ý : sliceHàm này có chủ ý chung chung; nó không yêu cầu giá trị này của nó là một đối tượng Array. Do đó, nó có thể được chuyển sang các loại đối tượng khác để sử dụng như một phương thức. Liệu slicechức năng có thể được áp dụng thành công cho một đối tượng máy chủ hay không phụ thuộc vào việc thực hiện.

Do đó, slicelàm việc trên bất cứ thứ gì có lengthtài sản, argumentsthuận tiện làm.


Nếu Array.prototype.slicequá nhiều lời nói cho bạn, bạn có thể viết tắt nó một chút bằng cách sử dụng mảng chữ:

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

Tuy nhiên, tôi có xu hướng cảm thấy rằng phiên bản cũ rõ ràng hơn, vì vậy tôi thích nó hơn. Lạm dụng các ký hiệu nghĩa đen mảng cảm thấy hacky và trông lạ.


5
Trong các phiên bản Firefox gần đây (2.0?) Và ECMAScript 5, có một phương thức Array.slice giúp việc này đơn giản hơn một chút. Bạn sẽ gọi nó như thế này: var arge = Array.slice (argument, 0);.
Matthew Crumley

9
Để làm gì 0? Vì không có đối số nào bạn muốn vượt qua, tại sao bạn không thể sử dụng var args = Array.prototype.slice.call(arguments);? [ Trang đối số MDC ] ( developer.mozilla.org/en/JavaScript/Reference/ tựa ) gợi ý phương thức mà không có 0.
Peter Ajtai

3
@Barney - sắp xếp là cần thiết vì câu hỏi ban đầu muốn sắp xếp các đối số. Nếu bạn chỉ muốn chuyển đổi nó thành một mảng, thì không cần thiết.
Giảm

17
"Bạn không nên cắt các đối số vì nó ngăn chặn tối ưu hóa trong các công cụ JavaScript (ví dụ V8)." - từ MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
frameworkninja

3
Tôi không chắc đó là trả lời cho câu hỏi ban đầu. Thời gian thấm thoắt trôi qua. Với ES6 đã được thử nghiệm trên Firefox và Chrome, tham số còn lại đang trở thành một lựa chọn thay thế tốt - function sortArgs(...argsToSort) { return argsToSort.sort(); }từ MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
trộm

40

Cũng đáng tham khảo trang wiki thư viện hứa hẹn này cho thấy cách quản lý argumentsđối tượng thành mảng theo cách làm cho chức năng được tối ưu hóa trong công cụ JavaScript V8 :

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

Phương pháp này được sử dụng có lợi cho var args = [].slice.call(arguments);. Tác giả cũng chỉ ra làm thế nào một bước xây dựng có thể giúp giảm mức độ chi tiết.


2
+1 và cảm ơn vì liên kết đến trang Bluebird Optimization Killer. PS: Gần đây tôi đã thấy tối ưu hóa này được sử dụng trong dự án nút-thunkify .
Yves M.

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

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

Một số phương thức mảng được cố ý thực hiện không yêu cầu đối tượng đích là một mảng thực tế. Họ chỉ yêu cầu mục tiêu phải có một thuộc tính có tên là độ dài và chỉ số (phải là số nguyên bằng 0 hoặc lớn hơn).

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

12

Sử dụng:

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

Array(arg1, arg2, ...) trả lại [arg1, arg2, ...]

Array(str1) trả lại [str1]

Array(num1)trả về một mảng có num1các phần tử

Bạn phải kiểm tra số lượng đối số!

Array.slice phiên bản (chậm hơn):

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

Array.push phiên bản (chậm hơn, nhanh hơn lát):

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

Di chuyển phiên bản (chậm hơn, nhưng kích thước nhỏ nhanh hơn):

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

Array.concat phiên bản (chậm nhất):

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

1
Lưu ý rằng vì hành vi làm phẳng trong Array.concat, nó sẽ chấp nhận các đối số mảng. sortArgument (2, [3.0], 1) trả về 0,1,2,3.
Hafthor

9

Nếu tôi đang sử dụng jQuery, thì theo tôi đây là một cách dễ nhớ hơn:

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

1
Đó thực chất là những gì tôi đang tìm kiếm trong JS thuần túy, nhưng +1 cho ví dụ chắc chắn về cách jQuery làm cho JS dễ dàng hơn một chút.
Andrew Coleson

2
"Trừ khi một thẻ khác cho khung / thư viện cũng được bao gồm, một câu trả lời JavaScript thuần túy được mong đợi."
Michał Perłakowski

6

Trong ECMAScript 6, không cần phải sử dụng các bản hack xấu xí như thế nào Array.prototype.slice(). Thay vào đó, bạn có thể sử dụng cú pháp lây lan ( ...) .

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

Nó có thể trông lạ, nhưng nó khá đơn giản. Nó chỉ trích xuất argumentscác phần tử và đưa chúng trở lại mảng. Nếu bạn vẫn không hiểu, hãy xem ví dụ này:

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

Lưu ý rằng nó không hoạt động trong một số trình duyệt cũ hơn như IE 11, vì vậy nếu bạn muốn hỗ trợ các trình duyệt này, bạn nên sử dụng Babel .


5

Dưới đây là điểm chuẩn của một số phương thức chuyển đổi đối số thành mảng.

Đối với tôi, giải pháp tốt nhất cho số lượng nhỏ các đối số là:

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

Đối với các trường hợp khác:

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

+1 cho điểm chuẩn. Hiện tại các biểu đồ cho thấy bạn sortArgsnhanh hơn 6 lần trong Firefox, nhưng chậm hơn gấp đôi so với Chrome mới nhất, so với Array.apply.
joeytwiddle

1
Vì Array.prototype.slice không được chấp nhận vì nó ngăn chặn tối ưu hóa động cơ V8 V8, tôi coi đây là giải pháp tốt nhất cho đến khi ES6 sẽ được phổ biến rộng rãi +1
Sasha

1
Hơn nữa, các phương thức tổng quát của Array như Array.slice () cũng không được dùng nữa
Sasha

4

Tôi khuyên bạn nên sử dụng toán tử trải ECMAScript 6 , sẽ ràng buộc các tham số theo dõi đến một mảng. Với giải pháp này, bạn không cần phải chạm vào argumentsđối tượng và mã của bạn sẽ được đơn giản hóa. Nhược điểm của giải pháp này là nó không hoạt động trên hầu hết các trình duyệt, vì vậy thay vào đó bạn sẽ phải sử dụng trình biên dịch JS như Babel. Dưới mui xe Babel biến argumentsthành một Array với một vòng lặp for.

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

Nếu bạn không thể sử dụng ECMAScript 6, tôi khuyên bạn nên xem một số câu trả lời khác như @Jonathan Fingerland

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

4

Nhà nghỉ:

var args = _.toArray(arguments);

trong hành động:

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

sản xuất:

[2,3]

6
Tại sao không _.toArray?
Menixator

5
Nhưng _.toArraylà thích hợp hơn.
Menixator

Tôi sẽ tranh luận _.slice là phù hợp hơn vì bạn thường muốn bỏ một số đối số hàng đầu.
Hafthor

4

Sử dụng Array.from(), lấy một đối tượng giống như mảng (như arguments) làm đối số và chuyển đổi nó thành mảng:

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

Lưu ý rằng nó không hoạt động trong một số trình duyệt cũ hơn như IE 11, vì vậy nếu bạn muốn hỗ trợ các trình duyệt này, bạn nên sử dụng 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

Một câu trả lời khác.

Sử dụng phép thuật ma thuật đen:

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

Firefox, Chrome, Node.js, IE11 đều ổn.


6
Đây chắc chắn là giải pháp tốt nhất ... nếu bạn muốn làm cho mã của mình hoàn toàn không thể đọc được. Ngoài ra, __proto__là không tán thành. Xem tài liệu MDN .
Michał Perłakowski

1

Hãy thử sử dụng Object.setPrototypeOf()

Giải thích: Thiết lập prototypecủa argumentsđểArray.prototype

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

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


Giải thích: Lấy từng chỉ mục của arguments, đặt vật phẩm vào một mảng tại chỉ mục tương ứng của mảng.

thay thế có thể sử dụng Array.prototype.map()

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

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


Giải thích: Lấy từng chỉ mục của arguments, đặt vật phẩm vào một mảng tại chỉ mục tương ứng của mảng.

for..of vòng

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

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


hoặc là Object.create()

Giải thích: Tạo đối tượng, đặt thuộc tính của đối tượng thành các mục tại mỗi chỉ mục của arguments; tập hợp prototypecác đối tượng được tạo thànhArray.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]]]))


Bạn đã hỏi mọi người rằng câu trả lời của bạn có thỏa mãn cảnh báo dưới câu hỏi về "câu trả lời dài" không. Dường như với tôi vấn đề chính với câu trả lời của bạn là bạn không giải thích được tại sao người ta nên sử dụng bất kỳ phương pháp nào bạn trình bày ở đây. Không có cách nào tôi sẽ sử dụng bất kỳ giải pháp nào bạn đề xuất bởi vì tất cả chúng đều tệ hơn các giải pháp trong câu trả lời được chấp nhận. Ví dụ, tài liệu tham khảo của bạn đến tài liệu Object.setPrototypeOfcảnh báo rằng đó là "hoạt động rất chậm". Tại sao trên trái đất tôi sẽ sử dụng này Array.prototype.slice.call?
Louis

@Louis Cập nhật câu trả lời với lời giải thích cho từng phương pháp. Câu hỏi tại OP dường như là "Có cách tích hợp nào để thực hiện bằng thư viện chuẩn không?" , không phải "tại sao người ta nên sử dụng bất kỳ phương thức nào" được tích hợp sẵn? Làm thế nào bất kỳ người dùng nào đã đăng một Câu trả lời xác định chính xác nếu các giải pháp được đăng là "đúng" cho OP? Có vẻ như là tùy thuộc vào OP để xác định điều này? Trên thực tế, phần thông báo này là điều khiến người dùng này quan tâm: "giải thích tại sao câu trả lời của bạn là đúng" . Có bất kỳ câu trả lời nào được cung cấp cho trạng thái Câu hỏi này không: "Câu trả lời này là" đúng "vì: x, y, z"?
khách271314

Những gì tôi đã giải thích là làm thế nào SO thường hoạt động. Việc OP có muốn biết tại sao một giải pháp tốt hơn giải pháp kia hay không hoàn toàn không liên quan, bởi vì các câu hỏi được đăng cho SO không phải chủ yếu dành cho OP mà là cho hàng trăm nghìn người sẽ đọc câu hỏi và câu trả lời sau đó. Ngoài ra, những người bỏ phiếu cho câu trả lời không bắt buộc phải bỏ phiếu theo bất cứ điều gì OP có thể hài lòng.
Louis

1

Đây là một giải pháp sạch sẽ và súc tích:

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

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

Object.values( )sẽ trở lại các giá trị của một đối tượng như là một mảng, và vì argumentslà một đối tượng, nó về cơ bản sẽ chuyển đổi argumentsthành một mảng, do đó cung cấp cho bạn với tất cả các chức năng helper của một mảng như map, forEach, filtervv


0

Benshmarck 3 phương pháp:

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

KẾT QUẢ?

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

Thưởng thức !


1
Muốn đề xuất sửa đổi thành một cái gì đó như arguments.length == 1?[arguments[0]]:Array.apply(null, arguments);để ngăn chặn hàm được gọi chỉ với một đối số nguyên dummyFn(3)và gây ra kết quả[,,]
không bao giờ

@nevermind công việc tốt, tôi chỉnh sửa, phiên bản của bạn tốt hơn;) Nhưng tôi không hiểu dummyFn(3)đây là gì? chức năng này không tồn tại ...
Ma trận

Uh ... đừng bận tâm, chỉ là một ví dụ muốn chỉ ra cách một hàm được gọi chỉ với một đối số nguyên và gây ra một mảng trống có kích thước cố định, xin lỗi cho tiếng Anh của tôi.
không bao giờ bắt đầu

0

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

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


0

Mặc dù các tham số nghỉ ngơi hoạt động tốt, nhưng nếu bạn muốn tiếp tục sử dụng argumentsvì một số lý do, hãy xem xét

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

[...arguments]có thể được coi là một loại thay thế Array.from(arguments), cũng hoạt động hoàn toàn tốt.

Một thay thế ES7 là một sự hiểu biết mảng:

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

Điều này có thể dễ nhất nếu bạn muốn xử lý hoặc lọc các đối số trước khi sắp xếp:

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

Tôi đã thử kỹ thuật phá hủy đơn giản


0

Đối tượng Argument chỉ khả dụng bên trong một thân hàm. Mặc dù bạn có thể lập chỉ mục Đối tượng đối số giống như một mảng, nhưng nó không phải là một mảng. Nó không có bất kỳ thuộc tính mảng nào ngoài chiều dài.

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

Mặc dù nó có thể được lập chỉ mục như một mảng như bạn có thể thấy trong ví dụ này:

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

Tuy nhiên, đối tượng Đối số không phải là một mảng và không có bất kỳ thuộc tính nào khác ngoài độ dài.

Bạn có thể chuyển đổi đối tượng đối số thành một mảng mà tại đó bạn có thể truy cập đối tượng Đối số.

Có nhiều cách bạn có thể truy cập đối tượng đối số bên trong một thân hàm và chúng bao gồm:

  1. bạn có thể gọi phương thức Array.prototoype.slice.call.

Array.prototype.slice.call (đối số)

function giveMeArgs(arg1,arg2){

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

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

  1. bạn có thể sử dụng mảng theo nghĩa đen

[] .slice.call (đối số).

function giveMeArgs(arg1,arg2){

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

return args;

}

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

  1. bạn có thể sử dụng phần còn lại ...

function giveMeArgs(...args){

return args;

}

console.log(giveMeArgs(1,2))

  1. bạn có thể sử dụng lây lan [...]

function giveMeArgs(){

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

}

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

  1. bạn có thể sử dụng Array.from ()

function giveMeArgs(){

var args = Array.from(arguments);

return args;

}

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


0

Bạn có thể tạo một hàm có thể sử dụng lại để thực hiện nó với bất kỳ đối số nào, đơn giản nhất là như thế này:

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

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

Cú pháp lây lan có thể được sử dụng trong ES6 trở lên ...

Nhưng nếu bạn muốn sử dụng một cái gì đó tương thích với ES5 trở xuống, bạn có thể sử dụng Array.prototype.slice.call, vì vậy mã của bạn trông như thế này:

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

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

Ngoài ra còn có một số cách khác để làm điều này, ví dụ sử dụng Array.fromhoặc lặp qua các đối số và gán chúng cho một mảng mới ...


-2

Đây là một câu hỏi rất cũ, nhưng tôi nghĩ rằng tôi có một giải pháp dễ gõ hơn một chút so với các giải pháp trước đó và không dựa vào các thư viện bên ngoài:

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

2
Array.applysẽ không hoạt động nếu bạn chỉ có một đối số nguyên dương duy nhất. Đó là bởi vì new Array(3)tạo ra một chiều dài mảng 3. Để được cụ thể hơn, kết quả sẽ được [undefined, undefined, undefined]và không [3].
inf3rno
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.