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

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

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

  • Использование разных имен в первую очередь
  • Использование дополнительных аргументов, таких как y = y || 'default'
  • Использование количества аргументов
  • Проверка типов аргументов
  • Или как?

Ответ 1

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

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

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

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

Ответ 2

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

С#:

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, чем С#. Параметры, которые не указаны, являются "undefined" в javascript, который вычисляет false в выражении if. Однако определение функции не передает информацию о том, что p2 и p3 являются необязательными. Если вам нужно много перегрузки, jQuery решил использовать объект в качестве параметра, например jQuery.ajax(options). Я согласен с ними в том, что это самый мощный и четко документируемый подход к перегрузке, но мне редко требуется более одного или двух быстрых необязательных параметров.

EDIT: измененный IF-тест на предложение Ian

Ответ 3

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

Ответ 4

Правильный ответ: В JAVASCRIPT нет переполнения.

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

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

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

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

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

Ответ 5

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

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

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

Ответ 6

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

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

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

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


Вот эталон различных подходов - https://jsperf.com/function-overloading. Это показывает, что перегрузка функций (с учетом типов) может быть примерно в 13 раз медленнее в Google Chrome V8 с 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 and Point. Возможно, Rectangle может добавить метод GetRectangle() к прототипу Dimension и Point, и тогда проблема с перегрузкой функции будет решена. А как насчет примитивов? Итак, у нас есть длина аргумента, которая теперь является допустимым тестом, поскольку у объектов есть метод 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);

Ответ 7

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

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

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

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

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

Ответ 8

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

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

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

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

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: выше ссылка идет на мой личный блог, который имеет дополнительные подробности.

Ответ 9

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

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

Ответ 10

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

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

Ответ 11

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

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

}

здесь является примером того, как использовать объект options

Ответ 12

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

Удачи!

Ответ 13

Другим способом подхода к этому является использование специальной переменной: 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;
}

Ответ 14

Введение

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

Function overloading Definition, Function Length property, Function argument property

Function overloading в своей простейшей форме означает, что функция выполняет разные задачи на основе количества передаваемых ей аргументов. Примечательно, что TASK1, TASK2 и TASK3 подсвечены ниже и выполняются на основе количества 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 не обеспечивает встроенную возможность перегрузки функций.

Alternative

John E Resig (создатель 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 ...
    } 
  }
}

Ответ 15

проверьте это. Это очень здорово. http://ejohn.org/blog/javascript-method-overloading/ Trick 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

Ответ 16

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

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

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

Ответ 17

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

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

Ответ 18

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

Переслать в другую функцию, имя которой построено из 3-й и 4-й точек:

  1. Использование количества аргументов
  2. Проверка типов аргументов
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 ....   

Ответ 19

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

Это из большей части кода, который включает функции проверки типа 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 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")
;

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

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

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

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

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

Версия JSCompress.com(1114 байт, 744 байта g-zipped):

window.overload=function(){'use strict';function b(n){return'[object Function]'===m.call(n)}function c(n){return!!(n&&n===Object(n))}function d(n){return'[object Array]'===m.call(n)}function e(n){return Array.prototype.slice.call(n)}function g(n,p,q){if(q=d(q)?q:e(q),b(n))return n.apply(p||this,q)}function h(n,p,q){q&&!n[q]&&(n[q]=p)}function k(n){var p=b(n)?{default:n}:c(n)?n:{default:function(){throw'Overload not found for arguments: ['+e(arguments)+']'}},q=function(){var r,s,t,u=arguments,v=p[u.length]||[];for(s=0;s<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}();

Ответ 20

Теперь вы можете выполнять перегрузку функций в 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"

Ответ 21

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

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

Источник

Ответ 22

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

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

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

Ответ 23

Это старый вопрос, но мне кажется, что нужна другая запись (хотя я сомневаюсь, что кто-то ее прочтет). Использование выражений с мгновенной вызывной функцией (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 или вызывает функцию old, если arguments.length !== 2. Этот шаблон можно повторять любое количество раз, чтобы наделить одну переменную несколькими различными функциональными отклонениями.

Ответ 24

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

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

Ответ 25

По состоянию на июль 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

Ответ 26

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

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

Ответ 27

Перегрузка функций в Javascript:

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

Javascript не имеет встроенной функции перегрузки. Однако такое поведение можно эмулировать разными способами. Вот удобный простой:

function sayHi(a, b) {
  console.log('hi there ' + a);
  if (b) { console.log('and ' + b) } // if the parameter is present, execute the block
}

sayHi('Frank', 'Willem');

Ответ 28

Мы сделали 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);
    }
  )

};

Ответ 29

Итак, мне очень понравился этот способ делать то, что я нашел в секретах ниндзя 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, который становится трудно читаемым, если вы начинаете писать сложный код (в результате получится принятый ответ).

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

Ответ 30

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

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);//Очищает панель, на которую ссылается myDiv