Javascript中的函数重载-最佳做法

JavaScript伪造函数重载的最佳方法是什么?

我知道不可能像其他语言一样重载Javascript中的函数。 如果我需要两个函数一起使用foo(x)foo(x,y,z) ,这是最佳/首选方式:

  1. 首先使用不同的名称
  2. 使用可选参数,例如y = y || 'default' y = y || 'default'
  3. 使用参数数量
  4. 检查参数类型
  5. 或如何?

#1楼

我刚刚尝试过,也许它适合您的需求。 根据参数的数量,您可以访问其他函数。 首次调用时将其初始化。 并且功能图隐藏在闭包中。

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

测试一下。

TEST.multiFn("0")
    .multiFn()
    .multiFn("0","1","2");

#2楼

第一个选项确实值得关注,因为这是我在非常复杂的代码设置中遇到的问题。 所以,我的答案是

  1. 首先使用不同的名称

有一点但必不可少的提示,对于计算机,名称应该看起来有所不同,但对于您来说,名称应该没有。 重载函数的名称,例如:func,func1,func2。


#3楼

我们制作了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);
    }
  )

};

#4楼

由于JavaScript没有函数重载选项,因此可以使用对象。 如果有一个或两个必需的参数,最好将它们与options对象分开。 这是一个有关如何使用选项对象和填充值作为默认值(如果未在选项对象中传递值的情况下)的示例。

    function optionsObjectTest(x, y, opts) {
        opts = opts || {}; // default to an empty options object

        var stringValue = opts.stringValue || "string default value";
        var boolValue = !!opts.boolValue; // coerces value to boolean with a double negation pattern
        var numericValue = opts.numericValue === undefined ? 123 : opts.numericValue;

        return "{x:" + x + ", y:" + y + ", stringValue:'" + stringValue + "', boolValue:" + boolValue + ", numericValue:" + numericValue + "}";

}

是有关如何使用选项对象的示例


#5楼

所以我真的很喜欢这种在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语句,如果您开始编写复杂的代码,该语句将变得难以阅读(接受的答案将导致这种情况)。

就缺点而言,我认为代码最初有点晦涩难懂...但是我不确定其他代码吗?


#6楼

由于这篇文章已经包含了许多不同的解决方案,所以我想我再发表一个。

function onlyUnique(value, index, self) {
    return self.indexOf(value) === index;
}

function overload() {
   var functions = arguments;
   var nroffunctionsarguments = [arguments.length];
    for (var i = 0; i < arguments.length; i++) {
        nroffunctionsarguments[i] = arguments[i].length;
    }
    var unique = nroffunctionsarguments.filter(onlyUnique);
    if (unique.length === arguments.length) {
        return function () {
            var indexoffunction = nroffunctionsarguments.indexOf(arguments.length);
            return functions[indexoffunction].apply(this, arguments);
        }
    }
    else throw new TypeError("There are multiple functions with the same number of parameters");

}

可以如下所示使用:

var createVector = overload(
        function (length) {
            return { x: length / 1.414, y: length / 1.414 };
        },
        function (a, b) {
            return { x: a, y: b };
        },
        function (a, b,c) {
            return { x: a, y: b, z:c};
        }
    );
console.log(createVector(3, 4));
console.log(createVector(3, 4,5));
console.log(createVector(7.07));

这个解决方案不是完美的,但我只想演示如何完成。


#7楼

这是一个古老的问题,但我认为还需要另一个条目(尽管我怀疑有人会读它)。 立即调用函数表达式(IIFE)的使用可以与闭包和内联函数结合使用,以允许函数重载。 考虑以下(人为)示例:

var foo;

// original 'foo' definition
foo = function(a) {
  console.log("a: " + a);
}

// define 'foo' to accept two arguments
foo = (function() {
  // store a reference to the previous definition of 'foo'
  var old = foo;

  // use inline function so that you can refer to it internally
  return function newFoo(a,b) {

    // check that the arguments.length == the number of arguments 
    // defined for 'newFoo'
    if (arguments.length == newFoo.length) {
      console.log("a: " + a);
      console.log("b: " + b);

    // else if 'old' is a function, apply it to the arguments
    } else if (({}).toString.call(old) === '[object Function]') {
      old.apply(null, arguments);
    }
  }
})();

