Перегрузка функций в Javascript — лучшие практики

StackOverflow https://stackoverflow.com/questions/456177

  •  19-08-2019
  •  | 
  •  

Вопрос

Каковы наилучшие способы имитировать перегрузку функций в Javascript?

Я знаю, что в Javascript невозможно перегружать функции, как в других языках.Если бы мне нужна была функция с двумя вариантами использования foo(x) и foo(x,y,z) какой лучший/предпочтительный способ:

  1. Использование разных имен в первую очередь
  2. Использование необязательных аргументов, таких как y = y || 'default'
  3. Использование количества аргументов
  4. Проверка типов аргументов
  5. Или как?
Это было полезно?

Решение

Лучший способ перегрузить функцию параметрами - не проверять длину аргумента или его типы; проверка типов просто замедлит ваш код, и вы получите удовольствие от массивов, нулей, объектов и т. д.

То, что делают большинство разработчиков, это привязывает объект как последний аргумент к своим методам. Этот объект может содержать что угодно.

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

Тогда вы можете справиться с этим так, как хотите в своем методе. [Переключение, если-еще, и т. Д.]

Другие советы

Я часто делаю это:

С#:

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-эквивалент:

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

Этот конкретный пример на самом деле более элегантен в JavaScript, чем в C#.Параметры, которые не указаны, в javascript являются «неопределенными», что в операторе if оценивается как false.Однако определение функции не передает информацию о том, что p2 и p3 являются необязательными.Если вам нужно много перегрузок, jQuery решил использовать объект в качестве параметра, например, jQuery.ajax(options).Я согласен с ними, что это самый мощный и четко документируемый подход к перегрузке, но мне редко нужно больше одного или двух быстрых дополнительных параметров.

РЕДАКТИРОВАТЬ:изменен тест IF по предложению Яна

В JavaScript нет реальной перегрузки функций, поскольку она позволяет передавать любое количество параметров любого типа. Вы должны проверить внутри функции, сколько аргументов и какие типа они есть.

Правильный ответ: в JAVASCRIPT НЕТ ПЕРЕГРУЗКИ.

Проверка / переключение внутри функции не является ПЕРЕГРУЗКОЙ.

Концепция перегрузки: В некоторых языках программирования перегрузка функций или перегрузка методов - это возможность создавать несколько методов с одинаковыми именами в разных реализациях. Вызовы перегруженной функции будут запускать конкретную реализацию этой функции, соответствующую контексту вызова, что позволяет одному вызову функции выполнять различные задачи в зависимости от контекста.

Например, doTask () и doTask (объект O) являются перегруженными методами. Чтобы вызвать последнее, объект должен быть передан как параметр, тогда как первый не требует параметра и вызывается с пустым полем параметра. Распространенной ошибкой будет присвоение объекту значения по умолчанию во втором методе, что приведет к неоднозначной ошибке вызова, поскольку компилятор не будет знать, какой из двух методов использовать.

https://en.wikipedia.org/wiki/Function_overloading

Все предлагаемые реализации великолепны, но, по правде говоря, для JavaScript нет нативной реализации.

Есть два способа лучше подойти к этому:

