Hoe functies in javascript overbelasten?

Klassieke (niet-js) benadering van overbelasting:

function myFunc(){
 //code
}
function myFunc(overloaded){
 //other code
}

Javascript laat niet meer dan één functie met dezelfde naam definiëren. Als zodanig verschijnen dingen als deze:

function myFunc(options){
 if(options["overloaded"]){
  //code
 }
}

Is er een betere oplossing voor overbelasting van functies in Javascript, behalve het doorgeven van een object met de overbelasting erin?

Het doorgeven van overbelastingen kan er snel toe leiden dat een functie te uitgebreid wordt, omdat voor elke mogelijke overbelasting dan een voorwaardelijke instructie nodig is. Het gebruik van functies om de //codebinnen die voorwaardelijke instructies te bereiken, kan lastige situaties met scopes veroorzaken.


Antwoord 1, autoriteit 100%

Er zijn meerdere aspecten aan overbelasting van argumenten in Javascript:

  1. Variabele argumenten– U kunt verschillende sets argumenten doorgeven (zowel type als aantal) en de functie zal zich gedragen op een manier die overeenkomt met de argumenten die eraan zijn doorgegeven.

  2. Standaardargumenten– U kunt een standaardwaarde voor een argument definiëren als het niet wordt doorgegeven.

  3. Benoemde argumenten– Argumentvolgorde wordt irrelevant en u hoeft alleen maar te noemen welke argumenten u aan de functie wilt doorgeven.

Hieronder vindt u een sectie over elk van deze categorieën van argumentbehandeling.

Variabele argumenten

Omdat javascript geen typecontrole heeft op argumenten of een aantal argumenten vereist, kunt u slechts één implementatie van myFunc()hebben die zich kan aanpassen aan de argumenten die eraan zijn doorgegeven door het type, de aanwezigheid te controleren of hoeveelheid argumenten.

jQuery doet dit altijd. Je kunt sommige argumenten optioneel maken of je kunt je functie vertakken, afhankelijk van de argumenten die eraan worden doorgegeven.

Bij het implementeren van dit soort overbelastingen heb je verschillende technieken die je kunt gebruiken:

  1. U kunt de aanwezigheid van een bepaald argument controleren door te controleren of de gedeclareerde waarde van de argumentnaam undefinedis.
  2. Je kunt het totale aantal of de argumenten controleren met arguments.length.
  3. Je kunt het type van elk gegeven argument controleren.
  4. Voor variabele aantallen argumenten kunt u de argumentspseudo-array gebruiken om toegang te krijgen tot een gegeven argument met arguments[i].

Hier zijn enkele voorbeelden:

Laten we eens kijken naar de obj.data()-methode van jQuery. Het ondersteunt vier verschillende vormen van gebruik:

obj.data("key");
obj.data("key", value);
obj.data();
obj.data(object);

Elk daarvan veroorzaakt een ander gedrag en zou, zonder deze dynamische vorm van overbelasting te gebruiken, vier afzonderlijke functies vereisen.

Hier is hoe je al deze opties in het Engels kunt onderscheiden en dan zal ik ze allemaal in code combineren:

// get the data element associated with a particular key value
obj.data("key");

Als het eerste argument dat wordt doorgegeven aan .data()een string is en het tweede argument undefinedis, dan moet de beller dit formulier gebruiken.


// set the value associated with a particular key
obj.data("key", value);

Als het tweede argument niet ongedefinieerd is, stel dan de waarde van een bepaalde sleutel in.


// get all keys/values
obj.data();

Als er geen argumenten worden doorgegeven, retourneer dan alle sleutels/waarden in een geretourneerd object.


// set all keys/values from the passed in object
obj.data(object);

Als het type van het eerste argument een gewoon object is, stel dan alle sleutels/waarden van dat object in.


Hier ziet u hoe u deze allemaal kunt combineren in één set javascript-logica:

// 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
     }
 },