foo(1);
> a: 1
foo(1,2);
> a: 1
> b: 2
foo(1,2,3)
> a: 1

简而言之,IIFE的使用创建了局部作用域,使我们能够定义私有变量old来存储对函数foo的初始定义的引用。 然后,此函数返回一个内联函数newFoo ,如果正好传递了两个参数ab ,则记录两个参数的内容;如果arguments.length !== 2则调用old函数。 这种模式可以重复多次,以赋予一个变量几种不同的功能定义。


#8楼

您可以使用John Resig的“ 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);
  };
}

#9楼

如果我需要两个函数一起使用foo(x)和foo(x,y,z),那是最好的/首选的方式?

问题是JavaScript本身不支持方法重载。 因此,如果它看到/解析了两个或多个具有相同名称的函数,则只需考虑最后定义的函数并覆盖先前的函数。

我认为适合大多数情况的一种方法如下:

可以说你有方法

function foo(x)
{
} 

代替在javascript中不可能的重载方法您可以定义一个新方法

fooNew(x,y,z)
{
}

然后按如下所示修改第一个功能-

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

如果您有许多这样的重载方法,请考虑使用switch不是if-else语句。

更多详细信息

PS:上面的链接转到我的个人博客,其中包含其他详细信息。


#10楼

我想分享一个类似重载方法的有用示例。

function Clear(control)
{
  var o = typeof control !== "undefined" ? control : document.body;
  var children = o.childNodes;
  while (o.childNodes.length > 0)
    o.removeChild(o.firstChild);
}

用法:Clear(); //清除所有文件

清除(myDiv); //清除myDiv引用的面板


#11楼

我不确定最佳做法,但是这是我的做法:

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

#12楼

我喜欢@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);
}

#13楼

正确的答案是JAVASCRIPT中没有超载。

在功能内检查/切换不是过载。

重载的概念:在某些编程语言中,函数重载或方法重载是创建具有不同实现的相同名称的多个方法的能力。 对重载函数的调用将针对该调用的上下文运行该函数的特定实现,从而允许一个函数调用根据上下文执行不同的任务。

例如,doTask()和doTask(object O)是重载方法。 要调用后者,必须将一个对象作为参数传递,而前者不需要参数,而是使用空的参数字段进行调用。 一个常见的错误是在第二种方法中为对象分配默认值,这将导致模棱两可的调用错误,因为编译器不知道要使用这两种方法中的哪一种。

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

建议的所有实现都是不错的选择,但实际上,JavaScript没有本地实现。


#14楼

转发模式=> JS重载的最佳实践

转发到另一个从第三点和第四点开始构建的函数:

  1. 使用参数数量
  2. 检查参数类型
window['foo_'+arguments.length+'_'+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments)

适用于您的案例:

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

  }
   //------Assuming that `x` , `y` and `z` are String when calling `foo` . 

  /**-- for :  foo(x)*/
  function foo_1_string(){
  }
  /**-- for : foo(x,y,z) ---*/
  function foo_3_string_string_string(){

  }

其他复杂样本:

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

        /** one argument & this argument is string */
      function foo_1_string(){

      }
       //------------
       /** one argument & this argument is object */
      function foo_1_object(){

      }
      //----------
      /** two arguments & those arguments are both string */
      function foo_2_string_string(){

      }
       //--------
      /** Three arguments & those arguments are : id(number),name(string), callback(function) */
      function foo_3_number_string_function(){
                let args=arguments;
                  new Person(args[0],args[1]).onReady(args[3]);
      }

       //--- And so on ....   

#15楼

JavaScript是非类型化语言,我只认为重载方法/函数的参数数量是有意义的。 因此,我建议检查是否已定义参数:

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

#16楼

截至2017年7月,以下是常用技术。 请注意,我们还可以在函数中执行类型检查。

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

#17楼

JavaScript中没有实际的函数重载,因为它允许传递任意数量的任何类型的参数。 您必须在函数内部检查已传递了多少个参数以及它们是什么类型。


#18楼

有两种方法可以更好地解决此问题:

  1. 如果您想保留很大的灵活性,请传递字典(关联数组)

  2. 以一个对象作为参数,并使用基于原型的继承来增加灵活性。


#19楼

