Function overloading in Javascript - Best practices

后端 未结 30 1471
难免孤独
难免孤独 2020-11-22 03:33

What is the best way(s) to fake function overloading in Javascript?

I know it is not possible to overload functions in Javascript as in other languages. If I neede

30条回答
  •  伪装坚强ぢ
    2020-11-22 04:16

    Function Overloading via Dynamic Polymorphism in 100 lines of JS

    • VanillaJS, no external dependencies
    • Full Browser Support - Array.prototype.slice, Object.prototype.toString
    • 1114 bytes uglify'd / 744 bytes g-zipped

    This is from a larger body of code which includes the isFn, isArr, etc. type checking functions. The VanillaJS version below has been reworked to remove all external dependencies, however you will have to define you're own type checking functions for use in the .add() calls.

    Note: This is a self-executing function (so we can have a closure/closed scope), hence the assignment to window.overload rather than 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) {
            //#  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;
    }();
    

    Usage:

    The caller defines their overloaded functions by assigning a variable to the return of overload(). Thanks to chaining, the additional overloads can be defined in series:

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

    The single optional argument to overload() defines the "default" function to call if the signature cannot be identified. The arguments to .add() are:

    1. fn: function defining the overload;
    2. a_vArgumentTests: Array of functions defining the tests to run on the arguments. Each function accepts a single argument and returns truethy based on if the argument is valid;
    3. sAlias (Optional): string defining the alias to directly access the overload function (fn), e.g. myOverloadedFn.noArgs() will call that function directly, avoiding the dynamic polymorphism tests of the arguments.

    This implementation actually allows for more than just traditional function overloads as the second a_vArgumentTests argument to .add() in practice defines custom types. So, you could gate arguments not only based on type, but on ranges, values or collections of values!

    If you look through the 145 lines of code for overload() you'll see that each signature is categorized by the number of arguments passed to it. This is done so that we're limiting the number of tests we are running. I also keep track of a call count. With some additional code, the arrays of overloaded functions could be re-sorted so that more commonly called functions are tested first, again adding some measure of performance enhancement.

    Now, there are some caveats... As Javascript is loosely typed, you will have to be careful with your vArgumentTests as an integer could be validated as a float, etc.

    JSCompress.com version (1114 bytes, 744 bytes g-zipped):

    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

提交回复
热议问题