Pergunta

O que é a melhor maneira (s) a função de falso sobrecarga em JavaScript?

Eu sei que não é possível as funções de sobrecarga em Javascript como em outros idiomas. Se eu precisasse de uma função com dois usos foo(x) e foo(x,y,z) que é a melhor maneira / preferido:

  1. O uso de nomes diferentes, em primeiro lugar
  2. Usando argumentos opcionais como y = y || 'default'
  3. Usando o número de argumentos
  4. Verificar tipos de argumentos
  5. Ou como?
Foi útil?

Solução

A melhor maneira de fazer sobrecarga de funções com parâmetros não é verificar o comprimento do argumento ou os tipos; verificando os tipos só vai fazer o seu código lento e você tem a diversão de Arrays, nulos, objetos, etc.

O que a maioria dos desenvolvedores fazer é orçada em um objeto como o último argumento para seus métodos. Este objeto pode conter qualquer coisa.

function foo(a, b, opts) {
  // ...
  if (opts['test']) { } //if test param exists, do something.. 
}


foo(1, 2, {"method":"add"});
foo(3, 4, {"test":"equals", "bar":"tree"});

Em seguida, você pode lidar com isso de qualquer maneira que você quer em seu método. [Switch, if-else, etc.]

Outras dicas

muitas vezes eu fazer isso:

C #:

public string CatStrings(string p1)                  {return p1;}
public string CatStrings(string p1, int p2)          {return p1+p2.ToString();}
public string CatStrings(string p1, int p2, bool p3) {return p1+p2.ToString()+p3.ToString();}

CatStrings("one");        // result = one
CatStrings("one",2);      // result = one2
CatStrings("one",2,true); // result = one2true

JavaScript equivalente:

function CatStrings(p1, p2, p3)
{
  var s = p1;
  if(typeof p2 !== "undefined") {s += p2;}
  if(typeof p3 !== "undefined") {s += p3;}
  return s;
};

CatStrings("one");        // result = one
CatStrings("one",2);      // result = one2
CatStrings("one",2,true); // result = one2true

Este exemplo particular é realmente mais elegante em javascript que C #. Parâmetros que não são especificados são 'indefinido' em javascript, que avalia a falsa em uma instrução if. No entanto, a definição da função não transmite as informações que P2 e P3 são opcionais. Se você precisar de um monte de sobrecarga, jQuery decidiu usar um objeto como o parâmetro, por exemplo, jQuery.ajax (opções). Concordo com eles que esta é a abordagem mais poderoso e claramente documentable a sobrecarga, mas raramente precisam mais do que um ou dois parâmetros opcionais rápidos.

EDIT: mudou teste IF por sugestão de Ian

Não há nenhuma função real sobrecarga em JavaScript, uma vez que permite passar qualquer número de parâmetros de qualquer tipo. Você tem que verificar dentro da função como muitos argumentos rel="noreferrer"> foram passadas eo que tipo são.

Há duas maneiras que você poderia abordar esta questão melhor:

  1. Passe um dicionário (matriz associativa) se você quiser deixar um monte de flexibilidade

  2. Tome um objeto como a herança com base argumentação e uso protótipo para adicionar flexibilidade.

Aqui está uma abordagem que permite verdadeiro método sobrecarga utilizando tipos de parâmetros, mostrados abaixo:

Func(new Point());
Func(new Dimension());
Func(new Dimension(), new Point());
Func(0, 0, 0, 0);

Editar (2018) :. Uma vez que este foi escrito em 2011, a velocidade de chamadas de métodos diretos aumentou significativamente enquanto a velocidade de métodos sobrecarregados não tem

Não é uma abordagem que eu recomendo, mas é um exercício de pensamento vale a pena pensar sobre como você pode resolver esses tipos de problemas.


Aqui é um ponto de referência das diferentes abordagens - https://jsperf.com/function-overloading. Isso mostra que a sobrecarga de função (tendo tipos em conta) pode ser em torno de 13 vezes mais lento no Google V8 do Chrome a partir de 16,0 (beta) .

Como bem como a passagem de um objecto (i.e. {x: 0, y: 0}), pode-se também levar a abordagem C, quando apropriado, nomeando os métodos em conformidade. Por exemplo, Vector.AddVector (vector), Vector.AddIntegers (x, y, z, ...) e Vector.AddArray (IntegerArray). Você pode olhar para bibliotecas C, como o OpenGL para nomear inspiração.