De sleutel tot deze techniek is ervoor te zorgen dat alle vormen van argumenten die u wilt accepteren uniek identificeerbaar zijn en dat er nooit verwarring bestaat over welke vorm de beller gebruikt. Dit vereist over het algemeen de juiste volgorde van de argumenten en ervoor zorgen dat het type en de positie van de argumenten voldoende uniek zijn, zodat u altijd kunt zien welke vorm wordt gebruikt.

Als u bijvoorbeeld een functie heeft waaraan drie stringargumenten moeten doorgegeven worden:

obj.query("firstArg", "secondArg", "thirdArg");

Je kunt het derde argument gemakkelijk optioneel maken en je kunt die voorwaarde gemakkelijk detecteren, maar je kunt niet alleen het tweede argument optioneel maken omdat je niet kunt zien welke van deze de beller bedoelt te passeren, omdat er geen manier is om te identificeren als het tweede argument bedoeld is als het tweede argument of als het tweede argument is weggelaten, dus wat op de plek van het tweede argument staat, is eigenlijk het derde argument:

obj.query("firstArg", "secondArg");
obj.query("firstArg", "thirdArg");

Omdat alle drie de argumenten van hetzelfde type zijn, kun je het verschil tussen verschillende argumenten niet zien, dus je weet niet wat de beller bedoelde. Met deze aanroepstijl kan alleen het derde argument optioneel zijn. Als u het tweede argument wilt weglaten, zou het in plaats daarvan als null(of een andere detecteerbare waarde) moeten worden doorgegeven en uw code zou dat detecteren:

obj.query("firstArg", null, "thirdArg");

Hier is een jQuery-voorbeeld van optionele argumenten. beide argumenten zijn optioneel en nemen standaardwaarden aan als ze niet worden doorgegeven:

clone: function( dataAndEvents, deepDataAndEvents ) {
    dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
    deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
    return this.map( function () {
        return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
    });
},

Hier is een jQuery-voorbeeld waarbij het argument kan ontbreken of een van de drie verschillende typen is, waardoor u vier verschillende overbelastingen krijgt:

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;
},

Benoemde argumenten

Andere talen (zoals Python) staan ​​het toe om benoemde argumenten door te geven om slechts enkele argumenten door te geven en de argumenten onafhankelijk te maken van de volgorde waarin ze worden doorgegeven. Javascript ondersteunt de functie van benoemde argumenten niet rechtstreeks. Een ontwerppatroon dat vaak in plaats daarvan wordt gebruikt, is het doorgeven van een kaart met eigenschappen/waarden. Dit kan worden gedaan door een object met eigenschappen en waarden door te geven, of in ES6 en hoger kunt u een kaartobject zelf doorgeven.

Hier is een eenvoudig ES5-voorbeeld:

jQuery’s $.ajax()accepteert een vorm van gebruik waarbij je het gewoon een enkele parameter doorgeeft, wat een normaal Javascript-object is met eigenschappen en waarden. Welke eigenschappen u doorgeeft, bepaalt welke argumenten/opties worden doorgegeven aan de ajax-aanroep. Sommige zijn mogelijk vereist, veel zijn optioneel. Omdat het eigenschappen van een object zijn, is er geen specifieke volgorde. In feite zijn er meer dan 30 verschillende eigenschappen die aan dat object kunnen worden doorgegeven, slechts één (de url) is vereist.

Hier is een voorbeeld:

$.ajax({url: "http://www.example.com/somepath", data: myArgs, dataType: "json"}).then(function(result) {
    // process result here
});

Binnen de $.ajax()-implementatie kan het dan gewoon ondervragen welke eigenschappen zijn doorgegeven aan het inkomende object en die gebruiken als benoemde argumenten. Dit kan worden gedaan met for (prop in obj)of door alle eigenschappen in een array te krijgen met Object.keys(obj)en vervolgens die array te herhalen.

