termine - Función de sobrecarga en Javascript-Mejores prácticas
llamar funcion javascript html sin evento (30)
Si necesito una función con dos usos foo (x) y foo (x, y, z), ¿cuál es la mejor / la mejor opción?
El problema es que JavaScript NO admite de forma nativa la sobrecarga de métodos. Entonces, si ve / analiza dos o más funciones con los mismos nombres, solo considerará la última función definida y sobrescribirá las anteriores.
Una de las formas que creo que es adecuada para la mayoría de los casos es la siguiente:
Digamos que tienes método
function foo(x)
{
}
En lugar de un método de sobrecarga que no es posible en javascript , puede definir un nuevo método
fooNew(x,y,z)
{
}
y luego modifique la primera función de la siguiente manera:
function foo(arguments)
{
if(arguments.length==2)
{
return fooNew(arguments[0], arguments[1]);
}
}
Si tiene muchos de estos métodos sobrecargados, considere usar el switch
que solo if-else
instrucciones if-else
.
( más detalles )
¿Cuál es la mejor manera de falsificar la sobrecarga de funciones en Javascript?
Sé que no es posible sobrecargar funciones en Javascript como en otros idiomas. Si necesitara una función con dos usos foo(x)
y foo(x,y,z)
que es la mejor / la mejor forma de elegir:
- Usando nombres diferentes en primer lugar
- Usando argumentos opcionales como
y = y || ''default''
y = y || ''default''
- Utilizando numero de argumentos
- Comprobando tipos de argumentos
- ¿O como?
Patrón de reenvío => la mejor práctica en la sobrecarga de JS
Reenvíe a otra función cuyo nombre se construye desde los puntos 3 y 4:
- Utilizando numero de argumentos
- Comprobando tipos de argumentos
window[''foo_''+arguments.length+''_''+Array.from(arguments).map((arg)=>typeof arg).join(''_'')](...arguments)
Solicitud en su caso:
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(){
}
Otra muestra compleja:
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 ....
Función de sobrecarga a través de Polimorfismo Dinámico en 100 líneas de JS
- VanillaJS, sin dependencias externas.
- Compatibilidad completa con el navegador: Array.prototype.slice , Object.prototype.toString
- 1114 bytes uglify''d / 744 bytes g-zipeados
Esto proviene de un cuerpo de código más amplio que incluye las funciones de verificación de tipo isFn
, isArr
, etc. La versión de VanillaJS a continuación ha sido rediseñada para eliminar todas las dependencias externas, sin embargo, tendrá que definir sus propias funciones de verificación de tipos para usar en las llamadas .add()
.
Nota: esta es una función de ejecución automática (por lo que podemos tener un cierre / ámbito cerrado), por lo tanto, la asignación a window.overload
lugar de la 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;
}();
Uso:
La persona que llama define sus funciones sobrecargadas asignando una variable al retorno de overload()
. Gracias al encadenamiento, las sobrecargas adicionales se pueden definir en serie:
var myOverloadedFn = overload(function(){ console.log("default", arguments) })
.add(function(){ console.log("noArgs", arguments) }, [], "noArgs")
.add(function(){ console.log("str", arguments) }, [function(s){ return typeof s === ''string'' }], "str")
;
El único argumento opcional para overload()
define la función "predeterminada" para llamar si no se puede identificar la firma. Los argumentos para .add()
son:
-
fn
:function
define la sobrecarga; -
a_vArgumentTests
:a_vArgumentTests
defunction
definen las pruebas que se ejecutarán en losarguments
. Cadafunction
acepta un solo argumento y devuelve tutrue
función de si el argumento es válido; -
sAlias
(Opcional):string
define el alias para acceder directamente a la función de sobrecarga (fn
), por ejemplo,myOverloadedFn.noArgs()
llamará a esa función directamente, evitando las pruebas de polimorfismo dinámico de los argumentos.
Esta implementación en realidad permite más que solo sobrecargas de funciones tradicionales, ya que el segundo argumento .add()
para .add()
en la práctica define tipos personalizados. Por lo tanto, podría generar argumentos no solo basados en el tipo, sino también en rangos, valores o colecciones de valores.
Si observa la overload()
las 145 líneas de código overload()
verá que cada firma se clasifica por el número de arguments
pasan. Esto se hace para limitar el número de pruebas que estamos ejecutando. También hago un seguimiento de un recuento de llamadas. Con algo de código adicional, las matrices de funciones sobrecargadas se podrían reordenar de modo que primero se prueben las funciones más comúnmente llamadas, agregando nuevamente alguna medida de mejora de rendimiento.
Ahora, hay algunas advertencias ... A medida que Javascript se escribe con holgura, tendrá que tener cuidado con su, vArgumentTests
ya que integer
podría validarse como a float
, etc.
Versión de JSCompress.com (1114 bytes, 744 bytes 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}();
A menudo hago esto:
DO#:
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
Equivalente de 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
Este ejemplo en particular es en realidad más elegante en javascript que en C #. Los parámetros que no se especifican son ''indefinidos'' en javascript, que se evalúa como falso en una sentencia if. Sin embargo, la definición de la función no transmite la información de que p2 y p3 son opcionales. Si necesita mucha sobrecarga, jQuery ha decidido usar un objeto como parámetro, por ejemplo, jQuery.ajax (opciones). Estoy de acuerdo con ellos en que este es el enfoque más poderoso y claramente documentable para la sobrecarga, pero rara vez necesito más de uno o dos parámetros opcionales rápidos.
EDITAR: cambio de prueba de IF por sugerencia de Ian
Acabo de intentar esto, tal vez se adapte a sus necesidades. Dependiendo del número de argumentos, puede acceder a una función diferente. Lo inicializas la primera vez que lo llamas. Y el mapa de funciones está oculto en el cierre.
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;
};
Pruébalo.
TEST.multiFn("0")
.multiFn()
.multiFn("0","1","2");
Aquí hay una prueba de referencia sobre la sobrecarga de funciones: http://goo.gl/UyYAD (código que se muestra en esta publicación). Muestra que la sobrecarga de funciones (teniendo en cuenta los tipos) puede ser aproximadamente 13 veces más lenta en el V8 de Google Chrome a partir del 16.0 (beta) .
Además de pasar un objeto (es decir, {x: 0, y: 0}
), también se puede tomar el enfoque de C cuando sea apropiado, nombrando los métodos en consecuencia. Por ejemplo, Vector.AddVector (vector), Vector.AddIntegers (x, y, z, ...) y Vector.AddArray (integerArray). Puedes mirar las bibliotecas de C, como OpenGL para nombrar la inspiración.
Edición : arg.hasOwnProperty(''param'')
un punto de referencia para pasar un objeto y probar el objeto usando ''param'' in arg
y arg.hasOwnProperty(''param'')
, y la sobrecarga de funciones es mucho más rápida que pasar un objeto y verificar las propiedades (en esta referencia al menos).
Desde la perspectiva del diseño, la sobrecarga de funciones solo es válida o lógica si los parámetros sobrecargados corresponden a la misma acción. Por lo tanto, es lógico pensar que debería haber un método subyacente que solo esté relacionado con detalles específicos, de lo contrario puede indicar elecciones de diseño inapropiadas. Por lo tanto, también se puede resolver el uso de la sobrecarga de funciones al convertir los datos en un objeto respectivo. Por supuesto, uno debe considerar el alcance del problema, ya que no hay necesidad de hacer diseños elaborados si su intención es simplemente imprimir un nombre, pero para el diseño de marcos y bibliotecas, tal pensamiento está justificado.
Mi ejemplo proviene de una implementación de Rectángulo, de ahí la mención de Dimensión y Punto. Tal vez Rectangle podría agregar un método GetRectangle()
al prototipo de Dimension
y Point
, y luego se resuelve el problema de sobrecarga de funciones. ¿Y qué pasa con los primitivos? Bueno, tenemos la longitud del argumento, que ahora es una prueba válida ya que los objetos tienen un método 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);
Como esta publicación ya contiene muchas soluciones diferentes, pensé que publicaría otra.
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");
}
Esto se puede utilizar como se muestra a continuación:
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));
Esta solución no es perfecta, pero solo quiero demostrar cómo se podría hacer.
Dado que JavaScript no tiene opciones de función, el objeto de sobrecarga puede usarse en su lugar. Si hay uno o dos argumentos requeridos, es mejor mantenerlos separados del objeto de opciones. Este es un ejemplo de cómo usar las opciones objeto y los valores poblados al valor predeterminado en caso de que el valor no se haya pasado en el objeto opciones.
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 + "}";
}
here hay un ejemplo de cómo usar las opciones de objetos.
Hay dos maneras en que puedes enfocar esto mejor:
Pase un diccionario (matriz asociativa) si desea dejar mucha flexibilidad
Tome un objeto como argumento y use la herencia basada en prototipos para agregar flexibilidad.
La mejor manera de realizar la sobrecarga de funciones con parámetros es no verificar la longitud del argumento o los tipos; la comprobación de los tipos solo hará que su código sea lento y usted se divierte con Arrays, nulls, Objects, etc.
Lo que la mayoría de los desarrolladores hacen es agregar un objeto como último argumento a sus métodos. Este objeto puede contener cualquier cosa.
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"});
Entonces puedes manejarlo como quieras en tu método. [Cambiar, si-si no, etc.]
La mejor manera realmente depende de la función y los argumentos. Cada una de tus opciones es una buena idea en diferentes situaciones. Generalmente intento estos en el siguiente orden hasta que uno de ellos funciona:
Usando argumentos opcionales como y = y || ''defecto''. Esto es conveniente si puede hacerlo, pero puede que no siempre funcione en la práctica, por ejemplo, cuando 0 / null / undefined sería un argumento válido.
Utilizando número de argumentos. Similar a la última opción, pero puede funcionar cuando # 1 no funciona.
Comprobando los tipos de argumentos. Esto puede funcionar en algunos casos donde el número de argumentos es el mismo. Si no puede determinar los tipos de manera confiable, es posible que deba usar nombres diferentes.
Usando nombres diferentes en primer lugar. Es posible que deba hacer esto si las otras opciones no funcionan, no son prácticas o por coherencia con otras funciones relacionadas.
La respuesta correcta es que NO HAY SOBRECARGAS EN JAVASCRIPT.
La comprobación / conmutación dentro de la función no está sobrecargada.
El concepto de sobrecarga: en algunos lenguajes de programación, la sobrecarga de funciones o la sobrecarga de métodos es la capacidad de crear múltiples métodos del mismo nombre con diferentes implementaciones. Las llamadas a una función sobrecargada ejecutarán una implementación específica de esa función apropiada para el contexto de la llamada, permitiendo que una llamada de función realice diferentes tareas dependiendo del contexto.
Por ejemplo, doTask () y doTask (objeto O) son métodos sobrecargados. Para llamar a este último, se debe pasar un objeto como parámetro, mientras que el primero no requiere un parámetro, y se llama con un campo de parámetro vacío. Un error común sería asignar un valor predeterminado al objeto en el segundo método, lo que daría lugar a un error de llamada ambiguo, ya que el compilador no sabría cuál de los dos métodos utilizar.
https://en.wikipedia.org/wiki/Function_overloading
Todas las implementaciones sugeridas son excelentes, pero la verdad es que no hay una implementación nativa para JavaScript.
No estoy seguro acerca de las mejores prácticas, pero aquí es cómo lo hago:
/*
* 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"
No hay manera de funcionar con sobrecarga en javascript. Por lo tanto, recomiendo como siguiente el método de typeof()
lugar de la función múltiple para falsificar la sobrecarga.
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!!");
}
}
¡Buena suerte!
No hay una sobrecarga real de funciones en JavaScript, ya que permite pasar cualquier número de parámetros de cualquier tipo. Debe verificar dentro de la función cuántos arguments se han pasado y qué tipo son.
Otra forma de abordar esto es mediante el uso de la variable especial: argumentos , esta es una implementación:
function sum() {
var x = 0;
for (var i = 0; i < arguments.length; ++i) {
x += arguments[i];
}
return x;
}
para que pueda modificar este código a:
function sum(){
var s = 0;
if (typeof arguments[0] !== "undefined") s += arguments[0];
.
.
.
return s;
}
Puede utilizar el ''addMethod'' de John Resig. Con este método puede "sobrecargar" los métodos basados en el conteo de argumentos.
// 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 );
};
}
También he creado una alternativa a este método que utiliza el almacenamiento en caché para mantener las variaciones de la función. Las diferencias se describen aquí.
// 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);
};
}
mira esto. Esta muy padre. ejohn.org/blog/javascript-method-overloading Trick Javascript para permitirte hacer llamadas como esta:
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
INTRODUCCIÓN
Hasta ahora, leer tantas respuestas le daría dolor de cabeza a cualquiera. Cualquier persona que intente conocer el concepto deberá conocer los siguientes requisitos previos .
Function overloading Definition
, Function Length property
Function argument property
, Function argument property
Function overloading
en su forma más simple significa que una función realiza diferentes tareas en función del número de argumentos que se le pasan. Cabe destacar que TASK1, TASK2 y TASK3 se resaltan a continuación y se realizan en base al número de arguments
se pasan a la misma función 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
NOTA : JS no proporciona la capacidad incorporada de sobrecarga de funciones.
Alternativa
John E Resig (creador de JS) ha señalado una alternativa que utiliza los requisitos previos anteriores para lograr la capacidad de implementar la sobrecarga de funciones.
El código a continuación utiliza un enfoque directo pero ingenuo al usar if-else
o switch
.
- evalúa la propiedad de
argument-length
. - Diferentes valores resultan en la invocación de diferentes funciones.
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 ...
}
}
}
Otra técnica es mucho más limpia y dinámica. El punto culminante de esta técnica es la función genérica addMethod
.
definimos una función
addMethod
que se usa para agregar diferentes funciones a un objeto con el mismo nombre pero con diferentes funcionalidades .debajo de la función
addMethod
acepta el objeto de tres params nombre deobject
, nombre de la funciónname
y la función que queremos invocarfn
.- Dentro de
addMethod
definitionvar old
almacena la referencia a lafunction
anteriorfunction
se almacena con la ayuda de un cierre: una burbuja protectora.
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);
};
};
- Utilice el depurador para entender el flujo de código.
- debajo de
addMethod
agrega tres funciones que cuando se invoca usandoninja.whatever(x)
con el número de argumentosx
que puede ser cualquier cosa, es decir, en blanco o una o más de una invoca diferentes funciones según se define mientras se usa la funciónaddMethod
.
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);
Así que realmente me gustó esta forma de hacer las cosas que encontré en los secretos del ninja 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);
}
}
}
luego utiliza addMethod para agregar funciones sobrecargadas a cualquier objeto. La principal confusión en este código para mí fue el uso de fn.length == argumentos.length - esto funciona porque fn.length es el número de parámetros esperados, mientras que los argumentos.length es el número de parámetros que realmente se llaman con el función. La razón por la que la función anónima no tiene argumento es porque puede pasar cualquier número de argumentos en javascript y el lenguaje es indulgente.
Me gustó esto porque puede usarlo en cualquier lugar, solo cree esta función y simplemente use el método en la base de código que desee.
También evita tener una instrucción if / switch ridículamente grande, que se vuelve difícil de leer si empiezas a escribir código complejo (la respuesta aceptada resultará en esto).
En términos de contras, supongo que el código es inicialmente un poco oscuro ... ¿pero no estoy seguro de los demás?
Estoy trabajando en una biblioteca que proporciona capacidades de código de clase a Javascript, actualmente admite constructores, herencia, sobrecarga de métodos por número de parámetros y por tipos de parámetros, mixins, propiedades estáticas y singleton.
Vea un ejemplo de sobrecarga de métodos usando esa biblioteca:
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
Cualquier comentario, corrección de errores, documentos y mejoras de pruebas son bienvenidos!
He estado usando esta función para pretender mis sobrecargas durante años:
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);
}
}
Demostrado:
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);
Echa un vistazo al off
método de jQuery :
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 );
} );
}
Muchas funciones sobrecargadas cuando están optimizadas para el rendimiento son casi ilegibles. Tienes que descifrar el jefe de la función. Esto es quizás más rápido que usar una overload
función como la que proporciono; sin embargo, es más lento desde la perspectiva humana con respecto a la identificación de la sobrecarga que se llamó.
Me gustaría compartir un ejemplo útil de enfoque similar a una sobrecarga.
function Clear(control)
{
var o = typeof control !== "undefined" ? control : document.body;
var children = o.childNodes;
while (o.childNodes.length > 0)
o.removeChild(o.firstChild);
}
Uso: Borrar (); // Borra todo el documento
Borrar (myDiv); // Borra el panel referenciado por myDiv
A partir de julio de 2017, la técnica común ha sido la siguiente. Tenga en cuenta que también podemos realizar la verificación de tipos dentro de la función.
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
Esta es una pregunta antigua pero creo que necesita otra entrada (aunque dudo que alguien la lea). El uso de Expresiones de función inmediatamente invocadas (IIFE) se puede utilizar junto con cierres y funciones en línea para permitir la sobrecarga de funciones. Considere el siguiente ejemplo (artificial):
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
En resumen, el uso del IIFE crea un alcance local, lo que nos permite definir la variable privada old
para almacenar una referencia a la definición inicial de la función foo
. Esta función devuelve una función en línea newFoo
que registra el contenido de ambos dos argumentos si se pasa exactamente dos argumentos a
y b
o llama a la old
función si arguments.length !== 2
. Este patrón se puede repetir cualquier cantidad de veces para dotar a una variable con varias definiciones funcionales diferentes.
Hicimos over.js para resolver este problema de una manera muy elegante. Tu puedes hacer:
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 es un lenguaje sin tipo, y solo creo que tiene sentido sobrecargar un método / función con respecto al número de parámetros. Por lo tanto, recomendaría verificar si el parámetro ha sido definido:
myFunction = function(a, b, c) {
if (b === undefined && c === undefined ){
// do x...
}
else {
// do y...
}
};
La primera opción realmente merece atención porque es lo que surgió en una configuración de código bastante compleja. Entonces, mi respuesta es
- Usando nombres diferentes en primer lugar
Con una pequeña pero esencial sugerencia, los nombres deben verse diferentes para la computadora, pero no para ti. Nombre funciones sobrecargadas como: func, func1, func2.
Me gusta el enfoque de @ AntouanK. A menudo me encuentro ofreciendo una función con diferentes números o parámetros y diferentes tipos. A veces no siguen una orden. Yo uso para mapear mirando los tipos de parámetros:
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);
}
Para su caso de uso, así es como lo abordaría ES6
(ya que es el final de 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;
}
Obviamente, puede adaptar esto para trabajar con cualquier cantidad de parámetros y simplemente cambiar sus declaraciones condicionales en consecuencia.