Editar : Eu adicionei uma referência para a passagem de um objeto e testando para o objeto usando tanto 'param' in arg e arg.hasOwnProperty('param') e sobrecarga de funções é muito mais rápido do que passar um objeto e verificação de propriedades (neste referência, pelo menos).

De uma perspectiva de design, a sobrecarga de função só é válida ou lógica, se os parâmetros sobrecarregados correspondem à mesma ação. Então é lógico que deve haver um método subjacente que só se preocupa com detalhes específicos, caso contrário, que pode indicar inadequados projeto escolhas. Assim, também se poderia resolver o uso da função sobrecarga ao converter dados para um respectivo objecto. Claro que é preciso considerar o escopo do problema, não há nenhuma necessidade em fazer projetos elaborados se sua intenção é apenas para imprimir um nome, mas para a concepção de estruturas e bibliotecas tal pensamento é justificada.

Meu exemplo vem de uma implementação do retângulo - daí a menção de Dimensão e Point. Talvez Retângulo poderia adicionar um método GetRectangle() ao protótipo Dimension e Point, e, em seguida, a função de sobrecarga questão está classificada. E o que dizer primitivas? Bem, temos de comprimento do argumento, que agora é um teste válido desde que os objetos têm um método GetRectangle().

function Dimension() {}
function Point() {}

var Util = {};

Util.Redirect = function (args, func) {
  'use strict';
  var REDIRECT_ARGUMENT_COUNT = 2;

  if(arguments.length - REDIRECT_ARGUMENT_COUNT !== args.length) {
    return null;
  }

  for(var i = REDIRECT_ARGUMENT_COUNT; i < arguments.length; ++i) {
    var argsIndex = i-REDIRECT_ARGUMENT_COUNT;
    var currentArgument = args[argsIndex];
    var currentType = arguments[i];
    if(typeof(currentType) === 'object') {
      currentType = currentType.constructor;
    }
    if(typeof(currentType) === 'number') {
      currentType = 'number';
    }
    if(typeof(currentType) === 'string' && currentType === '') {
      currentType = 'string';
    }
    if(typeof(currentType) === 'function') {
      if(!(currentArgument instanceof currentType)) {
        return null;
      }
    } else {
      if(typeof(currentArgument) !== currentType) {
        return null;
      }
    } 
  }
  return [func.apply(this, args)];
}

function FuncPoint(point) {}
function FuncDimension(dimension) {}
function FuncDimensionPoint(dimension, point) {}
function FuncXYWidthHeight(x, y, width, height) { }

function Func() {
  Util.Redirect(arguments, FuncPoint, Point);
  Util.Redirect(arguments, FuncDimension, Dimension);
  Util.Redirect(arguments, FuncDimensionPoint, Dimension, Point);
  Util.Redirect(arguments, FuncXYWidthHeight, 0, 0, 0, 0);
}

Func(new Point());
Func(new Dimension());
Func(new Dimension(), new Point());
Func(0, 0, 0, 0);

A melhor maneira realmente depende da função e os argumentos. Cada uma de suas opções é uma boa idéia em diferentes situações. Eu geralmente tentar estes na seguinte ordem até que um deles funciona:

  1. Usando argumentos opcionais como y = y || 'Default'. Isto é conveniente se você pode fazê-lo, mas pode nem sempre funcionar na prática, por exemplo, quando 0 / null / indefinido seria um argumento válido.

  2. Usando o número de argumentos. Semelhante à última opção, mas pode funcionar quando nº 1 não funciona.

  3. Verificar tipos de argumentos. Isso pode funcionar em alguns casos em que o número de argumentos é o mesmo. Se não for possível determinar com fiabilidade os tipos, você pode precisar usar nomes diferentes.

  4. O uso de nomes diferentes em primeiro lugar. Você pode precisar fazer isso se as outras opções não vai funcionar, não são práticos, ou para a coerência com outras funções relacionadas.

Se necessário uma função com duas utilizações foo (x) e foo (x, y, z), que é o melhor caminho / preferido?

O problema é que o JavaScript não nativamente método de apoio sobrecarga. Então, se ele vê / analisa duas ou mais funções com mesmos nomes ele só vai considerar a última função definida e substituir os anteriores.

