JavaScript số lượng đối số để hoạt động


531

Có cách nào để cho phép các vars "không giới hạn" cho một hàm trong JavaScript không?

Thí dụ:

load(var1, var2, var3, var4, var5, etc...)
load(var1)


liên quan / có thể trùng lặp của stackoverflow.com/questions/4633125/ Kẻ
Luke

1
@Luke không, không phải vậy. Câu hỏi đó hỏi làm thế nào để gọi một hàm với số lượng đối số tùy ý với các đối số trong một mảng. Điều này hỏi làm thế nào để xử lý một cuộc gọi như vậy.
Scruffy

1
Để dễ dàng tìm kiếm hơn, một chức năng như vậy được gọi là "chức năng dao động".
gschenk

Câu trả lời:


814

Chắc chắn, chỉ cần sử dụng các argumentsđối tượng.

function foo() {
  for (var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);
  }
}

1
Tnx. Thật tuyệt vời khi phân tích chuỗi từ mã gốc Android sang javascript trong Webview.
Johan Hoeksma

4
Giải pháp này làm việc tốt nhất cho tôi. Cảm ơn. Thông tin thêm về từ khóa đối số TẠI ĐÂY .
dùng2

26
argumentslà một đối tượng "giống như mảng" đặc biệt, có nghĩa là nó có độ dài, nhưng không có các hàm mảng khác. Xem developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ Khăn để biết thêm thông tin và câu trả lời này: stackoverflow.com/a/13145228/1766230
Luke

4
Thật thú vị, các phương thức Array trong Javascript đã được định nghĩa theo cách chúng hoạt động trên bất kỳ đối tượng nào có thuộc lengthtính. Điều này bao gồm các argumentsđối tượng. Biết được điều này và phương thức concattrả về một bản sao của 'mảng' mà nó được gọi, chúng ta có thể dễ dàng chuyển đổi argumentsđối tượng thành một mảng thực như thế này : var args = [].concat.call(arguments). Một số người thích sử dụng Array.prototype.concat.callthay thế, nhưng tôi thích [], chúng ngắn và ngọt ngào!
Stijn de Witt

2
@YasirJan sử dụng[...arguments].join()
Willian D. Andrade

179

Trong (hầu hết) các trình duyệt gần đây, bạn có thể chấp nhận số lượng đối số khác nhau với cú pháp này:

function my_log(...args) {
     // args is an Array
     console.log(args);
     // You can pass this array as parameters to another function
     console.log(...args);
}

Đây là một ví dụ nhỏ:

function foo(x, ...args) {
  console.log(x, args, ...args, arguments);
}

foo('a', 'b', 'c', z='d')

=>

a
Array(3) [ "b", "c", "d" ]
b c d
Arguments
    0: "a"
    1: "b"
    2: "c"
    3: "d"
    length: 4

Tài liệu và nhiều ví dụ khác tại đây: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Fiances/rest_parameter


27
FYI nó được gọi là "cú pháp tham số phần còn lại": developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
Kẻ

4
+1 Đây là giải pháp thanh lịch và sạch sẽ. Đặc biệt thích hợp để chuyển qua danh sách dài các tham số vào một lệnh gọi hàm khác và có thể các tham số biến đó ở vị trí tùy ý.
haxpor

3
Hãy nhớ rằng theo developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/, nó không được hỗ trợ trên IE.
Roee Gavirel

2
Đây là bảng hiển thị hỗ trợ trình duyệt - caniuse.com/#feat=rest-parameter
Brian Burns

1
Thật là một câu trả lời hay!
Ashish

113

Một tùy chọn khác là chuyển các đối số của bạn trong một đối tượng ngữ cảnh.

function load(context)
{
    // do whatever with context.name, context.address, etc
}

và sử dụng nó như thế này

load({name:'Ken',address:'secret',unused:true})

Điều này có lợi thế là bạn có thể thêm bao nhiêu đối số được đặt tên mà bạn muốn và hàm có thể sử dụng chúng (hoặc không) khi nó thấy phù hợp.


4
Điều này sẽ tốt hơn vì nó loại bỏ khớp nối theo thứ tự đối số. Các giao diện được ghép lỏng lẻo là thực hành tiêu chuẩn tốt ...
Jonas Schubert Erlandsson

9
Chắc chắn, điều đó tốt hơn trong một số trường hợp. Nhưng giả sử các đối số riêng lẻ không thực sự liên quan đến nhau hoặc tất cả đều được cho là có ý nghĩa như nhau (như các phần tử mảng). Vậy thì cách của OP là tốt nhất.
rvighne

1
Điều này cũng tốt vì nếu bạn muốn, bạn có thể xây dựng contextđối số bằng mã và chuyển nó xung quanh trước khi nó được sử dụng.
Nate CK

46

