Chức năng Quá tải thông qua Đa hình động trong 100 dòng JS
Đây là từ một cơ thể lớn hơn mã bao gồm isFn
, isArr
vv loại chức năng kiểm tra. Phiên bản VanillaJS dưới đây đã được làm lại để loại bỏ tất cả các phụ thuộc bên ngoài, tuy nhiên bạn sẽ phải xác định các chức năng kiểm tra loại của riêng bạn để sử dụng trong các .add()
cuộc gọi.
Lưu ý: Đây là một chức năng tự thực hiện (vì vậy chúng ta có thể có một phạm vi đóng / đóng), do đó việc gán cho window.overload
thay vì function overload() {...}
.
window.overload = function () {
"use strict"
var a_fnOverloads = [],
_Object_prototype_toString = Object.prototype.toString
;
function isFn(f) {
return (_Object_prototype_toString.call(f) === '[object Function]');
} //# isFn
function isObj(o) {
return !!(o && o === Object(o));
} //# isObj
function isArr(a) {
return (_Object_prototype_toString.call(a) === '[object Array]');
} //# isArr
function mkArr(a) {
return Array.prototype.slice.call(a);
} //# mkArr
function fnCall(fn, vContext, vArguments) {
//# <ES5 Support for array-like objects
//# See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#Browser_compatibility
vArguments = (isArr(vArguments) ? vArguments : mkArr(vArguments));
if (isFn(fn)) {
return fn.apply(vContext || this, vArguments);
}
} //# fnCall
//#
function registerAlias(fnOverload, fn, sAlias) {
//#
if (sAlias && !fnOverload[sAlias]) {
fnOverload[sAlias] = fn;
}
} //# registerAlias
//#
function overload(vOptions) {
var oData = (isFn(vOptions) ?
{ default: vOptions } :
(isObj(vOptions) ?
vOptions :
{
default: function (/*arguments*/) {
throw "Overload not found for arguments: [" + mkArr(arguments) + "]";
}
}
)
),
fnOverload = function (/*arguments*/) {
var oEntry, i, j,
a = arguments,
oArgumentTests = oData[a.length] || []
;
//# Traverse the oArgumentTests for the number of passed a(rguments), defaulting the oEntry at the beginning of each loop
for (i = 0; i < oArgumentTests.length; i++) {
oEntry = oArgumentTests[i];
//# Traverse the passed a(rguments), if a .test for the current oArgumentTests fails, reset oEntry and fall from the a(rgument)s loop
for (j = 0; j < a.length; j++) {
if (!oArgumentTests[i].tests[j](a[j])) {
oEntry = undefined;
break;
}
}
//# If all of the a(rgument)s passed the .tests we found our oEntry, so break from the oArgumentTests loop
if (oEntry) {
break;
}
}
//# If we found our oEntry above, .fn.call its .fn
if (oEntry) {
oEntry.calls++;
return fnCall(oEntry.fn, this, a);
}
//# Else we were unable to find a matching oArgumentTests oEntry, so .fn.call our .default
else {
return fnCall(oData.default, this, a);
}
} //# fnOverload
;
//#
fnOverload.add = function (fn, a_vArgumentTests, sAlias) {
var i,
bValid = isFn(fn),
iLen = (isArr(a_vArgumentTests) ? a_vArgumentTests.length : 0)
;
//#
if (bValid) {
//# Traverse the a_vArgumentTests, processinge each to ensure they are functions (or references to )
for (i = 0; i < iLen; i++) {
if (!isFn(a_vArgumentTests[i])) {
bValid = _false;
}
}
}
//# If the a_vArgumentTests are bValid, set the info into oData under the a_vArgumentTests's iLen
if (bValid) {
oData[iLen] = oData[iLen] || [];
oData[iLen].push({
fn: fn,
tests: a_vArgumentTests,
calls: 0
});
//#
registerAlias(fnOverload, fn, sAlias);
return fnOverload;
}
//# Else one of the passed arguments was not bValid, so throw the error
else {
throw "poly.overload: All tests must be functions or strings referencing `is.*`.";
}
}; //# overload*.add
//#
fnOverload.list = function (iArgumentCount) {
return (arguments.length > 0 ? oData[iArgumentCount] || [] : oData);
}; //# overload*.list
//#
a_fnOverloads.push(fnOverload);
registerAlias(fnOverload, oData.default, "default");
return fnOverload;
} //# overload
//#
overload.is = function (fnTarget) {
return (a_fnOverloads.indexOf(fnTarget) > -1);
} //# overload.is
return overload;
}();
Sử dụng:
Người gọi xác định các hàm quá tải của chúng bằng cách gán một biến cho trả về overload()
. Nhờ có chuỗi, quá tải bổ sung có thể được xác định theo chuỗi:
var myOverloadedFn = overload(function(){ console.log("default", arguments) })
.add(function(){ console.log("noArgs", arguments) }, [], "noArgs")
.add(function(){ console.log("str", arguments) }, [function(s){ return typeof s === 'string' }], "str")
;
Đối số tùy chọn duy nhất để overload()
xác định hàm "mặc định" để gọi nếu chữ ký không thể được xác định. Các đối số .add()
là:
fn
: function
xác định quá tải;
a_vArgumentTests
: Array
của function
s xác định các bài kiểm tra để chạy trên arguments
. Mỗi đối tượng function
chấp nhận một đối số duy nhất và trả về true
thy dựa trên nếu đối số đó hợp lệ;
sAlias
(Tùy chọn): string
xác định bí danh để truy cập trực tiếp vào hàm quá tải ( fn
), ví dụ: myOverloadedFn.noArgs()
sẽ gọi hàm đó trực tiếp, tránh các phép thử đa hình động của các đối số.
Việc triển khai này thực sự cho phép nhiều hơn chỉ là quá tải chức năng truyền thống như là a_vArgumentTests
đối số thứ hai .add()
trong thực tế xác định các loại tùy chỉnh. Vì vậy, bạn có thể cổng đối số không chỉ dựa trên loại, mà dựa trên phạm vi, giá trị hoặc bộ sưu tập giá trị!
Nếu bạn xem qua 145 dòng mã, overload()
bạn sẽ thấy rằng mỗi chữ ký được phân loại theo số lượng arguments
được chuyển cho nó. Điều này được thực hiện để chúng tôi giới hạn số lượng thử nghiệm chúng tôi đang chạy. Tôi cũng theo dõi số lượng cuộc gọi. Với một số mã bổ sung, các mảng của các hàm quá tải có thể được sắp xếp lại để các hàm được gọi phổ biến hơn được kiểm tra trước, thêm một số biện pháp nâng cao hiệu suất.
Bây giờ, có một số hãy cẩn thận ... Như Javascript là loại lỏng lẻo, bạn sẽ phải cẩn thận với bạn vArgumentTests
như một integer
thể xác nhận như một float
vv
Phiên bản JSCompress.com (1114 byte, 744 byte g-zip):
window.overload=function(){'use strict';function b(n){return'[object Function]'===m.call(n)}function c(n){return!!(n&&n===Object(n))}function d(n){return'[object Array]'===m.call(n)}function e(n){return Array.prototype.slice.call(n)}function g(n,p,q){if(q=d(q)?q:e(q),b(n))return n.apply(p||this,q)}function h(n,p,q){q&&!n[q]&&(n[q]=p)}function k(n){var p=b(n)?{default:n}:c(n)?n:{default:function(){throw'Overload not found for arguments: ['+e(arguments)+']'}},q=function(){var r,s,t,u=arguments,v=p[u.length]||[];for(s=0;s<v.length;s++){for(r=v[s],t=0;t<u.length;t++)if(!v[s].tests[t](u[t])){r=void 0;break}if(r)break}return r?(r.calls++,g(r.fn,this,u)):g(p.default,this,u)};return q.add=function(r,s,t){var u,v=b(r),w=d(s)?s.length:0;if(v)for(u=0;u<w;u++)b(s[u])||(v=_false);if(v)return p[w]=p[w]||[],p[w].push({fn:r,tests:s,calls:0}),h(q,r,t),q;throw'poly.overload: All tests must be functions or strings referencing `is.*`.'},q.list=function(r){return 0<arguments.length?p[r]||[]:p},l.push(q),h(q,p.default,'default'),q}var l=[],m=Object.prototype.toString;return k.is=function(n){return-1<l.indexOf(n)},k}();