Uma das maneira eu acho que é adequado para a maioria dos casos é seguinte -

Vamos dizer que você tem método

function foo(x)
{
} 

Em vez do método que não é possível em javascript você pode definir um novo método

sobrecarregar
fooNew(x,y,z)
{
}

e, em seguida, modificar a primeira função da seguinte forma -

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

Se você tem muitos desses métodos sobrecarregados considerar o uso switch do que apenas declarações if-else.

( mais detalhes )

PS:. Link acima vai para o meu blog pessoal que tem detalhes adicionais

Eu não tenho certeza sobre a melhor prática, mas aqui é como eu faço isso:

/*
 * Object Constructor
 */
var foo = function(x) {
    this.x = x;
};

/*
 * Object Protoype
 */
foo.prototype = {
    /*
     * f is the name that is going to be used to call the various overloaded versions
     */
    f: function() {

        /*
         * Save 'this' in order to use it inside the overloaded functions
         * because there 'this' has a different meaning.
         */   
        var that = this;  

        /* 
         * Define three overloaded functions
         */
        var f1 = function(arg1) {
            console.log("f1 called with " + arg1);
            return arg1 + that.x;
        }

        var f2 = function(arg1, arg2) {
             console.log("f2 called with " + arg1 + " and " + arg2);
             return arg1 + arg2 + that.x;
        }

        var f3 = function(arg1) {
             console.log("f3 called with [" + arg1[0] + ", " + arg1[1] + "]");
             return arg1[0] + arg1[1];
        }

        /*
         * Use the arguments array-like object to decide which function to execute when calling f(...)
         */
        if (arguments.length === 1 && !Array.isArray(arguments[0])) {
            return f1(arguments[0]);
        } else if (arguments.length === 2) {
            return f2(arguments[0], arguments[1]);
        } else if (arguments.length === 1 && Array.isArray(arguments[0])) {
            return f3(arguments[0]);
        }
    } 
}

/* 
 * Instantiate an object
 */
var obj = new foo("z");

/*
 * Call the overloaded functions using f(...)
 */
console.log(obj.f("x"));         // executes f1, returns "xz"
console.log(obj.f("x", "y"));    // executes f2, returns "xyz"
console.log(obj.f(["x", "y"]));  // executes f3, returns "xy"

Eu apenas tentei isso, talvez ele se adapte às suas necessidades. Dependendo do número dos argumentos, você pode acessar uma função diferente. Você inicializa a primeira vez que você chamá-lo. E o mapa de funções está escondido no fechamento.

TEST = {};

TEST.multiFn = function(){
    // function map for our overloads
    var fnMap = {};

    fnMap[0] = function(){
        console.log("nothing here");
        return this;    //    support chaining
    }

    fnMap[1] = function(arg1){
        //    CODE here...
        console.log("1 arg: "+arg1);
        return this;
    };

    fnMap[2] = function(arg1, arg2){
        //    CODE here...
        console.log("2 args: "+arg1+", "+arg2);
        return this;
    };

    fnMap[3] = function(arg1,arg2,arg3){
        //    CODE here...
        console.log("3 args: "+arg1+", "+arg2+", "+arg3);
        return this;
    };

    console.log("multiFn is now initialized");

    //    redefine the function using the fnMap in the closure
    this.multiFn = function(){
        fnMap[arguments.length].apply(this, arguments);
        return this;
    };

    //    call the function since this code will only run once
    this.multiFn.apply(this, arguments);

    return this;    
};

Test-lo.

TEST.multiFn("0")
    .multiFn()
    .multiFn("0","1","2");

Uma vez que JavaScript não tem função de opções de sobrecarga objeto pode ser usado em seu lugar. Se houver um ou dois argumentos necessários, é melhor mantê-los separados do objeto opções. Aqui está um exemplo de como usar opções de objeto e valores povoadas para o valor padrão no caso, se o valor não foi aprovada em opções objeto.

    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 + "}";

}

aqui é um exemplo de como usar opções objeto

Não há nenhuma maneira de sobrecarga de funções em javascript. Então, eu recomendo como a seguinte pelo método typeof() em vez de múltipla função de falsificar sobrecarga.