<Ол>
  • Передайте словарь (ассоциативный массив), если вы хотите оставить большую гибкость

  • Возьмите объект в качестве аргумента и используйте наследование на основе прототипа для повышения гибкости.

  • Вот подход, который допускает перегрузку реальных методов с использованием типов параметров, как показано ниже:

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

    Изменить (2018) . С тех пор, как это было написано в 2011 году, скорость прямых вызовов методов значительно возросла, а скорость перегруженных методов - нет.

    Это не тот подход, который я бы порекомендовал, но стоит подумать над тем, как решить эти проблемы.

    <Ч>

    Вот пример различных подходов - https://jsperf.com/function-overloading. Это показывает, что перегрузка функций (с учетом типов) может быть примерно в 13 раз медленнее в Google V8 Chrome по состоянию на 16,0 (бета) .

    Наряду с передачей объекта (т. е. {x: 0, y: 0}) можно также использовать подход C, когда это уместно, и называть методы соответствующим образом. Например, Vector.AddVector (vector), Vector.AddIntegers (x, y, z, ...) и Vector.AddArray (integerArray). Вы можете посмотреть библиотеки C, такие как OpenGL, для вдохновения в именовании.

    Редактировать . Я добавил тест для передачи объекта и тестирования объекта с использованием как 'param' in arg, так и arg.hasOwnProperty('param'), а перегрузка функции намного быстрее, чем передача объекта и проверка на наличие свойства (по крайней мере, в этом тесте).

    С точки зрения дизайна перегрузка функции допустима или логична только в том случае, если перегруженные параметры соответствуют одному и тому же действию. Таким образом, само собой разумеется, что должен быть базовый метод, который касается только конкретных деталей, в противном случае это может указывать на неправильный выбор дизайна. Таким образом, можно также разрешить использование перегрузки функций путем преобразования данных в соответствующий объект. Конечно, нужно учитывать масштаб проблемы, поскольку нет необходимости создавать сложные дизайны, если вы хотите просто напечатать имя, но для разработки каркасов и библиотек такая мысль оправдана.

    Мой пример взят из реализации Rectangle - отсюда и упоминание Dimension и Point. Возможно, Rectangle может добавить метод GetRectangle() к прототипу Dimension и Point, и тогда проблема перегрузки функции будет решена. А как насчет примитивов? Итак, у нас есть длина аргумента, которая теперь является допустимым тестом, поскольку у объектов есть метод <=>.

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

    Лучший способ действительно зависит от функции и аргументов. Каждый из ваших вариантов - хорошая идея в разных ситуациях. Обычно я пробую их в следующем порядке, пока один из них не сработает:

    <Ол>
  • Использование необязательных аргументов, таких как y = y || 'default'. Это удобно, если вы можете это сделать, но это может не всегда работать практически, например, когда 0 / null / undefined будет допустимым аргументом.

  • Использование количества аргументов. Аналогичен последнему варианту, но может работать, когда # 1 не работает.

  • Проверка типов аргументов. Это может работать в некоторых случаях, когда количество аргументов одинаково. Если вы не можете точно определить типы, вам может потребоваться использовать разные имена.

  • Во-первых, используйте разные имена. Это может потребоваться, если другие параметры не будут работать, нецелесообразны или не соответствуют другим связанным функциям.

  •   

    Если мне нужна функция с двумя функциями foo (x) и foo (x, y, z), что является лучшим / предпочтительным способом?

    Проблема в том, что JavaScript НЕ поддерживает перегрузку методов. Поэтому, если он видит / анализирует две или более функций с одинаковыми именами, он & # 8217; я просто рассмотрю последнюю определенную функцию и перезапишу предыдущие.

    Один из способов, который я считаю подходящим для большинства случаев, заключается в следующем:

    Допустим, у вас есть метод

    function foo(x)
    {
    } 
    

    Вместо перегрузки метода , который невозможен в javascript , вы можете определить новый метод

    fooNew(x,y,z)
    {
    }
    

    , а затем измените первую функцию следующим образом -

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

    Если у вас много таких перегруженных методов, рассмотрите возможность использования switch, а не только if-else операторов.

    ( более подробно )

    PS: приведенная выше ссылка ведет на мой личный блог, в котором есть дополнительная информация.

    Я не уверен насчет наилучшей практики, но вот как я это делаю:

    /*
     * 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"
    

    Я только что попробовал это, возможно, это соответствует вашим потребностям В зависимости от количества аргументов вы можете получить доступ к другой функции. Вы инициализируете его при первом вызове. А карта функций скрыта в замыкании.

    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.multiFn("0")
        .multiFn()
        .multiFn("0","1","2");
    

    Поскольку в JavaScript нет опций перегрузки функций, можно использовать объект. Если есть один или два обязательных аргумента, лучше держать их отдельно от объекта параметров. Ниже приведен пример использования объекта параметров и заполненных значений до значения по умолчанию в случае, если значение не было передано в объекте параметров.

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

    здесь - пример использования объекта параметров

    В javascript нет способа перегрузки функций. Итак, я рекомендую, как следующий метод typeof() вместо множественная функция для фальсификации перегрузки.

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

    Удачи!

    ВВЕДЕНИЕ

    До сих пор чтение такого количества ответов вызвало бы у любого головную боль.Любой, кто пытается понять эту концепцию, должен знать следующее предварительное условиес.

    Function overloading Definition, Function Length property, Function argument property

    Function overloading в своей простейшей форме означает, что функция выполняет различные задачи в зависимости от количества передаваемых ей аргументов.Примечательно, что ЗАДАЧА 1, ЗАДАЧА 2 и ЗАДАЧА 3 выделены ниже и выполняются в зависимости от количества arguments передается той же функции fooYo.

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

    ПРИМЕЧАНИЕ- JS не предоставляет встроенной возможности перегрузки функций.

    Альтернатива

    Джон Э. Ресиг (создатель JS) указал на альтернативу, которая использует вышеуказанные предпосылки для достижения возможности реализации перегрузки функций.

    В приведенном ниже коде используется простой, но наивный подход: if-else или switch заявление.

    • оценивает argument-length свойство.
    • разные значения приводят к вызову разных функций.

    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 ...
        } 
      }
    }

    Другая техника гораздо более чистая и динамичная.Особенностью этой техники является addMethod универсальная функция.

    • мы определяем функцию addMethod который используется для добавления различных функций к объекту с помощью то же имя но различные функциональные возможности.

    • ниже addMethod функция принимает имя объекта с тремя параметрами object, имя функции name и функция, которую мы хотим вызвать fn.

    • Внутри addMethod определение var old сохраняет ссылку на предыдущий function хранится с помощью закрытия - защитного пузыря.

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

    • используйте отладчик, чтобы понять поток кода.
    • ниже addMethod добавляет три функции, которые при вызове с помощью ninja.whatever(x) с количеством аргументов x что может быть чем угодно, т.е.либо пусто, либо одна или более одной вызывают разные функции, определенные при использовании 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);

    Другой способ решения этой проблемы - использование специальной переменной: arguments , это реализация:

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

    чтобы вы могли изменить этот код на:

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

    проверить это. Это очень круто. http://ejohn.org/blog/javascript-method-overloading/ Уловка Javascript, позволяющая вам делать звонки следующим образом:

    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
    

    Поскольку в этом посте уже есть много разных решений, я подумал, что выложу другое.

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

    это можно использовать, как показано ниже:

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

    Это решение не идеально, но я только хочу продемонстрировать, как это можно сделать.

    Вы можете использовать addMethod от Джона Резига. С помощью этого метода вы можете & Quot; overload & Quot; методы, основанные на количестве аргументов.

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

    Я также создал альтернативу этому методу, который использует кэширование для хранения вариантов функции. Здесь описаны различия

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

    Шаблон пересылки = > лучшая практика по перегрузке JS

    Переслать другой функции, имя которой построено с 3-го & amp; 4 балла:

      
        
    1. Использование количества аргументов
    2.   
    3. Проверка типов аргументов
    4.   
    window['foo_'+arguments.length+'_'+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments)
    

    Заявление по вашему делу:

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

    Другой сложный образец:

          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 ....   
    

    Перегрузка функций посредством динамического полиморфизма в 100 строках JS

    • VanillaJS, без внешних зависимостей
    • Полная поддержка браузера - Массив.prototype.slice, Object.prototype.toString
    • 1114 байт в углицированном виде / 744 байт в сжатом формате g-zip

    Это из более крупного кода, который включает в себя isFn, isArr, и т. д.функции проверки типов.Версия VanillaJS, приведенная ниже, была переработана для удаления всех внешних зависимостей, однако вам придется определить собственные функции проверки типов для использования в .add() звонки.

    Примечание: Это самоисполняющаяся функция (поэтому мы можем иметь замыкание/закрытую область), отсюда и назначение window.overload скорее, чем 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;
    }();
    

    Использование:

    Вызывающая сторона определяет свои перегруженные функции, присваивая переменную возвращаемому значению. overload().Благодаря цепочке дополнительные перегрузки могут быть определены последовательно:

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

    Единственный необязательный аргумент для overload() определяет функцию «по умолчанию», которую следует вызывать, если подпись не может быть идентифицирована.Аргументы .add() являются:

    1. fn: function определение перегрузки;
    2. a_vArgumentTests: Array из functionопределение тестов для запуска на arguments.Каждый function принимает один аргумент и возвращает trueваше основано на том, действителен ли аргумент;
    3. sAlias (Необязательный): string определение псевдонима для прямого доступа к функции перегрузки (fn), например myOverloadedFn.noArgs() вызовет эту функцию напрямую, избегая динамических проверок полиморфизма аргументов.

    Эта реализация на самом деле позволяет использовать больше, чем просто традиционные перегрузки функций, поскольку вторая a_vArgumentTests аргумент для .add() на практике определяет пользовательские типы.Таким образом, вы можете группировать аргументы не только по типу, но и по диапазонам, значениям или коллекциям значений!

    Если вы просмотрите 145 строк кода, overload() вы увидите, что каждая подпись классифицирована по количеству arguments перешёл на это.Это сделано для того, чтобы ограничить количество выполняемых тестов.Я также веду учет звонков.С помощью некоторого дополнительного кода массивы перегруженных функций можно было бы пересортировать так, чтобы сначала тестировались наиболее часто вызываемые функции, что опять-таки в некоторой степени повышает производительность.

    Теперь есть некоторые предостережения...Поскольку Javascript слабо типизирован, вам придется быть осторожным с vArgumentTests как integer может быть подтверждено как float, и т. д.

    Версия JCompress.com (1114 байт, 744 байт в архиве 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}();
    

    Теперь вы можете выполнять перегрузку функций в ECMAScript 2018 без полифилов, проверки длины/типа переменной и т. д. просто используйте синтаксис распространения.

    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"

    Что такое синтаксис распространения?

    Предложение Rest/Spread Properties для ECMAScript (этап 4) добавляет свойства распространения в литералы объектов.Он копирует собственные перечислимые свойства из предоставленного объекта в новый объект. Подробнее о МДН

    Примечание:Синтаксис распространения в литералах объектов не работает в Edge и IE и является экспериментальной функцией. посмотреть совместимость браузера

    Первый вариант действительно заслуживает внимания, потому что я подошел к довольно сложной настройке кода. Итак, мой ответ

      <Ол>   
  • Использование разных имен в первую очередь
  •   

    С небольшой, но важной подсказкой имена должны отличаться для компьютера, но не для вас. Назовите перегруженные функции, такие как: func, func1, func2.

    Это старый вопрос, но мне кажется, что нужна еще одна запись (хотя я сомневаюсь, что кто-нибудь прочтет ее). Использование выражений немедленного вызова функций (IIFE) может использоваться в сочетании с замыканиями и встроенными функциями для обеспечения перегрузки функций. Рассмотрим следующий (надуманный) пример:

    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
    

    Короче говоря, использование IIFE создает локальную область, позволяя нам определить личную переменную old для хранения ссылки на первоначальное определение функции foo. Затем эта функция возвращает встроенную функцию newFoo, которая регистрирует содержимое обоих двух аргументов, если ей передано ровно два аргумента a и b, или вызывает функцию arguments.length !== 2, если <=>. Этот шаблон может повторяться любое количество раз, чтобы наделить одну переменную несколькими различными функциональными определениями.

    JavaScript - нетипизированный язык, и я думаю, что имеет смысл перегрузить метод / функцию в отношении количества параметров. Следовательно, я бы порекомендовал проверить, был ли параметр определен:

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

    По состоянию на июль 2017 года следующее было распространено. Обратите внимание, что мы также можем выполнять проверку типов внутри функции.

    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
    

    Для вашего случая использования, я бы решил это с помощью ES6 (так как это уже конец 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;
    }
    

    Очевидно, что вы можете адаптировать его для работы с любым количеством параметров и просто соответствующим образом изменить свои условные выражения.

    Нечто подобное можно сделать для перегрузки функций.

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

    Источник

    Мы решили over.js решить эту проблему - очень элегантный способ. Вы можете сделать:

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

    Поэтому мне очень понравился этот способ делать то, что я нашел в секретах ниндзя javascript

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

    Затем вы используете addMethod для добавления перегруженных функций к любому объекту. Основной путаницей в этом коде для меня было использование fn.length == arguments.length - это работает, потому что fn.length - это число ожидаемых параметров, а arguments.length - это количество параметров, которые фактически вызываются с помощью функция. Причина, по которой анонимная функция не имеет аргументов, заключается в том, что вы можете передать любое количество аргументов в javascript, и язык простителен.

    Мне это понравилось, потому что вы можете использовать его везде - просто создайте эту функцию и просто используйте метод в любой кодовой базе, которую вы хотите.

    Это также позволяет избежать смехотворно большого оператора if / switch, который становится трудным для чтения, если вы начнете писать сложный код (в результате будет принят принятый ответ).

    С точки зрения минусов, я думаю, код изначально немного неясен ... но я не уверен в других?

    Я хотел бы поделиться полезным примером подхода, подобного перегрузке.

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

    Использование:Прозрачный();// Очищаем весь документ

    Очистить (мойДив);// Очищает панель, на которую ссылается myDiv

    Мне нравится подход @ AntouanK. Я часто нахожу себя предлагающим функцию с разными числами параметров и разными типами. Иногда они не следуют порядку. Я использую для отображения типов параметров:

    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);
    }
    
    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top