Tôi đồng ý với câu trả lời của Ken là năng động nhất và tôi muốn tiến thêm một bước nữa. Nếu đó là một hàm mà bạn gọi nhiều lần với các đối số khác nhau - tôi sử dụng thiết kế của Ken nhưng sau đó thêm các giá trị mặc định:

function load(context) {

    var defaults = {
        parameter1: defaultValue1,
        parameter2: defaultValue2,
        ...
    };

    var context = extend(defaults, context);

    // do stuff
}

Theo cách này, nếu bạn có nhiều tham số nhưng không nhất thiết phải đặt chúng với mỗi lệnh gọi cho hàm, bạn chỉ cần chỉ định các giá trị không mặc định. Đối với phương thức mở rộng, bạn có thể sử dụng phương thức mở rộng của jQuery ( $.extend()), tự tạo phương thức của riêng bạn hoặc sử dụng các cách sau:

function extend() {
    for (var i = 1; i < arguments.length; i++)
        for (var key in arguments[i])
            if (arguments[i].hasOwnProperty(key))
                arguments[0][key] = arguments[i][key];
    return arguments[0];
}

Điều này sẽ hợp nhất đối tượng bối cảnh với các mặc định và điền vào bất kỳ giá trị không xác định nào trong đối tượng của bạn với các mặc định.


3
+1. Bí quyết đẹp. Lưu rất nhiều tấm nồi hơi để có mọi tham số được xác định, mặc định hoặc cách khác.
Neil

1
_.defaults()Phương thức của Underscore là một thay thế rất hay để hợp nhất các đối số được chỉ định và mặc định.
mbeasley

18

Vâng, giống như thế này:

function load()
{
  var var0 = arguments[0];
  var var1 = arguments[1];
}

load(1,2);

18

Tốt nhất là sử dụng cú pháp tham số phần còn lại như Ramast đã chỉ ra.

function (a, b, ...args) {}

Tôi chỉ muốn thêm một số thuộc tính tốt đẹp của ... đối số args

  1. Nó là một mảng, và không phải là một đối tượng như các đối số. Điều này cho phép bạn áp dụng các chức năng như bản đồ hoặc sắp xếp trực tiếp.
  2. Nó không bao gồm tất cả các tham số mà chỉ có một tham số được truyền từ nó vào. Ví dụ hàm (a, b, ... args) trong trường hợp này args chứa đối số 3 đến argument.length

15

Như đã đề cập, bạn có thể sử dụng arguments đối tượng để lấy một số lượng tham số hàm khác nhau.

Nếu bạn muốn gọi một hàm khác có cùng đối số, hãy sử dụng apply. Bạn thậm chí có thể thêm hoặc xóa các đối số bằng cách chuyển đổi argumentsthành một mảng. Ví dụ, chức năng này chèn một số văn bản trước khi đăng nhập vào bảng điều khiển:

log() {
    let args = Array.prototype.slice.call(arguments);
    args = ['MyObjectName', this.id_].concat(args);
    console.log.apply(console, args);
}

giải pháp tốt đẹp để chuyển đổi đối số thành mảng. Nó rất hữu ích cho tôi ngày hôm nay.
Dmytro Medvid

8

Mặc dù tôi thường đồng ý rằng cách tiếp cận đối số được đặt tên là hữu ích và linh hoạt (trừ khi bạn quan tâm đến thứ tự, trong trường hợp đó là đối số dễ nhất), tôi có lo ngại về chi phí của phương pháp mbeasley (sử dụng mặc định và mở rộng). Đây là một khoản chi phí cực lớn phải mất để kéo các giá trị mặc định. Đầu tiên, mặc định được xác định bên trong hàm, vì vậy chúng được lặp lại trên mỗi cuộc gọi. Thứ hai, bạn có thể dễ dàng đọc ra các giá trị được đặt tên và đặt mặc định cùng lúc bằng cách sử dụng | |. Không cần phải tạo và hợp nhất một đối tượng mới khác để có được thông tin này.

function load(context) {
   var parameter1 = context.parameter1 || defaultValue1,
       parameter2 = context.parameter2 || defaultValue2;

   // do stuff
}

Đây là khoảng cùng một lượng mã (có thể nhiều hơn một chút), nhưng nên là một phần nhỏ của chi phí thời gian chạy.


Đồng ý, mặc dù tác hại phụ thuộc vào loại giá trị hoặc mặc định của chính nó. Mặt khác, (parameter1=context.parameter1)===undefined && (parameter1=defaultValue1)hoặc với khối lượng mã ít hơn, một hàm trợ giúp nhỏ như: function def(providedValue, default) {return providedValue !== undefined ? providedValue : defaultValue;} var parameter1 = def(context.parameter1, defaultValue1)cung cấp các mẫu thay thế. Tuy nhiên, quan điểm của tôi vẫn đứng vững: tạo thêm các đối tượng cho mọi lệnh gọi hàm và chạy các vòng lặp đắt tiền để đặt một vài giá trị mặc định là một số tiền quá lớn.
mcurland