Deze techniek wordt heel vaak gebruikt in Javascript wanneer er grote aantallen argumenten zijn en/of veel argumenten optioneel zijn. Opmerking: dit legt een verantwoordelijkheid op de implementerende functie om ervoor te zorgen dat er een minimale geldige set argumenten aanwezig is en om de beller wat debug-feedback te geven wat er ontbreekt als er onvoldoende argumenten worden doorgegeven (waarschijnlijk door een uitzondering te genereren met een nuttig foutbericht) .

In een ES6-omgeving is het mogelijk om destructuring te gebruiken om standaardeigenschappen/waarden te creëren voor het hierboven doorgegeven object. Dit wordt in meer detail besproken in dit referentieartikel.

Hier is een voorbeeld uit dat artikel:

function selectEntries({ start=0, end=-1, step=1 } = {}) {
    ···
};

Dan kun je dit als volgt noemen:

selectEntries({start: 5});
selectEntries({start: 5, end: 10});
selectEntries({start: 5, end: 10, step: 2});
selectEntries({step: 3});
selectEntries();

De argumenten die u niet in de functieaanroep vermeldt, halen hun standaardwaarden op uit de functiedeclaratie.

Hiermee worden standaardeigenschappen en -waarden gemaakt voor de eigenschappen start, enden stepvoor een object dat is doorgegeven aan de selectEntries()functie.

Standaardwaarden voor functieargumenten

In ES6 voegt Javascript ingebouwde taalondersteuning toe voor standaardwaarden voor argumenten.

Bijvoorbeeld:

function multiply(a, b = 1) {
  return a*b;
}
multiply(5); // 5

Nadere beschrijving van de manieren waarop dit kan worden gebruikt hier op MDN.


2, Autoriteit 25%

Overbelasting van een functie in JavaScript kan op vele manieren worden gedaan. Allemaal omvatten ze een enkele masterfunctie die alle processen of afgevaardigden uitvoert voor subfuncties / processen.

Een van de meest voorkomende eenvoudige technieken omvat een eenvoudige schakelaar:

function foo(a, b) {
    switch (arguments.length) {
    case 0:
        //do basic code
        break;
    case 1:
        //do code with `a`
        break;
    case 2:
    default:
        //do code with `a` & `b`
        break;
    }
}

Een meer elegante techniek zou zijn om een ​​array (of object te gebruiken als u niet overbelastingen voor elke argument tellen maakt):

fooArr = [
    function () {
    },
    function (a) {
    },
    function (a,b) {
    }
];
function foo(a, b) {
    return fooArr[arguments.length](a, b);
}

Dat vorige voorbeeld is niet erg elegant, iedereen zou fooArrkunnen wijzigen, en het zou mislukken als iemand meer dan 2 argumenten inhoudt op foo, dus een betere vorm zou zijn om een ​​modulepatroon en een paar cheques te gebruiken:

var foo = (function () {
    var fns;
    fns = [
        function () {
        },
        function (a) {
        },
        function (a, b) {
        }
    ];
    function foo(a, b) {
        var fnIndex;
        fnIndex = arguments.length;
        if (fnIndex > foo.length) {
            fnIndex = foo.length;
        }
        return fns[fnIndex].call(this, a, b);
    }
    return foo;
}());

Natuurlijk willen uw overbelastingen een dynamisch aantal parameters gebruiken, zodat u een object voor de fnsCollection kunt gebruiken.

var foo = (function () {
    var fns;
    fns = {};
    fns[0] = function () {
    };
    fns[1] = function (a) {
    };
    fns[2] = function (a, b) {
    };
    fns.params = function (a, b /*, params */) {
    };
    function foo(a, b) {
        var fnIndex;
        fnIndex = arguments.length;
        if (fnIndex > foo.length) {
            fnIndex = 'params';
        }
        return fns[fnIndex].apply(this, Array.prototype.slice.call(arguments));
    }
    return foo;
}());

Mijn persoonlijke voorkeur heeft de neiging om de switchte zijn, hoewel het de Master-functie overtreedt. Een gemeenschappelijk voorbeeld van waar ik deze techniek zou gebruiken, zou een toebehoren / mutatormethode zijn:

