简体   繁体   中英

Method overloading in Javascript

I use method overloading as below in my Javascript code.

function somefunction()
{
    //1st function
}

function somefunction(a)
{
   //2nd function
}

function somefunction(a,b)
{
   //3rd function
}

somefunction(); // function call goes here

What I don't understand is if I call the somefunction() javascript should call the 1st function but the problem is javascript actually calls the 3rd function . Why is that? How can I call the 1st and 2nd function ? What is the reason for this? Is there a proper way to implement method overloading in Javascript? What's the industry standard?

JavaScript does not support method overloading (as in Java or similiar), your third function overwrites the previous declarations.

Instead, it supports variable arguments via the arguments object . You could do

function somefunction(a, b) {
    if (arguments.length == 0) { // a, b are undefined
        // 1st body
    } else if (arguments.length == 1) { // b is undefined
        // 2nd body
    } else if (arguments.length == 2) { // both have values
        // 3rd body
    } // else throw new SyntaxError?
}

You also could just check for typeof a == "undefined" etc, this would allow calling somefunction(undefined) , where arguments.length is 1 . This might allow easer calling with various parameters, eg when you have possibly-empty variables.

JS will pass undefined to any parameters which are not provided. If you want something like overloading, you'll need to do something similar to the code below:

function someFunction(a, b) {
    if (typeof a === 'undefined') {
        // Do the 0-parameter logic
    } else if (typeof b === 'undefined') {
        // Do the 1-parameter logic
    } else {
        // Do the 2-parameter logic
    }
}

You're just erasing the variable somefunction with each new declaration.

