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

JavaScript

小胖十三Stafan

2020-03-11

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

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

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

第553篇《Javascript中的函数重载-最佳做法》来自Winter(https://github.com/aiyld/aiyld.github.io)的站点

17个回答
樱飞云泡芙 2020.03.11

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

};
Green老丝Itachi 2020.03.11

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

显然,您可以对此进行修改以使其与任何数量的参数一起使用,并且只需相应地更改条件语句即可。

猴子猪猪 2020.03.11

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

  1. 首先使用不同的名称

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

十三米亚阳光 2020.03.11

截至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
梅老丝 2020.03.11

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

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

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

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引用的面板

null 2020.03.11

您现在可以在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中不起作用,这是一项实验性功能。查看浏览器的兼容性

Davaid番长 2020.03.11

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

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

资源

GoldenGG 2020.03.11

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

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

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

蛋蛋L西里 2020.03.11

看一下这个。非常酷 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
MandyMandyJim 2020.03.11

解决此问题的另一种方法是使用特殊变量: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;
}
斯丁小胖Jim 2020.03.11

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

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");
Eva理查德阳光 2020.03.11

由于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 + "}";

}

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

TomTony 2020.03.11

如果我需要同时使用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:上面的链接转到我的个人博客,其中包含其他详细信息。

SamJim 2020.03.11

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

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

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

L西门 2020.03.11

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

凯Sam 2020.03.11

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

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

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

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

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

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

问题类别

JavaScript Ckeditor Python Webpack TypeScript Vue.js React.js ExpressJS KoaJS CSS Node.js HTML Django 单元测试 PHP Asp.net jQuery Bootstrap IOS Android