7

Mặc dù @roufamatic đã cho thấy việc sử dụng từ khóa đối số và @Ken đã cho thấy một ví dụ tuyệt vời về đối tượng sử dụng, tôi cảm thấy không thực sự giải quyết được những gì đang diễn ra trong trường hợp này và có thể gây nhầm lẫn cho người đọc trong tương lai hoặc đưa ra một thực tiễn xấu là không nêu rõ chức năng / phương thức được dự định để lấy một lượng đối số / tham số khác nhau.

function varyArg () {
    return arguments[0] + arguments[1];
}

Khi một nhà phát triển khác đang xem qua mã của bạn, rất dễ giả sử chức năng này không lấy tham số. Đặc biệt là nếu nhà phát triển đó không bí mật với từ khóa đối số . Bởi vì điều này là một ý tưởng tốt để làm theo một hướng dẫn phong cách và nhất quán. Tôi sẽ sử dụng Google cho tất cả các ví dụ.

Chúng ta hãy nói rõ chức năng tương tự có các tham số biến:

function varyArg (var_args) {
    return arguments[0] + arguments[1];
}

Tham số đối tượng VS var_args

Có thể đôi khi một đối tượng là cần thiết vì nó là phương pháp thực hành tốt nhất được phê duyệt và được coi là tốt nhất của bản đồ dữ liệu. Mảng liên kết được nhăn mặt và không khuyến khích.

SIDENOTE: Từ khóa đối số thực sự trả về một đối tượng sử dụng số làm khóa. Di truyền nguyên mẫu cũng là họ đối tượng. Xem phần cuối của câu trả lời để sử dụng mảng thích hợp trong JS

Trong trường hợp này, chúng tôi cũng có thể nói rõ điều này. Lưu ý: quy ước đặt tên này không được cung cấp bởi Google nhưng là một ví dụ về khai báo rõ ràng về loại param. Điều này rất quan trọng nếu bạn đang tìm cách tạo một kiểu gõ chặt chẽ hơn trong mã của mình.

function varyArg (args_obj) {
    return args_obj.name+" "+args_obj.weight;
}
varyArg({name: "Brian", weight: 150});

Chọn cái nào?

Điều này phụ thuộc vào nhu cầu của chức năng và chương trình của bạn. Ví dụ, nếu bạn chỉ đơn giản là tìm cách trả về một cơ sở giá trị trên một quy trình lặp trên tất cả các đối số được thông qua thì chắc chắn sẽ gắn bó với từ khóa đối số . Nếu bạn cần định nghĩa cho các đối số và ánh xạ dữ liệu thì phương thức đối tượng là cách để đi. Hãy xem xét hai ví dụ và sau đó chúng ta đã hoàn tất!

Sử dụng đối số

function sumOfAll (var_args) {
    return arguments.reduce(function(a, b) {
        return a + b;
    }, 0);
}
sumOfAll(1,2,3); // returns 6

Đối tượng sử dụng

function myObjArgs(args_obj) {
    // MAKE SURE ARGUMENT IS AN OBJECT OR ELSE RETURN
    if (typeof args_obj !== "object") {
        return "Arguments passed must be in object form!";
    }

    return "Hello "+args_obj.name+" I see you're "+args_obj.age+" years old.";
}
myObjArgs({name: "Brian", age: 31}); // returns 'Hello Brian I see you're 31 years old

Truy cập một mảng thay vì một đối tượng ("... args" Tham số còn lại)

Như đã đề cập ở đầu câu trả lời, từ khóa đối số thực sự trả về một đối tượng. Do đó, bất kỳ phương thức nào bạn muốn sử dụng cho một mảng sẽ phải được gọi. Một ví dụ về điều này:

Array.prototype.map.call(arguments, function (val, idx, arr) {});

Để tránh điều này, hãy sử dụng tham số phần còn lại:

function varyArgArr (...var_args) {
    return var_args.sort();
}
varyArgArr(5,1,3); // returns 1, 3, 5

5

Sử dụng argumentsđối tượng khi bên trong hàm để có quyền truy cập vào tất cả các đối số được truyền vào.


3

Xin lưu ý rằng việc chuyển một Đối tượng có thuộc tính được đặt tên như Ken đề xuất sẽ thêm chi phí phân bổ và giải phóng đối tượng tạm thời cho mỗi cuộc gọi. Vượt qua các đối số bình thường theo giá trị hoặc tham chiếu thường sẽ hiệu quả nhất. Đối với nhiều ứng dụng mặc dù hiệu suất không quan trọng nhưng đối với một số ứng dụng thì có thể.

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.