This is equivalent to

   window.somefunction = function(...
   window.somefunction = function(...
   window.somefunction = function(...

Javascript doesn't offer method overloading.

The proper way is either :

  • to define the third function and to test what parameters are defined
  • to pass only one object containing the parameters (which isn't really different but is cleaner)

You can't overload methods in JavaScript. In javascript, functions are stored in variables. Global variables are stored on the window object. You can only have one property per object with the same name (exclusive-key hash).

What you can do, is define the definition with the most parameters and check to see how many were passed in.

function Test(a, b, c)
{
    if(typeof a == 'undefined') 
    {
        a = 1;
    }

    if(typeof b == 'undefined') 
    {
        b = "hi";
    }

    if(typeof c == 'undefined') 
    {
        c = Date.Now;
    }
}

Now if I call Test(), it'll act as if I called Test(1, "hi", Date.Now)

There is no real function overloading in JavaScript since it allows to pass any number of parameters of any type. the best practice would be to make a function like: myfunc(opt)

{
// with opt = {'arg1':'a1','arg2':2, etc}, then check your opt inside of the function
}

I tried to develop an elegant solution to this problem described here . And you can find the demo here . The usage looks like this:

var out = def({
    'int': function(a) {
        alert('Here is int '+a);
    },

    'float': function(a) {
        alert('Here is float '+a);
    },

    'string': function(a) {
        alert('Here is string '+a);
    },

    'int,string': function(a, b) {
        alert('Here is an int '+a+' and a string '+b);
    },
    'default': function(obj) {
        alert('Here is some other value '+ obj);
    }

});

out('ten');
out(1);
out(2, 'robot');
out(2.5);
out(true);

The methods used to achieve this:

var def = function(functions, parent) {
 return function() {
    var types = [];
    var args = [];
    eachArg(arguments, function(i, elem) {
        args.push(elem);
        types.push(whatis(elem));
    });
    if(functions.hasOwnProperty(types.join())) {
        return functions[types.join()].apply(parent, args);
    } else {
        if (typeof functions === 'function')
            return functions.apply(parent, args);
        if (functions.hasOwnProperty('default'))
            return functions['default'].apply(parent, args);        
    }
  };
};

var eachArg = function(args, fn) {
 var i = 0;
 while (args.hasOwnProperty(i)) {
    if(fn !== undefined)
        fn(i, args[i]);
    i++;
 }
 return i-1;
};

var whatis = function(val) {

 if(val === undefined)
    return 'undefined';
 if(val === null)
    return 'null';

 var type = typeof val;

 if(type === 'object') {
    if(val.hasOwnProperty('length') && val.hasOwnProperty('push'))
        return 'array';
    if(val.hasOwnProperty('getDate') && val.hasOwnProperty('toLocaleTimeString'))
        return 'date';
    if(val.hasOwnProperty('toExponential'))
        type = 'number';
    if(val.hasOwnProperty('substring') && val.hasOwnProperty('length'))
        return 'string';
 }

 if(type === 'number') {
    if(val.toString().indexOf('.') > 0)
        return 'float';
    else
        return 'int';
 }

 return type;
};

What I don't understand is if I call the somefunction() javascript should call the 1st function but the problem is javascript actually calls the 3rd function.

That is expected behavior.

Why is that ?

The issue is that JavaScript does NOT natively support method overloading. So, if it sees/parses two or more functions with a same names it'll just consider the last defined function and overwrite the previous ones.

Why is that ? How can I call the 1st and 2nd function ? What is the reason for this?

One of the way I think is suitable for most of the case is follows -

Lets say you have method

function foo(x)
{
} 

Instead of overloading method which is not possible in javascript you can define a new method

fooNew(x,y,z)
{
}

and then modify the 1st function as follows -

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

If you have many such overloaded method consider using switch than just if-else statements.

Is there a proper way to do the method overloading ? What's the industry standard ?

There is no standard as such or an more proven method to do method overloading in javascript. One should do what best suits their programming design. I would say simple switch on arguments length with checking type that it is not undefined should suffice.

arguments object are used to create a method overloading like concept.

arguments is a special type of object that's only available inside the functions execution context.

arguments.length property is used to identify the number of parameters passed into the function.

You can make the better use of first class functions to create fake method overloading. The full concept is explained on my own website: Overload Functions in JavaScript

Method overloading is not directly supported in JavaScript. Here is an example how you can achieve something very much like it as shown below:

function overloadMethod(object, name, fn){

            if(!object._overload){
            object._overload = {};
            }

            if(!object._overload[name]){
            object._overload[name] = {};
            }

            if(!object._overload[name][fn.length]){
            object._overload[name][fn.length] = fn;
            }

              object[name] = function() {
                        if(this._overload[name][arguments.length])
                        return this._overload[name][arguments.length].apply(this, arguments);
              };
}

function Students(){
  overloadMethod(this, "find", function(){
            // Find a student by name
  });

  overloadMethod(this, "find", function(first, last){
            // Find a student by first and last name
  });

}

var students = new Students();
students.find(); // Finds all
students.find("Rahul"); // Finds students by name
students.find("Rahul", "Mhatre"); // Finds users by first and last name

source: source

If you use classes you create two different classes which have the same method with different parameters. Then you can use spread operator along with dependency injection to mimic the overloading behavior.

 class Foo{ method(){ console.log('hello from method foo') } } class Bar{ method(name){ console.log(`hello from method bar. Name is ${name}`) } } function MimicOverload(obj, options){ obj.method(...options) } const obj1 = new Foo() const obj2 = new Bar() MimicOverload(obj1,[]) MimicOverload(obj2,['overloaded'])

I tinkered with this and was able to make this work against custom classes without much trouble.

let overloadTest = overload(
    [String], function(value) {
        console.log('we got a string', value);
    },
    [Number], function(value) {
        console.log('we got a number', value);
    },
    [String, Number], function(s, n) {
        console.log('we got a string AND a number', s, n);
    }
    [MyCustomClass], function(value) {
        console.log('we got a MyCustomClass instance', value);
    }
);

With this overload implementation:

function overload(...overloads) {
    const f = function(...args) {
        let constructorArray = args.map(arg => arg.constructor);
        let implIndex = f.overloads.findIndex(sig => {
            return constructorArray.length === sig.length &&
                constructorArray.every((o,i) => o === sig[i])
            ;
        }) + 1;
        if (implIndex > 0 && typeof(f.overloads[implIndex]) === 'function') {
            return f.overloads[implIndex].apply({}, args);
        } else {
            const message = "There is no implementation that matches the provided arguments.";
            console.error(message, constructorArray);
            throw Error(message);
        }
    };
    f.overloads = overloads;
    return f;
};

This doesn't work on instance methods just yet. But, it could be extended.

Also, the arguments list refers directly to constructors (as opposed to a strings), which means you could extend with additional validation if you like -- for example, ensuring that each argument type is actually a function/constructor at overload() time. You could also conceivably create a version of overload() that performs DI.

I think that can't be considered method overloading, but it's a alternative to use one function to diferent combinations of parameters. The function's parameter need to be an object.

function something(obj){
   if(obj.a != undefined){
      return a*a;
   }
   if((obj.b != undefined)&&(obj.c != undefined)){
      return b*c;
   }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM