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

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

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

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

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

Breton 19.01.2009 03:41

Это закрыто, но я делаю следующее: this.selectBy = {instance: selectByInstance, // Текст функции: selectByText, // Значение функции: selectByValue // Функция};

Prisoner ZERO 23.06.2012 00:46

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

Keldon Alleyne 24.10.2012 02:57

На всякий случай, я создал облегченный js-фреймворк, который позволяет перегрузку методов на основе типов. Очевидно, что те же предостережения применимы и в отношении производительности, но до сих пор он работал хорошо для моих нужд и все еще имеет довольно много возможностей для улучшения: blog.pebbl.co.uk/2013/01/describejs.html#methodoverloading

Pebbl 15.01.2013 04:24
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
823
4
475 220
37
Перейти к ответу Данный вопрос помечен как решенный

Ответы 37

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

Джон Ресиг (из jQuery) однажды попробовал это, но попытка была чисто академической и не принесла никакой реальной пользы.

scunliffe 19.01.2009 03:38

Перегрузка функции Джона Ресига здесь ejohn.org/blog/javascript-method-overloading

Terrance 27.05.2011 00:45

@Terrance: Мне тоже нравится метод Ресига. Работает как часы. Мне просто нужно найти способ создать для него тест для проверки вариантов использования.

chrisvillanueva 28.06.2013 07:38

«Эта функция не изменит мир, но она короткая, лаконичная и использует малоизвестную функцию JavaScript, поэтому она выигрывает в моей книге». :-)

Frerich Raabe 31.05.2018 09:44

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

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

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

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

roberthuttinger 07.12.2012 18:19

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

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

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

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

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

Ответ принят как подходящий

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

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

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

Тогда вы можете справиться с этим любым способом в своем методе. [Switch, if-else и т. д.]

Есть предложения по использованию этого шаблона с обратным вызовом? stackoverflow.com/questions/9831027/…

Paul Mennega 23.03.2012 18:57

Не могли бы вы предоставить образец реализации foo (), который иллюстрирует, как эти параметры "opts" используются / на которые ссылаются?

Moe Howard 29.03.2012 03:23

Мо // Это могло быть так; if (opts['test']) //if test param exists, do something.. if (opts['bar']) //if bar param exists, do something

Deckard 09.11.2012 17:07

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

d512 05.03.2013 07:13

@ user1334007 невозможно выполнить перегрузку функций, как в Java / .NET. Да, это не «точная» перегрузка, но она выполняет свою работу.

epascarello 05.03.2013 07:24

одно хорошо, что js не поддерживает перегрузку функций .. спасибо за ясность данных

saidesh kilaru 09.05.2013 18:19

Я удивлен, что никто об этом уже не спрашивал: почему не рекомендуется проверять arguments.length? Кроме того, я был здесь раньше и читал Большинство разработчиков делают ..., но я уверен, что это единственное место, где я видел подобное. Этот метод также портит синтаксическую сладость «перегрузок»!

c24w 03.07.2013 14:37

@ c24w Да, вы можете проверить lengthm, но это не сработает по таким причинам, как (obj, str, str) vs (obj, str, bool).

epascarello 03.07.2013 16:52

@epascarello: ах, поэтому нет никаких предположений, что arguments.length - плохое решение, просто в некоторых случаях вам придется вернуться к проверке типов (что не идеально). Попался!

c24w 03.07.2013 17:11

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

Dorian 17.06.2014 19:48

@Dorian Вы не можете комментировать или использовать JSDoc?

epascarello 17.06.2014 22:55

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

epascarello 17.06.2014 23:06

@Dorian Использование объектов параметров - это то, как это делают огромное количество библиотек JS - ваши (кхм) параметры ограничены.

Dave Newton 02.08.2014 01:04