function Foo() {} //constructor
Foo.prototype = {
    bar: function (val) {
        switch (arguments.length) {
        case 0:
            return this._bar;
        case 1:
            this._bar = val;
            return this;
        }
    }
}

3, Autoriteit 8%

U kunt geen methode overbelast in strikte zin. Niet zoals de manier waarop het wordt ondersteund in javaof c#.

Het probleem is dat JavaScript niet native ondersteunt overbelasting. Dus, als het twee of meer functies ziet / parseert met dezelfde namen, neemt u rekening met de laatst gedefinieerde functie en overschrijft de vorige.

Een van de manier waarop ik denk dat het geschikt is voor het grootste deel van de behuizing is volgt –

Laten we zeggen dat je methode

hebt

function foo(x)
{
} 

In plaats van overbelastingsmethode die niet mogelijk is in Javascript U kunt een nieuwe methode definiëren

fooNew(x,y,z)
{
}

en wijzig vervolgens de 1e functie als volgt –

function foo(x)
{
  if(arguments.length==2)
  {
     return fooNew(arguments[0],  arguments[1]);
  }
} 

Als je veel van dergelijke overbelaste methoden hebt, overweeg dan om switchte gebruiken dan alleen if-else-instructies.

(meer details)
PS: Bovenstaande link gaat naar mijn persoonlijke blog met aanvullende details hierover.


Antwoord 4, autoriteit 3%

Ik gebruik een iets andere overbelastingsbenadering op basis van het aantal argumenten.
Ik geloof echter dat de benadering van John Fawcett ook goed is.
Hier het voorbeeld, code gebaseerd op de uitleg van John Resig (jQuery’s Author).

// o = existing object, n = function name, f = function.
    function overload(o, n, f){
        var old = o[n];
        o[n] = function(){
            if(f.length == arguments.length){
                return f.apply(this, arguments);
            }
            else if(typeof o == 'function'){
                return old.apply(this, arguments);
            }
        };
    }

bruikbaarheid:

var obj = {};
overload(obj, 'function_name', function(){ /* what we will do if no args passed? */});
overload(obj, 'function_name', function(first){ /* what we will do if 1 arg passed? */});
overload(obj, 'function_name', function(first, second){ /* what we will do if 2 args passed? */});
overload(obj, 'function_name', function(first,second,third){ /* what we will do if 3 args passed? */});
//... etc :)

Antwoord 5, autoriteit 3%

Ik heb geprobeerd een elegante oplossing voor dit probleem te ontwikkelen dat hier< wordt beschreven. En je kunt de demo hiervinden. Het gebruik ziet er als volgt uit:

var out = def({
    'int': function(a) {
        alert('Here is int '+a);
    },
    'float': function(a) {
        alert('Here is float '+a);
    },
    'string': function(a) {
        alert('Here is string '+a);
    },
    'int,string': function(a, b) {
        alert('Here is an int '+a+' and a string '+b);
    },
    'default': function(obj) {
        alert('Here is some other value '+ obj);
    }
});
out('ten');
out(1);
out(2, 'robot');
out(2.5);
out(true);

De gebruikte methoden om dit te bereiken:

