Перегрузка функций в Javascript-рекомендации

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

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

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

30 ответов


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

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

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

затем вы можете справиться с этим в любом случае вы хотите в ваш метод. [Переключатель, if-else и т. д.]


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

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:

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

EDIT: изменено, если тест на предложение Яна


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


правильный ответ: в JAVASCRIPT нет перегрузки.

проверка / переключение внутри функции не перегружается.

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

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

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

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


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

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

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


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

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

  2. используя ряд аргументы. похож на последний вариант, но может работать, когда #1 не работает.

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

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


вот тестовый тест на перегрузку функций -http://goo.gl/UyYAD (код показан в этом сообщении). Он показывает, что перегрузка функции (с учетом типов) может быть вокруг 13 раз медленнее в Google хром в V8 по состоянию на 16.0 (beta).

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

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

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

мой пример исходит из реализации прямоугольника-отсюда упоминание измерения и точки. Возможно, Rectangle может добавить GetRectangle() метод Dimension и Point prototype, а затем проблема перегрузки функции сортируется. А как насчет примитивов? Ну, у нас есть длина аргумента, которая теперь является допустимым тестом, так как объекты имеют 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);

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

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

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

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

function foo(x)
{
} 

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

fooNew(x,y,z)
{
}

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

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

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

(дополнительные детали)


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

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

поскольку 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 object


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

удачи!


зацени. Это очень круто. 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

введение

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

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 не обеспечивает встроенную способность перегружать функции.

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

Джон 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 ...
    } 
  }
}

другая техника гораздо более чистая и динамичная. Изюминкой этой техники является 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);

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

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

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

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" от Джона Resig. С помощью этого метода вы можете "перегрузить" методы, основанные на подсчете аргументов.

// 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-й и 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 ....   

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

  • VanillaJS, никаких внешних зависимостей
  • Полная Поддержка Браузера - массив.прототип.ломтик, до .add() на практике определяет пользовательские типы. Таким образом, вы можете создавать аргументы не только на основе типа, но и на диапазонах, ценности или коллекции ценностей!

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

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

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

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

  1. используя различные имена в первую очередь

с небольшим, но существенным намеком, имена должны выглядеть по-разному для компьютера, но не для вас. Имя перегруженных функций, таких как: 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 и old функция if 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;
}

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


мы более.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 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);
    }
  }
}

затем вы используете addMethod для добавления перегруженных функций к любому объекту. Основной путаницей в этом коде для меня было использование fn.аргумент length.длина - это работает, потому что fn.длина-количество ожидаемых параметров, в то время как аргументы.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);
}

использование: Clear (); / / очищает весь документ

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


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

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

я использую эту функцию, чтобы украсить мои перегрузки в течение многих лет:

function overload(){
  const fs = arguments, fallback = fs[fs.length - 1];
  return function(){
    const f = fs[arguments.length] || (arguments.length >= fs.length ? fallback : null);
    return f.apply(this, arguments);
  }
}

установлено:

function curry1(f){
  return curry2(f, f.length);
}

function curry2(f, minimum){
  return function(...applied){
    if (applied.length >= minimum) {
      return f.apply(this, applied);
    } else {
      return curry2(function(...args){
        return f.apply(this, applied.concat(args));
      }, minimum - applied.length);
    }
  }
}

export const curry = overload(null, curry1, curry2);

взгляните на jQuery off способ:

  function off( types, selector, fn ) {
    var handleObj, type;
    if ( types && types.preventDefault && types.handleObj ) {

        // ( event )  dispatched jQuery.Event
        handleObj = types.handleObj;
        jQuery( types.delegateTarget ).off(
            handleObj.namespace ?
                handleObj.origType + "." + handleObj.namespace :
                handleObj.origType,
            handleObj.selector,
            handleObj.handler
        );
        return this;
    }
    if ( typeof types === "object" ) {

        // ( types-object [, selector] )
        for ( type in types ) {
            this.off( type, selector, types[ type ] );
        }
        return this;
    }
    if ( selector === false || typeof selector === "function" ) {

        // ( types [, fn] )
        fn = selector;
        selector = undefined;
    }
    if ( fn === false ) {
        fn = returnFalse;
    }
    return this.each( function() {
        jQuery.event.remove( this, types, fn, selector );
    } );
  }

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


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

см. пример перегрузки метода с использованием этой библиотеки:

eutsiv.define('My.Class', {
    constructor: function() {
        this.y = 2;
    },
    x: 3,
    sum: function() {
        return this.x + this.y;
    },
    overloads: {
        value: [
            function() { return this.x + ', ' + this.y },
            function(p1) { this.x = p1; },
            function(p1, p2) { this.x = p1; this.y = p2; }  // will set x and y
        ]
    }
});

var test = new My.Class({ x: 5 });   // create the object
test.value();                        // will return '5, 2'
test.sum();                          // will return 7
test.value(13);                      // will set x to 13
test.value();                        // will return '13, 2'
test.sum();                          // will return 15
test.value(10, 20);                  // will set x to 10 and y to 20
test.value();                        // will return '10, 20'
test.sum();                          // will return 30

любые отзывы, исправления ошибок, документы и тесты улучшаются приветствуются!

https://github.com/eutsiv/eutsiv.js