«проверка типов только замедлит ваш код» Условно говоря, создание объекта для последующего поиска свойств намного медленнее, чем проверка типов. Если переданный объект уже существует, то поиск свойств, скорее всего, будет быстрее. jsperf.com/type-check-vs-property-lookup Код в этом ответе создает новый объект с каждым вызовом, который по своей сути медленнее, чем проверка типов.

ReenignE 26.08.2014 01:53

один метод (который будет умышленно, за исключением неподдерживаемых типов) состоит в том, чтобы иметь тонкую оболочку вокруг объекта с функциями, зависящими от типа, с использованием параметра (ов) typeof в качестве ключа. например, var thing_handlers = { string : function(x){ return x;}, number : function(x) {return x+25; } }; function do_thing(x) {return thing_handlers[typeof x](x);}, он и быстрый, и полиморфный, и, что наиболее важно, дает сбой во время выполнения, чтобы заставить разработчика писать безопасный (r) код.

unsynchronized 23.07.2016 03:13

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

Удачи!

Ради бога! Используйте оператор switch!

jedmao 20.05.2013 23:00

Кроме того, если вы настаиваете на том, чтобы не использовать переключатель, вам следует вызвать typeof только один раз. var type = typeof param; if (type === 'string') ...

Walter Stabosz 22.08.2013 07:49

+1 к комментарию к "== = ". Другое преимущество оператора switch перед if (... == ...) - это типобезопасность.

Nathan Cooper 03.10.2013 18:16

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

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.AddIntegers (x, y, z, ...) и Vector.AddArray (integerArray). Вы можете посмотреть библиотеки C, такие как OpenGL, чтобы найти вдохновение.

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

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

Мой пример взят из реализации Rectangle - отсюда и упоминание Dimension и 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);

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

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

Обновлено: изменен тест IF по предложению Яна

Параметры, которые не указаны, - это undefined в JS, а не null. Рекомендуется никогда ничего не устанавливать на undefined, поэтому это не должно быть проблемой, если вы измените свой тест на p2 === undefined.

Tamzin Blake 31.08.2012 21:49

Если вы передадите false в качестве последнего аргумента, он не будет объединять "false" до конца, потому что if (p3) не будет ветвиться.

dreamlax 23.10.2012 09:50

Или более популярный typeof p2 === "undefined"

Ian 05.11.2012 19:14

Спасибо за это! Я тоже знаком с упомянутым вами шаблоном (используется в различных языках семейства C). Не работал в javascript. Искал исправление. Ваше решение отлично сработало! +1 :-)

iPadDeveloper2011 22.02.2013 10:57

Просто небольшое примечание, ваш typeof p2 === "undefined", вероятно, является противоположностью тому, что вы ожидаете в случае вашего примера, я думаю, что typeof p2 !== "undefined" - это то, что вы планировали. Кроме того, могу ли я предложить объединить строку, число и логическое значение, которое вы на самом деле делаете p2 === "number"; p3 === "boolean".

WillFM 23.04.2013 06:56

вы можете использовать p2 !== void 0 для менее подробного сравнения

bevacqua 11.11.2013 21:05

Мне нравится это делать: p3 = p3 || 'значение по умолчанию';

Dorian 17.06.2014 19:46

Что означают === и !==? Почему бы просто не использовать == и !=?

Ricardo Cruz 02.03.2016 22:41

Это можно улучшить с помощью аргументов, а более современный подход может использовать параметры отдыха, concat и join.

Mild Fuzz 08.03.2017 13:43

Небольшое примечание к вашему коду C#. Вызов ToString не требуется, когда вы объединяете объекты с помощью оператора конкатенации (+).

Stack0verflow 13.04.2017 18:27

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

Aamir 21.07.2017 06:08

@ Дориан, p3 = p3 || 'значение по умолчанию' не рекомендуется, потому что если вы передадите false в качестве предполагаемого значения, тогда ваше false будет преобразовано в значение по умолчанию, всегда следует проверять на undefined.

user678415 31.03.2018 03:17

Проверь это. Это очень круто. 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

Привет, Джайдер, посмотри мой ответ, он содержит код для перегрузки метода javascript действительный. Я говорю, что Func(new Point()) и Func(new Rectangle()) будут выполнять разные функции. Но я должен отметить, что это грязный прием, поскольку перегрузка метода на самом деле является задачей времени компиляции, а не времени выполнения.

Keldon Alleyne 24.10.2012 02:51

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

TEST = {};

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

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

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

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

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

    console.info("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");

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

  1. Using different names in the first place

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

Я собирался попробовать перегрузить, но решил просто использовать другие имена, например getDeviceInfoByID и getDeviceInfoByType ...

CramerTV 22.10.2014 02:48

Мы сделали 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, в настоящее время она поддерживает конструкторы, наследование, перегрузку методов по количеству параметров и по типам параметров, миксинам, свойствам статики и синглтону.

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

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

Поскольку в 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.

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 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.info(createVector(3, 4));
console.info(createVector(3, 4,5));
console.info(createVector(7.07));

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

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

var foo;

// original 'foo' definition
foo = function(a) {
  console.info("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.info("a: " + a);
      console.info("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. Этот шаблон можно повторять любое количество раз, чтобы наделить одну переменную несколькими различными функциональными определениями.

Вы можете использовать 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);
  };
}

If I needed a function with two uses foo(x) and foo(x,y,z) which is the best / preferred way?

Проблема в том, что 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.

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

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

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