最好的方法实际上取决于函数和参数。 在不同情况下,每个选项都是一个好主意。 我通常按​​以下顺序尝试这些方法,直到其中之一起作用为止:

  1. 使用可选参数,例如y = y || '默认'。 如果可以的话,这样做很方便,但是可能并不总是可行,例如,当0 / null / undefined是有效参数时。

  2. 使用参数数量。 与最后一个选项类似,但在#1不起作用时可能起作用。

  3. 检查参数类型。 在某些情况下,如果参数数量相同,则可以使用此方法。 如果无法可靠地确定类型,则可能需要使用其他名称。

  4. 首先使用不同的名称。 如果其他选项不起作用,不实用或与其他相关功能保持一致,则可能需要执行此操作。


#20楼

使用参数进行函数重载的最佳方法是不检查参数长度或类型; 检查类型只会使您的代码变慢,并且可以享受数组,空值,对象等的乐趣。

大多数开发人员所做的就是将对象作为其方法的最后一个参数。 该对象可以容纳任何东西。

function foo(a, b, opts) {
  // ...
  if (opts['test']) { } //if test param exists, do something.. 
}


foo(1, 2, {"method":"add"});
foo(3, 4, {"test":"equals", "bar":"tree"});

然后,您可以在方法中以任何方式处理它。 [切换,if-else等。]


#21楼

解决此问题的另一种方法是使用特殊变量: arguments ,这是一种实现:

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

#22楼

介绍

到目前为止,阅读如此众多的答案将使任何人头疼。 任何想知道该概念的人都需要知道以下先决条件

Function overloading DefinitionFunction Length propertyFunction argument property

最简单形式的Function overloading意味着函数根据传递给它的参数数量执行不同的任务。 值得注意的是,下面突出显示了TASK1,TASK2和TASK3,它们是根据传递给同一函数fooYoarguments数量来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-elseswitch语句,使用了一种简单但幼稚的方法。

  • 计算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函数下面,接受三个params对象名object ,函数名name和我们要调用的函数fn

  • addMethod定义内部, var old存储对通过闭包存储的先前function的引用-保护性气泡。

 function addMethod(object, name, fn) { var old = object[name]; object[name] = function(){ if (fn.length == arguments.length) return fn.apply(this, arguments) else if (typeof old == 'function') return old.apply(this, arguments); }; }; 

  • 使用调试器来了解代码流。
  • addMethod下方添加了三个函数,这些函数在使用带有数量x的参数x ninja.whatever(x)调用时可以是任意值,即空白或一个或多个可以调用使用addMethod函数时定义的不同函数。

 var ninja = {}; debugger; addMethod(ninja,'whatever',function(){ console.log("I am the one with ZERO arguments supplied") }); addMethod(ninja,'whatever',function(a){ console.log("I am the one with ONE arguments supplied") }); addMethod(ninja,'whatever',function(a,b){ console.log("I am the one with TWO arguments supplied") }); ninja.whatever(); ninja.whatever(1,2); ninja.whatever(3); 


#23楼

对于您的用例,这就是我要使用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;
}

您显然可以使它适应任何数量的参数,并相应地更改条件语句。


#24楼

通过动态多态性在100行JS中重载函数

这来自包含isFnisArr等类型检查功能的较大代码。 下面的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.log("default", arguments) })
    .add(function(){ console.log("noArgs", arguments) }, [], "noArgs")
    .add(function(){ console.log("str", arguments) }, [function(s){ return typeof s === 'string' }], "str")
;

如果无法识别签名,则overload()的单个可选参数定义要调用的“默认”函数。 .add()的参数为:

  1. fn :定义过载的function
  2. a_vArgumentTests :定义要在arguments上运行的测试的function Array 。 每个function接受一个参数,并根据参数是否有效返回true
  3. sAlias (可选):定义用于直接访问重载函数( fn )的别名的string ,例如myOverloadedFn.noArgs()将直接调用该函数,从而避免了对参数进行动态多态性测试。

实际上,此实现不仅允许传统的函数重载,因为实践中.add()的第二个a_vArgumentTests参数定义了自定义类型。 因此,您不仅可以根据类型,还可以根据范围,值或值的集合来设置参数!

如果仔细查看145行代码的overload()您会发现每个签名都是根据传递给它的arguments数量进行分类的。 这样做是为了限制正在运行的测试数量。 我还会跟踪通话次数。 使用一些附加代码,可以对重载函数的数组进行重新排序,以便首先测试更常用的函数,然后再添加一些性能增强措施。

