Есть несколько аспектов перегрузки аргументов в Javascript:
Переменные аргументы - вы можете передавать разные наборы аргументов (как по типу, так и по количеству), и функция будет вести себя в соответствии с переданными ей аргументами.
Аргументы по умолчанию - вы можете определить значение по умолчанию для аргумента, если оно не передано.
Именованные аргументы - порядок аргументов становится неактуальным, и вы просто называете аргументы, которые хотите передать функции.
Ниже приводится раздел по каждой из этих категорий обработки аргументов.
Переменные аргументы
Поскольку в javascript нет проверки типа аргументов или необходимого количества аргументов, вы можете иметь только одну реализацию, myFunc()которая может адаптироваться к тому, какие аргументы были переданы ему, путем проверки типа, наличия или количества аргументов.
jQuery делает это постоянно. Вы можете сделать некоторые из аргументов необязательными или вы можете выполнить ветвление в своей функции в зависимости от того, какие аргументы ей переданы.
При реализации этих типов перегрузок вы можете использовать несколько различных методов:
- Вы можете проверить наличие любого заданного аргумента, проверив, равно ли объявленное значение имени аргумента
undefined.
- Вы можете проверить общее количество или аргументы с помощью
arguments.length.
- Вы можете проверить тип любого заданного аргумента.
- Для переменного числа аргументов вы можете использовать
argumentsпсевдо-массив для доступа к любому заданному аргументу с помощью arguments[i].
Вот некоторые примеры:
Давайте посмотрим на obj.data()метод jQuery . Он поддерживает четыре различных формы использования:
obj.data("key");
obj.data("key", value);
obj.data();
obj.data(object);
Каждый запускает различное поведение, и без использования этой динамической формы перегрузки потребовалось бы четыре отдельные функции.
Вот как можно различить все эти варианты на английском языке, а затем я объединю их все в коде:
// get the data element associated with a particular key value
obj.data("key");
Если первый аргумент, переданный в, .data()является строкой, а второй аргумент - это undefined, то вызывающий должен использовать эту форму.
// set the value associated with a particular key
obj.data("key", value);
Если второй аргумент не является неопределенным, установите значение определенного ключа.
// get all keys/values
obj.data();
Если аргументы не переданы, вернуть все ключи / значения в возвращаемом объекте.
// set all keys/values from the passed in object
obj.data(object);
Если тип первого аргумента - простой объект, установите все ключи / значения из этого объекта.
Вот как вы могли бы объединить все это в один набор логики javascript:
// method declaration for .data()
data: function(key, value) {
if (arguments.length === 0) {
// .data()
// no args passed, return all keys/values in an object
} else if (typeof key === "string") {
// first arg is a string, look at type of second arg
if (typeof value !== "undefined") {
// .data("key", value)
// set the value for a particular key
} else {
// .data("key")
// retrieve a value for a key
}
} else if (typeof key === "object") {
// .data(object)
// set all key/value pairs from this object
} else {
// unsupported arguments passed
}
},
Ключ к этой технике - убедиться, что все формы аргументов, которые вы хотите принять, однозначно идентифицируемы, и никогда не возникает путаницы в том, какую форму использует вызывающий. Как правило, это требует соответствующего упорядочивания аргументов и обеспечения достаточной уникальности типа и положения аргументов, чтобы вы всегда могли определить, какая форма используется.
Например, если у вас есть функция, которая принимает три строковых аргумента:
obj.query("firstArg", "secondArg", "thirdArg");
Вы можете легко сделать третий аргумент необязательным, и вы можете легко обнаружить это условие, но вы не можете сделать только второй аргумент необязательным, потому что вы не можете сказать, какой из них вызывающий имеет намерение передать, потому что нет способа определить, является ли второй аргумент Аргумент должен быть вторым аргументом, или второй аргумент был опущен, поэтому то, что находится в месте второго аргумента, на самом деле является третьим аргументом:
obj.query("firstArg", "secondArg");
obj.query("firstArg", "thirdArg");
Поскольку все три аргумента относятся к одному типу, вы не можете отличить разные аргументы, поэтому не знаете, что имел в виду вызывающий. При таком стиле вызова только третий аргумент может быть необязательным. Если вы хотите опустить второй аргумент, его необходимо передать как null(или какое-либо другое обнаруживаемое значение), и ваш код обнаружит это:
obj.query("firstArg", null, "thirdArg");
Вот пример необязательных аргументов jQuery. оба аргумента являются необязательными и принимают значения по умолчанию, если не переданы:
clone: function( dataAndEvents, deepDataAndEvents ) {
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
return this.map( function () {
return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
});
},
Вот пример jQuery, где аргумент может отсутствовать или один из трех разных типов, что дает вам четыре разных перегрузки:
html: function( value ) {
if ( value === undefined ) {
return this[0] && this[0].nodeType === 1 ?
this[0].innerHTML.replace(rinlinejQuery, "") :
null;
// See if we can take a shortcut and just use innerHTML
} else if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
(jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
!wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
value = value.replace(rxhtmlTag, "<$1></$2>");
try {
for ( var i = 0, l = this.length; i < l; i++ ) {
// Remove element nodes and prevent memory leaks
if ( this[i].nodeType === 1 ) {
jQuery.cleanData( this[i].getElementsByTagName("*") );
this[i].innerHTML = value;
}
}
// If using innerHTML throws an exception, use the fallback method
} catch(e) {
this.empty().append( value );
}
} else if ( jQuery.isFunction( value ) ) {
this.each(function(i){
var self = jQuery( this );
self.html( value.call(this, i, self.html()) );
});
} else {
this.empty().append( value );
}
return this;
},
Именованные аргументы
Другие языки (например, Python) позволяют передавать именованные аргументы как средство передачи только некоторых аргументов и сделать аргументы независимыми от порядка, в котором они передаются. Javascript не поддерживает напрямую функцию именованных аргументов. Вместо него обычно используется шаблон проектирования, который передает карту свойств / значений. Это можно сделать, передав объект со свойствами и значениями, или в ES6 и выше вы можете передать сам объект Map.
Вот простой пример ES5:
jQuery $.ajax()принимает форму использования, в которой вы просто передаете ему один параметр, который является обычным объектом Javascript со свойствами и значениями. Какие свойства вы ему передаете, определяют, какие аргументы / параметры передаются в вызов ajax. Некоторые могут потребоваться, многие - по желанию. Поскольку они являются свойствами объекта, особого порядка нет. Фактически, этому объекту можно передать более 30 различных свойств, требуется только одно (URL-адрес).
Вот пример:
$.ajax({url: "http://www.example.com/somepath", data: myArgs, dataType: "json"}).then(function(result) {
// process result here
});
$.ajax()Затем внутри реализации он может просто запросить, какие свойства были переданы входящему объекту, и использовать их в качестве именованных аргументов. Это можно сделать либо с помощью, for (prop in obj)либо путем передачи всех свойств в массив с помощью Object.keys(obj)и затем повторения этого массива.
Этот метод очень часто используется в Javascript, когда есть большое количество аргументов и / или многие аргументы являются необязательными. Примечание: это накладывает бремя на реализующую функцию, чтобы убедиться, что присутствует минимальный допустимый набор аргументов, и дать вызывающей стороне некоторую обратную связь отладки, которая отсутствует, если передано недостаточное количество аргументов (возможно, путем выдачи исключения с полезным сообщением об ошибке) .
В среде ES6 можно использовать деструктурирование для создания свойств / значений по умолчанию для переданного выше объекта. Более подробно это обсуждается в этой справочной статье .
Вот один пример из той статьи:
function selectEntries({ start=0, end=-1, step=1 } = {}) {
···
};
Это создает свойства и значения по умолчанию для start, endи stepсвойства на объект передается в selectEntries()функцию.
Значения по умолчанию для аргументов функции
В ES6 Javascript добавляет встроенную языковую поддержку для значений аргументов по умолчанию.
Например:
function multiply(a, b = 1) {
return a*b;
}
multiply(5); // 5
Дальнейшее описание способов использования здесь, в MDN .