var def = function(functions, parent) {
 return function() {
    var types = [];
    var args = [];
    eachArg(arguments, function(i, elem) {
        args.push(elem);
        types.push(whatis(elem));
    });
    if(functions.hasOwnProperty(types.join())) {
        return functions[types.join()].apply(parent, args);
    } else {
        if (typeof functions === 'function')
            return functions.apply(parent, args);
        if (functions.hasOwnProperty('default'))
            return functions['default'].apply(parent, args);        
    }
  };
};
var eachArg = function(args, fn) {
 var i = 0;
 while (args.hasOwnProperty(i)) {
    if(fn !== undefined)
        fn(i, args[i]);
    i++;
 }
 return i-1;
};
var whatis = function(val) {
 if(val === undefined)
    return 'undefined';
 if(val === null)
    return 'null';
 var type = typeof val;
 if(type === 'object') {
    if(val.hasOwnProperty('length') && val.hasOwnProperty('push'))
        return 'array';
    if(val.hasOwnProperty('getDate') && val.hasOwnProperty('toLocaleTimeString'))
        return 'date';
    if(val.hasOwnProperty('toExponential'))
        type = 'number';
    if(val.hasOwnProperty('substring') && val.hasOwnProperty('length'))
        return 'string';
 }
 if(type === 'number') {
    if(val.toString().indexOf('.') > 0)
        return 'float';
    else
        return 'int';
 }
 return type;
};

Antwoord 6, autoriteit 3%

In javascript kun je de functie maar één keer implementeren en de functie aanroepen zonder de parameters myFunc()Vervolgens controleer je of de opties ‘undefined’ zijn

function myFunc(options){
 if(typeof options != 'undefined'){
  //code
 }
}

Antwoord 7, autoriteit 2%

https://github.com/jrf0110/leFunc

var getItems = leFunc({
  "string": function(id){
    // Do something
  },
  "string,object": function(id, options){
    // Do something else
  },
  "string,object,function": function(id, options, callback){
    // Do something different
    callback();
  },
  "object,string,function": function(options, message, callback){
    // Do something ca-raaaaazzzy
    callback();
  }
});
getItems("123abc"); // Calls the first function - "string"
getItems("123abc", {poop: true}); // Calls the second function - "string,object"
getItems("123abc", {butt: true}, function(){}); // Calls the third function - "string,object,function"
getItems({butt: true}, "What what?" function(){}); // Calls the fourth function - "object,string,function"

Antwoord 8, autoriteit 2%

Geen probleem met overbelasting in JS, de pb hoe een schone code te behouden bij overbelastingsfunctie?

Je kunt een forwardgebruiken om schone code te hebben, gebaseerd op twee dingen:

  1. Aantal argumenten (bij het aanroepen van de functie).
  2. Type argumenten (bij het aanroepen van de functie)

     function myFunc(){
          return window['myFunc_'+arguments.length+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments);
       }
        /** one argument & this argument is string */
      function myFunc_1_string(){
      }
       //------------
       /** one argument & this argument is object */
      function myFunc_1_object(){
      }
      //----------
      /** two arguments & those arguments are both string */
      function myFunc_2_string_string(){
      }
       //--------
      /** Three arguments & those arguments are : id(number),name(string), callback(function) */
      function myFunc_3_number_string_function(){
                let args=arguments;
                  new Person(args[0],args[1]).onReady(args[3]);
      }
       //--- And so on ....   
    

Antwoord 9, autoriteit 2%

Bekijk dit eens:

http://www.codeproject.com/Articles/688869/Overloading- JavaScript-functies

In principe nummer je in je klas je functies die je wilt overbelasten en dan voeg je met één functieaanroep functieoverbelasting toe, snel en gemakkelijk.


Antwoord 10, autoriteit 2%

Aangezien JavaScript geen functie-overbelasting heeft, kan in plaats daarvan options objectworden gebruikt. Als er een of twee vereiste argumenten zijn, is het beter om ze gescheiden te houden van het options-object. Hier is een voorbeeld van hoe u het optieobject en de ingevulde waarden kunt gebruiken als standaardwaarde in het geval dat de waarde niet is doorgegeven in het optieobject.

function optionsObjectTest(x, y, opts) {
    opts = opts || {}; // default to an empty options object
    var stringValue = opts.stringValue || "string default value";
    var boolValue = !!opts.boolValue; // coerces value to boolean with a double negation pattern
    var numericValue = opts.numericValue === undefined ? 123 : opts.numericValue;
    return "{x:" + x + ", y:" + y + ", stringValue:'" + stringValue + "', boolValue:" + boolValue + ", numericValue:" + numericValue + "}";

}

