Có nhiều khía cạnh đối với quá tải đối số trong Javascript:
Đối số biến - Bạn có thể truyền các tập đối số khác nhau (về cả kiểu và số lượng) và hàm sẽ hoạt động theo cách khớp với các đối số được truyền cho nó.
Đối số mặc định - Bạn có thể xác định giá trị mặc định cho một đối số nếu nó không được truyền.
Đối số được đặt tên - Thứ tự đối số trở nên không liên quan và bạn chỉ cần đặt tên cho đối số nào bạn muốn truyền cho hàm.
Dưới đây là một phần về từng loại xử lý đối số này.
Đối số biến
Vì javascript không có kiểm tra kiểu đối với các đối số hoặc số lượng đối số bắt buộc, bạn chỉ có thể có một triển khai của myFunc()nó có thể thích ứng với những đối số nào đã được chuyển cho nó bằng cách kiểm tra kiểu, sự hiện diện hoặc số lượng đối số.
jQuery thực hiện điều này mọi lúc. Bạn có thể đặt một số đối số là tùy chọn hoặc bạn có thể phân nhánh trong hàm của mình tùy thuộc vào đối số nào được truyền cho nó.
Khi thực hiện các loại quá tải này, bạn có một số kỹ thuật khác nhau có thể sử dụng:
- Bạn có thể kiểm tra sự hiện diện của bất kỳ đối số đã cho nào bằng cách kiểm tra xem giá trị tên đối số đã khai báo có phải là không
undefined.
- Bạn có thể kiểm tra tổng số lượng hoặc các đối số với
arguments.length.
- Bạn có thể kiểm tra loại của bất kỳ đối số nhất định nào.
- Đối với số lượng đối số thay đổi, bạn có thể sử dụng
argumentsmảng giả để truy cập vào bất kỳ đối số đã cho nào arguments[i].
Dưới đây là một số ví dụ:
Hãy xem xét obj.data()phương thức của jQuery . Nó hỗ trợ bốn hình thức sử dụng khác nhau:
obj.data("key");
obj.data("key", value);
obj.data();
obj.data(object);
Mỗi một kích hoạt một hành vi khác nhau và, nếu không sử dụng hình thức nạp chồng động này, sẽ yêu cầu bốn chức năng riêng biệt.
Đây là cách người ta có thể phân biệt giữa tất cả các tùy chọn này bằng tiếng Anh và sau đó tôi sẽ kết hợp tất cả chúng trong mã:
// get the data element associated with a particular key value
obj.data("key");
Nếu đối số đầu tiên được truyền đến .data()là một chuỗi và đối số thứ hai là undefined, thì người gọi phải sử dụng biểu mẫu này.
// set the value associated with a particular key
obj.data("key", value);
Nếu đối số thứ hai không phải là không xác định, thì hãy đặt giá trị của một khóa cụ thể.
// get all keys/values
obj.data();
Nếu không có đối số nào được chuyển, thì trả về tất cả các khóa / giá trị trong một đối tượng được trả về.
// set all keys/values from the passed in object
obj.data(object);
Nếu kiểu của đối số đầu tiên là một đối tượng thuần túy, thì hãy đặt tất cả các khóa / giá trị từ đối tượng đó.
Đây là cách bạn có thể kết hợp tất cả những thứ đó trong một tập hợp logic javascript:
// method declaration for .data()
data: function(key, value) {
if (arguments.length === 0) {
// .data()
// no args passed, return all keys/values in an object
} else if (typeof key === "string") {
// first arg is a string, look at type of second arg
if (typeof value !== "undefined") {
// .data("key", value)
// set the value for a particular key
} else {
// .data("key")
// retrieve a value for a key
}
} else if (typeof key === "object") {
// .data(object)
// set all key/value pairs from this object
} else {
// unsupported arguments passed
}
},
Chìa khóa của kỹ thuật này là đảm bảo rằng tất cả các dạng đối số mà bạn muốn chấp nhận đều có thể nhận dạng duy nhất và không bao giờ có bất kỳ sự nhầm lẫn nào về dạng mà người gọi đang sử dụng. Điều này thường yêu cầu sắp xếp các đối số một cách thích hợp và đảm bảo rằng có đủ tính duy nhất trong kiểu và vị trí của các đối số mà bạn luôn có thể biết được dạng nào đang được sử dụng.
Ví dụ: nếu bạn có một hàm nhận ba đối số chuỗi:
obj.query("firstArg", "secondArg", "thirdArg");
Bạn có thể dễ dàng đặt đối số thứ ba là tùy chọn và bạn có thể dễ dàng phát hiện điều kiện đó, nhưng bạn không thể chỉ đặt đối số thứ hai là tùy chọn bởi vì bạn không thể biết đối số nào trong số này có nghĩa là đang chuyển vì không có cách nào để xác định xem đối số thứ hai đối số có nghĩa là đối số thứ hai hoặc đối số thứ hai đã bị bỏ qua vì vậy những gì ở vị trí của đối số thứ hai thực sự là đối số thứ ba:
obj.query("firstArg", "secondArg");
obj.query("firstArg", "thirdArg");
Vì cả ba đối số là cùng một kiểu, bạn không thể phân biệt được sự khác biệt giữa các đối số khác nhau, do đó bạn không biết người gọi có ý định gì. Với kiểu gọi này, chỉ có thể tùy chọn đối số thứ ba. Nếu bạn muốn bỏ qua đối số thứ hai, thay vào đó nó sẽ phải được chuyển dưới dạng null(hoặc một số giá trị có thể phát hiện khác) và mã của bạn sẽ phát hiện ra rằng:
obj.query("firstArg", null, "thirdArg");
Đây là một ví dụ jQuery về các đối số tùy chọn. cả hai đối số là tùy chọn và nhận giá trị mặc định nếu không được truyền:
clone: function( dataAndEvents, deepDataAndEvents ) {
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
return this.map( function () {
return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
});
},
Đây là một ví dụ về jQuery trong đó đối số có thể bị thiếu hoặc bất kỳ một trong ba kiểu khác nhau cung cấp cho bạn bốn trạng thái quá tải khác nhau:
html: function( value ) {
if ( value === undefined ) {
return this[0] && this[0].nodeType === 1 ?
this[0].innerHTML.replace(rinlinejQuery, "") :
null;
// See if we can take a shortcut and just use innerHTML
} else if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
(jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
!wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
value = value.replace(rxhtmlTag, "<$1></$2>");
try {
for ( var i = 0, l = this.length; i < l; i++ ) {
// Remove element nodes and prevent memory leaks
if ( this[i].nodeType === 1 ) {
jQuery.cleanData( this[i].getElementsByTagName("*") );
this[i].innerHTML = value;
}
}
// If using innerHTML throws an exception, use the fallback method
} catch(e) {
this.empty().append( value );
}
} else if ( jQuery.isFunction( value ) ) {
this.each(function(i){
var self = jQuery( this );
self.html( value.call(this, i, self.html()) );
});
} else {
this.empty().append( value );
}
return this;
},
Đối số được đặt tên
Các ngôn ngữ khác (như Python) cho phép một người truyền các đối số được đặt tên như một phương tiện chỉ truyền một số đối số và làm cho các đối số độc lập với thứ tự chúng được truyền vào. Javascript không hỗ trợ trực tiếp tính năng của các đối số được đặt tên. Một mẫu thiết kế thường được sử dụng ở vị trí của nó là truyền một bản đồ các thuộc tính / giá trị. Điều này có thể được thực hiện bằng cách truyền một đối tượng có thuộc tính và giá trị hoặc trong ES6 trở lên, bạn thực sự có thể truyền chính đối tượng Bản đồ.
Đây là một ví dụ đơn giản về ES5:
jQuery's $.ajax()chấp nhận một dạng sử dụng trong đó bạn chỉ cần truyền cho nó một tham số duy nhất là một đối tượng Javascript thông thường với các thuộc tính và giá trị. Thuộc tính nào bạn chuyển nó sẽ xác định đối số / tùy chọn nào đang được chuyển đến lệnh gọi ajax. Một số có thể được yêu cầu, nhiều là tùy chọn. Vì chúng là các thuộc tính trên một đối tượng nên không có thứ tự cụ thể. Trên thực tế, có hơn 30 thuộc tính khác nhau có thể được truyền cho đối tượng đó, chỉ một (url) là bắt buộc.
Đây là một ví dụ:
$.ajax({url: "http://www.example.com/somepath", data: myArgs, dataType: "json"}).then(function(result) {
// process result here
});
Bên trong quá trình $.ajax()thực thi, nó có thể chỉ thẩm vấn các thuộc tính nào đã được chuyển cho đối tượng đến và sử dụng chúng làm đối số được đặt tên. Điều này có thể được thực hiện với for (prop in obj)hoặc bằng cách nhận tất cả các thuộc tính vào một mảng với Object.keys(obj)và sau đó lặp lại mảng đó.
Kỹ thuật này được sử dụng rất phổ biến trong Javascript khi có số lượng lớn các đối số và / hoặc nhiều đối số là tùy chọn. Lưu ý: điều này thúc đẩy hàm triển khai để đảm bảo rằng có một tập hợp các đối số hợp lệ tối thiểu và cung cấp cho người gọi một số phản hồi gỡ lỗi bị thiếu nếu không đủ đối số được truyền (có thể bằng cách ném một ngoại lệ với thông báo lỗi hữu ích) .
Trong môi trường ES6, có thể sử dụng hàm hủy để tạo các thuộc tính / giá trị mặc định cho đối tượng được truyền ở trên. Điều này được thảo luận chi tiết hơn trong bài viết tham khảo này .
Đây là một ví dụ từ bài báo đó:
function selectEntries({ start=0, end=-1, step=1 } = {}) {
···
};
Điều này tạo ra các thuộc tính và giá trị mặc định cho start, endvà stepcác thuộc tính trên một đối tượng được truyền cho selectEntries()hàm.
Giá trị mặc định cho các đối số của hàm
Trong ES6, Javascript bổ sung hỗ trợ ngôn ngữ tích hợp cho các giá trị mặc định cho các đối số.
Ví dụ:
function multiply(a, b = 1) {
return a*b;
}
multiply(5); // 5
Mô tả thêm về các cách này có thể được sử dụng tại đây trên MDN .