现在,有一些警告...由于Javascript是松散类型的,因此您必须小心使用vArgumentTests因为可以将integer验证为float等。

JSCompress.com版本(1114字节,744字节g压缩):

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

#25楼

您现在可以在ECMAScript 2018中执行函数重载,而无需使用polyfills,检查var长度/类型等, 只需使用传播语法即可

 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.log(mergedOpts.a); console.log(mergedOpts.b); console.log(mergedOpts.c); console.log(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" 

什么是传播语法?

ECMAScript建议的“休息/扩展属性”(阶段4)将扩展属性添加到对象文字中。 它将自己的可枚举属性从提供的对象复制到新对象。 有关mdn的更多信息

注意:对象文字中的传播语法在Edge和IE中不起作用,这是一项实验性功能。 查看浏览器兼容性


#26楼

对于函数重载,可以执行类似的操作。

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

资源


#27楼

JavaScript中的函数重载:

函数重载是编程语言创建具有不同实现方式的同名多个函数的能力。 当调用重载函数时,它将运行函数,该函数的特定实现适合于调用的上下文。 此上下文通常是接收到的参数数量,它允许一个函数调用根据上下文而有所不同。

Javascript 没有内置函数重载。 但是,可以以多种方式模拟此行为。 这是一个方便的简单方法:

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

在不知道要获取多少参数的情况下,可以使用rest运算符 ,它是三个点... 它将剩余的参数转换为数组。 不过要注意浏览器的兼容性。 这是一个例子:

 function foo (a, ...b) { console.log(b); } foo(1,2,3,4); foo(1,2); 


#28楼

无法在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!!");
     }
}

祝好运!


#29楼

这是一种允许使用参数类型进行实际方法重载的方法,如下所示:

Func(new Point());
Func(new Dimension());
Func(new Dimension(), new Point());
Func(0, 0, 0, 0);

编辑(2018) :自2011年编写以来,直接方法调用的速度大大提高,而重载方法的速度却没有。

这不是我推荐的方法,但是考虑如何解决这些类型的问题是一个值得思考的练习。


这是不同方法的基准-https: //jsperf.com/function-overloading 。 它表明,从16.0(测试版)开始 ,函数重载(考虑类型)在Google Chrome的V8中可以慢13倍左右。

除了传递对象(即{x: 0, y: 0} )之外,还可以在适当时采用C方法,并相应地命名方法。 例如,Vector.AddVector(矢量),Vector.AddIntegers(x,y,z,...)和Vector.AddArray(integerArray)。 您可以查看C库(例如OpenGL)来命名灵感。

编辑 :我已经添加了一个基准,用于传递对象并使用'param' in argarg.hasOwnProperty('param')测试对象,并且函数重载比传递对象和检查属性要快得多(在至少此基准)。

从设计的角度来看,仅当重载的参数对应于同一动作时,函数重载才是有效或逻辑的。 因此,有理由认为应该有一种仅关注特定细节的底层方法,否则可能表明设计选择不当。 因此,还可以通过将数据转换为相应的对象来解决函数重载的使用。 当然,您必须考虑问题的范围,因为如果您的意图只是打印名称,则无需进行复杂的设计,但是对于框架和库的设计而言,这种想法是合理的。

我的示例来自Rectangle实现-因此提到了Dimension和Point。 也许Rectangle可以向DimensionPoint原型添加GetRectangle()方法,然后对函数重载问题进行排序。 那原语呢? 好了,我们有参数长度,由于对象具有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);

#30楼

我经常这样做:

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中为“未定义”,在if语句中计算为false。 但是,函数定义未传达p2和p3是可选的信息。 如果您需要大量重载,则jQuery决定使用一个对象作为参数,例如jQuery.ajax(options)。 我同意他们的观点,这是最有效的方法,并且可以清楚地记录重载的方法,但是我很少需要一个或两个以上的快速可选参数。

编辑:根据Ian的建议更改了IF测试


#31楼

看一下这个。 非常酷 http://ejohn.org/blog/javascript-method-overloading/ Trick Javascript允许您进行如下调用:

var users = new Users();
users.find(); // Finds all
users.find("John"); // Finds users by name
users.find("John", "Resig"); // Finds users by first and last name
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值