function multiTypeFunc(param)
{
    if(typeof param == 'string') {
        alert("I got a string type parameter!!");
     }else if(typeof param == 'number') {
        alert("I got a number type parameter!!");
     }else if(typeof param == 'boolean') {
        alert("I got a boolean type parameter!!");
     }else if(typeof param == 'object') {
        alert("I got a object type parameter!!");
     }else{
        alert("error : the parameter is undefined or null!!");
     }
}

Boa sorte!

INTRODUÇÃO

Até agora a leitura através de tantas respostas daria a alguém uma dor de cabeça. Qualquer um que tenta conhecer o conceito precisa saber os seguintes pré-requisito s.

Function overloading Definition , Function Length property , Function argument property

Function overloading em sua forma mais simples meio de formulário que uma função executa diferentes tarefas com base no número de argumentos que estão sendo passados ??para ele. Notavelmente o TASK1, TASK2 e task3 são destacadas a seguir e estão a ser realizadas na base do número de arguments de ser passado para o mesmo fooYo função.

// if we have a function defined below
function fooYo(){
     // do something here
}
// on invoking fooYo with different number of arguments it should be capable to do different things

fooYo();  // does TASK1
fooYo('sagar'); // does TASK2
fooYo('sagar','munjal'); // does TAKS3

NOTA - JS não fornece a capacidade inerente de sobrecarga de funções.

Alternativa

John E Resig (criador de JS) apontou uma alternativa que utiliza os pré-requisitos acima para alcançar a capacidade de implementar sobrecarga de funções.

O código abaixo utiliza uma abordagem simples, mas ingênuo usando if-else ou switch comunicado.

  • avalia a propriedade argument-length.
  • valores diferentes resultar na invocação de funções diferentes.

var ninja = {
  whatever: function() {
       switch (arguments.length) {
         case 0:
           /* do something */
           break;
         case 1:
           /* do something else */
           break;
         case 2:
           /* do yet something else */
           break;
       //and so on ...
    } 
  }
}

Outra técnica é muito mais limpo e dinâmico. O destaque desta técnica é a função genérica addMethod.

  • definimos um addMethod função que é usado para adicionar funções diferentes para um objeto com o mesmo nome e diferentes funcionalidades .

  • abaixo a função addMethod aceita três parâmetros nome do objeto object, nome da função name e a função que queremos ser invocado fn.

  • dentro de definição addMethod var old armazena a referência ao function anterior sendo armazenadas com a ajuda de fechamento -. Uma bolha protetora

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

  • uso depurador para compreender o fluxo do código.
  • abaixo do addMethod adiciona três funções que quando invocados usando ninja.whatever(x) com o número de argumentos x que pode ser qualquer coisa, isto é, em branco ou um ou mais de um invoca funções diferentes, tal como definidos ao fazer uso da função addMethod.

var ninja = {};
debugger;


addMethod(ninja,'whatever',function(){ console.log("I am the one with ZERO arguments supplied") });
addMethod(ninja,'whatever',function(a){ console.log("I am the one with ONE arguments supplied") });
addMethod(ninja,'whatever',function(a,b){ console.log("I am the one with TWO arguments supplied") });


ninja.whatever();
ninja.whatever(1,2);
ninja.whatever(3);

Outra maneira de abordar isso é usando a variável especial: argumentos , esta é uma implementação:

function sum() {
    var x = 0;
    for (var i = 0; i < arguments.length; ++i) {
        x += arguments[i];
    }
    return x;
}

para que você possa modificar este código para:

function sum(){
    var s = 0;
    if (typeof arguments[0] !== "undefined") s += arguments[0];
.
.
.
    return s;
}

verificar isso. Ela é muito legal. http://ejohn.org/blog/javascript-method-overloading/ Enganar o Javascript para permitir que você faça chamadas como este:

var users = new Users();
users.find(); // Finds all
users.find("John"); // Finds users by name
users.find("John", "Resig"); // Finds users by first and last name

Como este post já contém uma série de soluções diferentes eu pensei que eu postar uma outra.

function onlyUnique(value, index, self) {
    return self.indexOf(value) === index;
}

function overload() {
   var functions = arguments;
   var nroffunctionsarguments = [arguments.length];
    for (var i = 0; i < arguments.length; i++) {
        nroffunctionsarguments[i] = arguments[i].length;
    }
    var unique = nroffunctionsarguments.filter(onlyUnique);
    if (unique.length === arguments.length) {
        return function () {
            var indexoffunction = nroffunctionsarguments.indexOf(arguments.length);
            return functions[indexoffunction].apply(this, arguments);
        }
    }
    else throw new TypeError("There are multiple functions with the same number of parameters");

}