/*
 * 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.info("f1 called with " + arg1);
            return arg1 + that.x;
        }

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

        var f3 = function(arg1) {
             console.info("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.info(obj.f("x"));         // executes f1, returns "xz"
console.info(obj.f("x", "y"));    // executes f2, returns "xyz"
console.info(obj.f(["x", "y"]));  // executes f3, returns "xy"

@Luis: Я добавил несколько, надеюсь, полезных комментариев.

chessweb 10.08.2015 10:39

Мне нравится подход @ 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);
}

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

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

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

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

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

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

наконец-то нормальный ответ! В JAVASCRIPT НЕТ ПЕРЕГРУЗКИ.

Razvan Tudorica 20.11.2019 00:31

ОП попросил способ перегрузки подделка.

Ateur Games 14.12.2019 00:30

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

Marco 16.12.2019 14:54

#Forwarding Pattern => лучшие практики по перегрузке JS Вперед к другой функции, имя которой состоит из 3-й и 4-й точек:

  1. Using number of arguments
  2. Checking types of arguments
window['foo_'+arguments.length+'_'+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments)

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

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

  }
   //------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(...args){
          return window['foo_'+args.length+'_'+Array.from(args).map((arg)=>typeof arg).join('_')](...args);
       }

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

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

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

Сразу хочу отметить, что нетипизированный не означает «нет типов».

hamdiakoguz 26.11.2016 18:08

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

function f(...rest){   // rest is an array
   console.info(rest.length);
   for (v of rest) if (typeof(v)= = "number")console.info(v);
}
f(1,2,3);  // 3 1 2 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 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 не предоставляет встроенной возможности перегрузки функций.

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

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

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

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

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

  • Внутри определения addMethodvar 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.info("I am the one with ZERO arguments supplied") });
addMethod(ninja,'whatever',function(a){ console.info("I am the one with ONE arguments supplied") });
addMethod(ninja,'whatever',function(a,b){ console.info("I am the one with TWO arguments supplied") });


ninja.whatever();
ninja.whatever(1,2);
ninja.whatever(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;
}

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

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

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

Это из более крупного кода, который включает функции проверки типов 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.info("default", arguments) })
    .add(function(){ console.info("noArgs", arguments) }, [], "noArgs")
    .add(function(){ console.info("str", arguments) }, [function(s){ return typeof s === 'string' }], "str")
;

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

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

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

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

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

Версия JSCompress.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}();

Я уже много лет использую эту функцию для уточнения своих перегрузок:

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

Теперь вы можете выполнять перегрузку функций в 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.info(mergedOpts.a);
  console.info(mergedOpts.b);
  console.info(mergedOpts.c);  
  console.info(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"

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

The Rest/Spread Properties for ECMAScript proposal (stage 4) adds spread properties to object literals. It copies own enumerable properties from a provided object onto a new object. More on mdn

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

в JS нет фактической перегрузки, в любом случае мы можем моделировать перегрузку метода несколькими способами:

способ №1:использовать объект

function test(x,options){
  if ("a" in options)doSomething();
  else if ("b" in options)doSomethingElse();
}
test("ok",{a:1});
test("ok",{b:"string"});

способ №2:использовать остальные (спред) параметры

function test(x,...p){
 if (p[2])console.info("3 params passed"); //or if (typeof p[2]= = "string")
else if (p[1])console.info("2 params passed");
else console.info("1 param passed");
}

способ №3:использовать undefined

function test(x, y, z){
 if (typeof(z)= = "undefined")doSomething();
}

способ №4:проверка типа

function test(x){
 if (typeof(x)= = "string")console.info("a string passed")
 else ...
}

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

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

Источник

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

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

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

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

sayHi('Frank', 'Willem');

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

function foo (a, ...b) {
  console.info(b);
}

foo(1,2,3,4);
foo(1,2);

Вы можете использовать этот метод

function func1(a, b, c) {
  console.info(arguments[0]);
  // expected output: 1

  console.info(arguments[1]);
  // expected output: 2

  console.info(arguments[2]);
  // expected output: 3
}

func1(1, 2, 3);

ссылка: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments

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

function transformer(
    firstNumber = 1,
    secondNumber = new Date().getFullYear(),
    transform = function multiply(firstNumber, secondNumber) {
        return firstNumber * secondNumber;
    }
) {
    return transform(firstNumber, secondNumber);
}

console.info(transformer());
console.info(transformer(8));
console.info(transformer(2, 6));
console.info(transformer(undefined, 65));

function add(firstNumber, secondNumber) {
    return firstNumber + secondNumber;
}
console.info(transformer(undefined, undefined, add));
console.info(transformer(3, undefined, add));

Результаты (за 2020 год):

2020
16160
12
65
2021
2023

Более подробная информация: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters

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

The answer is to supply multiple function types for the same function as a list of overloads. This list is what the compiler will use to resolve function calls. Let’s create a list of overloads that describe what our pickCard accepts and what it returns.

let suits = ["hearts", "spades", "clubs", "diamonds"];

function pickCard(x: { suit: string; card: number }[]): number;
function pickCard(x: number): { suit: string; card: number };
function pickCard(x: any): any {
  // Check to see if we're working with an object/array
  // if so, they gave us the deck and we'll pick the card
  if (typeof x == "object") {
    let pickedCard = Math.floor(Math.random() * x.length);
    return pickedCard;
  }
  // Otherwise just let them pick the card
  else if (typeof x == "number") {
    let pickedSuit = Math.floor(x / 13);
    return { suit: suits[pickedSuit], card: x % 13 };
  }
}

let myDeck = [
  { suit: "diamonds", card: 2 },
  { suit: "spades", card: 10 },
  { suit: "hearts", card: 4 },
];

let pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);

let pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);

With this change, the overloads now give us type checked calls to the pickCard function.

In order for the compiler to pick the correct type check, it follows a similar process to the underlying JavaScript. It looks at the overload list and, proceeding with the first overload, attempts to call the function with the provided parameters. If it finds a match, it picks this overload as the correct overload. For this reason, it’s customary to order overloads from most specific to least specific.

Note that the function pickCard(x): any piece is not part of the overload list, so it only has two overloads: one that takes an object and one that takes a number. Calling pickCard with any other parameter types would cause an error.

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