hieris een voorbeeld van het gebruik van options-object


Antwoord 11, autoriteit 2%

Hiervoor moet je een functie maken die de functie aan een object toevoegt, waarna deze wordt uitgevoerd afhankelijk van het aantal argumenten dat je naar de functie stuurt:

<script > 
//Main function to add the methods
function addMethod(object, name, fn) {
  var old = object[name];
  object[name] = function(){
    if (fn.length == arguments.length)
      return fn.apply(this, arguments)
    else if (typeof old == 'function')
      return old.apply(this, arguments);
  };
}
  var ninjas = {
   values: ["Dean Edwards", "Sam Stephenson", "Alex Russell"]
};
//Here we declare the first function with no arguments passed
  addMethod(ninjas, "find", function(){
    return this.values;
});
//Second function with one argument
  addMethod(ninjas, "find", function(name){
    var ret = [];
    for (var i = 0; i < this.values.length; i++)
      if (this.values[i].indexOf(name) == 0)
        ret.push(this.values[i]);
    return ret;
  });
//Third function with two arguments
  addMethod(ninjas, "find", function(first, last){
    var ret = [];
    for (var i = 0; i < this.values.length; i++)
      if (this.values[i] == (first + " " + last))
        ret.push(this.values[i]);
    return ret;
  });
//Now you can do:
ninjas.find();
ninjas.find("Sam");
ninjas.find("Dean", "Edwards")
</script>

12

Ik vind het leuk om subfuncties toe te voegen binnen een ouderfunctie om de mogelijkheid te bereiken om te onderscheiden tussen argumentgroepen voor dezelfde functionaliteit.

var doSomething = function() {
    var foo;
    var bar;
};
doSomething.withArgSet1 = function(arg0, arg1) {
    var obj = new doSomething();
    // do something the first way
    return obj;
};
doSomething.withArgSet2 = function(arg2, arg3) {
    var obj = new doSomething();
    // do something the second way
    return obj;
};

13

Hoe zit het met het gebruik van Spread-operator als parameter? Hetzelfde blok kan met meerdere parameters worden genoemd. Alle parameters worden toegevoegd aan een array en binnen de methode die u kunt lijden op basis van de lengte.

   function mName(...opt){
        console.log(opt); 
    }
    mName(1,2,3,4); //[1,2,3,4]
    mName(1,2,3); //[1,2,3]

14

(() => {
  //array that store functions
    var Funcs = []
     /**
     * @param {function} f overload function
     * @param {string} fname overload function name
   * @param {parameters} vtypes function parameters type descriptor (number,string,object....etc
     */
    overloadFunction = function(f, fname, ...vtypes) {
        var k,l, n = false;
        if (!Funcs.hasOwnProperty(fname)) Funcs[fname] = [];
        Funcs[fname].push([f, vtypes?vtypes: 0 ]);
        window[fname] = function() {
            for (k = 0; k < Funcs[fname].length; k++)
                if (arguments.length == Funcs[fname][k][0].length) {
                    n=true;
                    if (Funcs[fname][k][1]!=0)
                    for(i=0;i<arguments.length;i++)
                    {
                        if(typeof arguments[i]!=Funcs[fname][k][1][i])
                        {
                            n=false;
                        }
                    }
                    if(n) return Funcs[fname][k][0].apply(this, arguments);
                }
        }
    }
})();
//First sum function definition with parameter type descriptors
overloadFunction(function(a,b){return a+b},"sum","number","number")
//Second sum function definition with parameter with parameter type descriptors
overloadFunction(function(a,b){return a+" "+b},"sum","string","string")
//Third sum function definition (not need parameter type descriptors,because no other functions with the same number of parameters
overloadFunction(function(a,b,c){return a+b+c},"sum")
//call first function
console.log(sum(4,2));//return 6
//call second function
console.log(sum("4","2"));//return "4 2"
//call third function
console.log(sum(3,2,5));//return 10
//ETC...

Other episodes