esta pode ser utilizada como se mostra abaixo:

var createVector = overload(
        function (length) {
            return { x: length / 1.414, y: length / 1.414 };
        },
        function (a, b) {
            return { x: a, y: b };
        },
        function (a, b,c) {
            return { x: a, y: b, z:c};
        }
    );
console.log(createVector(3, 4));
console.log(createVector(3, 4,5));
console.log(createVector(7.07));

Esta solução não é perfeita, mas eu só quero demonstrar como isso poderia ser feito.

Você pode utilizador o 'addMethod' de John Resig. Com este método você pode "sobrecarregar" os métodos baseados em argumentos contar.

// addMethod - By John Resig (MIT Licensed)
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 );
    };
}

Além disso, criei uma alternativa a este método que usa o cache para manter as variações da função. Os differencies são descritos aqui

// addMethod - By Stavros Ioannidis
function addMethod(obj, name, fn) {
  obj[name] = obj[name] || function() {
    // get the cached method with arguments.length arguments
    var method = obj[name].cache[arguments.length];

    // if method exists call it 
    if ( !! method)
      return method.apply(this, arguments);
    else throw new Error("Wrong number of arguments");
  };

  // initialize obj[name].cache
  obj[name].cache = obj[name].cache || {};

  // Check if a method with the same number of arguments exists  
  if ( !! obj[name].cache[fn.length])
    throw new Error("Cannot define multiple '" + name +
      "' methods with the same number of arguments!");

  // cache the method with fn.length arguments
  obj[name].cache[fn.length] = function() {
    return fn.apply(this, arguments);
  };
}

Forwarding Padrão => a melhor prática em JS sobrecarga

Encaminhar para outra função que o nome é construído a partir dos 3 e 4 pontos:

  1. Usando o número de argumentos
  2. Verificar tipos de argumentos
window['foo_'+arguments.length+'_'+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments)

Aplicação no seu caso:

 function foo(){
          return window['foo_'+arguments.length+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments);

  }
   //------Assuming that `x` , `y` and `z` are String when calling `foo` . 

  /**-- for :  foo(x)*/
  function foo_1_string(){
  }
  /**-- for : foo(x,y,z) ---*/
  function foo_3_string_string_string(){

  }

Outros Amostra Complex:

      function foo(){
          return window['foo_'+arguments.length+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments);
       }

        /** one argument & this argument is string */
      function foo_1_string(){

      }
       //------------
       /** one argument & this argument is object */
      function foo_1_object(){

      }
      //----------
      /** two arguments & those arguments are both string */
      function foo_2_string_string(){

      }
       //--------
      /** Three arguments & those arguments are : id(number),name(string), callback(function) */
      function foo_3_number_string_function(){
                let args=arguments;
                  new Person(args[0],args[1]).onReady(args[3]);
      }

       //--- And so on ....   

Sobrecarga de função através de dinâmica Polimorfismo em 100 linhas de JS

Esta é a partir de um conjunto maior de código que inclui o isFn, isArr, etc funções de controlo do tipo. A versão VanillaJS abaixo foi reformulado para remover todas as dependências externas, no entanto, terá de definir que você está próprias funções verificação de tipo para uso nas chamadas .add().

Nota:. Esta é uma função de auto-execução (para que possamos ter um fecho / escopo fechado), daí a designação de window.overload em vez de 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;
}();

Uso:

O chamador define suas funções sobrecarregadas, atribuindo uma variável para o retorno de overload(). Graças ao encadeamento, as sobrecargas adicionais podem ser definidos em série:

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

O único argumento opcional para overload() define a função "default" para chamar se a assinatura não pode ser identificado. Os argumentos para .add() são:

  1. fn: function definir a sobrecarga;
  2. a_vArgumentTests: Array de functions que definem os testes a serem executados no arguments. Cada function aceita um único argumento e retorna truethy com base em se o argumento é válido;
  3. sAlias (opcional): string definindo o alias para aceder directamente a função de sobrecarga (fn), por exemplo myOverloadedFn.noArgs() vai chamar essa função diretamente, evitando os testes de polimorfismo dinâmica dos argumentos.

Esta implementação realmente permite mais de sobrecargas de função apenas tradicionais como o segundo argumento a_vArgumentTests para .add() em tipos define personalizados prática. Então, você poderia argumentos portão não só com base no tipo, mas em faixas, valores ou coleções de valores!

Se você olhar através das 145 linhas de código para overload() você verá que cada assinatura é categorizado pelo número de arguments passado para ele. Isto é feito para que nós estamos limitando o número de testes que estão sendo executados. Eu também manter o controle de uma contagem de chamadas. Com algum código adicional, as matrizes de funções sobrecarregadas poderia ser re-ordenada de modo que as funções mais comumente chamado são testados primeiro, novamente adicionando algumas medidas de melhoria de desempenho.

Agora, existem algumas ressalvas ... Como Javascript é vagamente digitado, você tem que ter cuidado com o seu vArgumentTests como um integer poderia ser validado como um float, etc.

JSCompress.com versão (bytes de 1114, 744 g bytes-compactado):

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}();

Você pode agora fazer a função sobrecarga no ECMAScript 2018 sem polyfills, verificando comprimento var / tipo, etc., apenas usar o propagação sintaxe .

function foo(var1, var2, opts){
  // set default values for parameters
  const defaultOpts = {
    a: [1,2,3],
    b: true,
    c: 0.3289,
    d: "str",
  }
  // merge default and passed-in parameters
  // defaultOpts must go first!
  const mergedOpts = {...defaultOpts, ...opts};

  // you can now refer to parameters like b as mergedOpts.b,
  // or just assign mergedOpts.b to b
  console.log(mergedOpts.a);
  console.log(mergedOpts.b);
  console.log(mergedOpts.c);  
  console.log(mergedOpts.d);
}
// the parameters you passed in override the default ones
// all JS types are supported: primitives, objects, arrays, functions, etc.
let var1, var2="random var";
foo(var1, var2, {a: [1,2], d: "differentString"});

// parameter values inside foo:
//a: [1,2]
//b: true
//c: 0.3289
//d: "differentString"

O que é a sintaxe propagação?

O Resto / Propriedades spread para proposta ECMAScript (estágio 4) adiciona propriedades espalhadas para literais de objeto. Ele copia possui propriedades enumeráveis ??de um objecto fornecida para um novo objecto. Mais sobre MDN

Nota: sintaxe propagação em literais de objeto não funciona em Edge e IE e é um recurso experimental. ver a compatibilidade do navegador

A primeira opção realmente merece atenção porque é a coisa que eu vim acima na configuração do código bastante complexo. Portanto, a minha resposta é

  1. O uso de nomes diferentes, em primeiro lugar

Com um pouco, mas essencial dica, nomes deve olhar diferente para computador, mas não para você. Nome funções sobrecarregadas como:. Func, func1, func2

Esta é uma questão de idade, mas um que eu acho que precisa de outra entrada (embora eu duvido que alguém vai lê-lo). A utilização das expressões função imediatamente Invoked (IIFE) pode ser usado em conjunto com tampas e funções em linha para permitir a função de sobrecarga. Considere o seguinte exemplo (artificial):

var foo;

// original 'foo' definition
foo = function(a) {
  console.log("a: " + a);
}

// define 'foo' to accept two arguments
foo = (function() {
  // store a reference to the previous definition of 'foo'
  var old = foo;

  // use inline function so that you can refer to it internally
  return function newFoo(a,b) {

    // check that the arguments.length == the number of arguments 
    // defined for 'newFoo'
    if (arguments.length == newFoo.length) {
      console.log("a: " + a);
      console.log("b: " + b);

    // else if 'old' is a function, apply it to the arguments
    } else if (({}).toString.call(old) === '[object Function]') {
      old.apply(null, arguments);
    }
  }
})();

foo(1);
> a: 1
foo(1,2);
> a: 1
> b: 2
foo(1,2,3)
> a: 1

Em suma, o uso do IIFE cria um escopo local, o que nos permite definir a old variável privada para armazenar uma referência para a definição inicial do foo função. Esta função retorna uma função newFoo linha que registra o conteúdo de ambos os dois argumentos, se ele é passado exatamente dois argumentos a e b ou chama a função old se arguments.length !== 2. Este padrão pode ser repetido qualquer número de vezes para dotar uma variável com vários defitions funcionais diferentes.

O JavaScript é uma linguagem sem tipo, e eu só acho que faz sentido sobrecarregar um método / função com relação ao número de parâmetros. Por isso, eu recomendaria para verificar se o parâmetro foi definido:

myFunction = function(a, b, c) {
     if (b === undefined && c === undefined ){
          // do x...
     }
     else {
          // do y...
     }
};

A partir de julho de 2017, a seguinte foi a técnica comum. Note que também pode executar a verificação de tipo dentro da função.

function f(...rest){   // rest is an array
   console.log(rest.length);
   for (v of rest) if (typeof(v)=="number")console.log(v);
}
f(1,2,3);  // 3 1 2 3

Para o seu caso de uso, este é como eu iria enfrentá-lo com ES6 (uma vez que já é o fim de 2017):

const foo = (x, y, z) => {
  if (y && z) {
    // Do your foo(x, y, z); functionality
    return output;
  }
  // Do your foo(x); functionality
  return output;
}

Você pode, obviamente, adaptar este para trabalhar com qualquer quantidade de parâmetros e apenas alterar suas declarações condicionais em conformidade.

Algo como isso pode ser feito por sobrecarga de funções.

function addCSS(el, prop, val) {
  return {
    2: function() {
      // when two arguments are set
      // now prop is an oject
      for (var i in prop) {
          el.style[i] = prop[i];
      }
    },
    3: function() {
      // when three arguments are set
      el.style[prop] = val;
    }
    }[arguments.length]();
}
// usage
var el = document.getElementById("demo");
addCSS(el, "color", "blue");
addCSS(el, {
    "backgroundColor": "black",
  "padding": "10px"
});

Fonte

over.js para resolver este problema é uma maneira muito elegante. Você pode fazer:

var obj = {

  /**
   * Says something in the console.
   *
   * say(msg) - Says something once.
   * say(msg, times) - Says something many times.
   */
  say: Over(
    function(msg$string){
      console.info(msg$string);
    },
    function(msg$string, times$number){
      for (var i = 0; i < times$number; i++) this.say(msg$string);
    }
  )

};

Então, eu realmente gostei esta maneira de fazer as coisas que eu encontrei em segredos do javascript ninja

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

Você, então, usar addMethod para adicionar funções sobrecarregadas a qualquer objeto. A principal confusão neste código para mim foi o uso de fn.length == arguments.length - Isso funciona porque fn.length é o número de parâmetros esperados, enquanto arguments.length é o número de parâmetros que são, na verdade, chamado com o função. A razão a função anônima não tem argumento é porque você pode passar em qualquer número de argumentos em javascript e a língua é perdoar.

Eu gostei porque você pode usá-lo em todos os lugares - basta criar essa função e simplesmente usar o método em qualquer base de código que você deseja.

Ele também evita ter um ridiculamente grande declaração if / switch, que se torna difícil de ler se você começar a escrever código complexo (a resposta aceita irá resultar em isso).

Em termos de contras, eu acho que o código é inicialmente um pouco obscuro ... mas eu não tenho certeza de outros?

Eu gostaria de compartilhar um exemplo útil de sobrecarga semelhante abordagem.

function Clear(control)
{
  var o = typeof control !== "undefined" ? control : document.body;
  var children = o.childNodes;
  while (o.childNodes.length > 0)
    o.removeChild(o.firstChild);
}

Uso: Claro(); // Limpa todo o documento

Clear (myDiv); // painel Limpa referenciado por myDiv

Eu gosto @ abordagem da AntouanK. Sinto-me frequentemente oferecendo uma função com números diferentes S parâmetros e tipos diferentes. Às vezes, eles não seguem uma ordem. Eu uso para mapear procurando os tipos de parâmetros:

findUDPServers: function(socketProperties, success, error) {
    var fqnMap = [];

    fqnMap['undefined'] = fqnMap['function'] = function(success, error) {
        var socketProperties = {name:'HELLO_SERVER'};

        this.searchServers(socketProperties, success, error);
    };

    fqnMap['object'] = function(socketProperties, success, error) {
        var _socketProperties = _.merge({name:'HELLO_SERVER'}, socketProperties || {});

        this.searchServers(_socketProperties, success, error);
    };

    fqnMap[typeof arguments[0]].apply(this, arguments);
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top