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)
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)
Câu trả lời:
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]);
}
}
arguments
là 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
length
tí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 concat
trả 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.call
thay thế, nhưng tôi thích []
, chúng ngắn và ngọt ngào!
[...arguments].join()
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
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.
context
đối số bằng mã và chuyển nó xung quanh trước khi nó được sử dụng.
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.
_.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.
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
- 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.
- 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
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 arguments
thà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);
}
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.
(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.
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];
}
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});
Đ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!
function sumOfAll (var_args) {
return arguments.reduce(function(a, b) {
return a + b;
}, 0);
}
sumOfAll(1,2,3); // returns 6
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
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
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.
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ể.