当前位置:  开发笔记 > 编程语言 > 正文

Javascript中的函数重载 - 最佳实践

如何解决《Javascript中的函数重载-最佳实践》经验,为你挑选了11个好方法。

在Javascript中伪造函数重载的最佳方法是什么?

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

    首先使用不同的名称

    使用可选参数 y = y || 'default'

    使用参数数量

    检查参数的类型

    或者怎么样?

epascarello.. 568

使用参数进行函数重载的最佳方法是不检查参数长度或类型; 检查类型只会让你的代码变慢,你可以享受Arrays,nulls,Objects等的乐趣.

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

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等]



1> epascarello..:

使用参数进行函数重载的最佳方法是不检查参数长度或类型; 检查类型只会让你的代码变慢,你可以享受Arrays,nulls,Objects等的乐趣.

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

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等]


这不是函数重载.函数重载具有两个具有相同名称但不同参数的独立函数.你所描述的只是一个带有对象参数的函数.
@ user1334007不可能像在Java/.NET中那样进行函数重载.是的,这不是"完全"超载,但它完成了工作.
你能提供一个foo()的示例实现来说明如何使用/引用这些"opts"参数吗?
萌//这可能是这样的; `if(opts ['test'])//如果测试参数存在,做一些事情..如果(opts ['bar'])//如果bar param存在,做一些事情
我很惊讶没有人问过这个问题:为什么不建议检查`arguments.length`?此外,我以前来过这里并阅读_大多数开发人员做的是......,但我确信这是我见过的唯一一个完成的地方.这种方法也破坏了"重载"的语法含糖!
"检查类型只会使你的代码变慢"相对而言,创建一个对象然后查找属性远比类型检查慢.如果传递的对象已经存在,那么对它的属性查找很可能会更快.http://jsperf.com/type-check-vs-property-lookup本回答中的代码为每个调用创建一个新对象,该对象本身比类型检查慢.
@ c24w是的,你可以查看lengthm,但它会因(obj,str,str)vs(obj,str,bool)等原因而失败.
@Dorian使用选项对象是大量JS库的作用 - 你的(咳)选项是有限的.
@Dorian你不能评论或使用JSDoc?

2> rocketsarefa..:

我经常这样做:

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中为'undefined',在if语句中计算结果为false.但是,函数定义不传达p2和p3是可选的信息.如果你需要大量的重载,jQuery决定使用一个对象作为参数,例如,jQuery.ajax(options).我同意他们这是最强大且可清晰记录的重载方法,但我很少需要一个或两个以上的快速可选参数.

编辑:根据伊恩的建议改变了IF测试


未指定的参数在JS中是"undefined",而不是"null".作为一种最佳实践,你永远不应该将任何东西设置为"undefined",所以只要你将测试改为`p2 === undefined`就不会有问题.
我喜欢这样做:p3 = p3 || '默认值';
简单来说,你的`typeof p2 ==="undefined"`可能与你在你的例子中所期望的相反,我认为`typeof p2!=="undefined"`就是你想要的.另外,我建议你应该连接字符串,数字和布尔值,你实际上做的是`p2 ==="number"; p3 ==="boolean"`
如果你传递`false`作为最后一个参数,那么它就不会将``false``连接到结尾,因为`if(p3)`不会分支.
`===`和`!==`是什么意思?为什么不使用`==`和`!=`?

3> Gumbo..:

JavaScript中没有真正的函数重载,因为它允许传递任何类型的任意数量的参数.您必须在函数内部检查已传递的参数数量以及它们的类型.


John Resig的功能超载http://ejohn.org/blog/javascript-method-overloading/

4> Marco..:

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

检查/切换功能内部不是OVERLOADING.

重载的概念:在一些编程语言中,函数重载或方法重载是能够使用不同的实现创建多个同名方法.对重载函数的调用将运行适合于调用上下文的该函数的特定实现,允许一个函数调用根据上下文执行不同的任务.

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

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

所有建议的实现都很棒,但事实上,没有JavaScript的原生实现.



5> t3rse..:

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

    如果要保留很多灵活性,请传递字典(关联数组)

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



6> Keldon Alley..:

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

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(测试版)开始,谷歌Chrome的V8中的功能重载(考虑类型)可能会大约13倍.

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

编辑:我添加了一个标杆传递对象,并使用既为对象测试'param' in argarg.hasOwnProperty('param'),和函数重载比传递对象和属性检查(在这个基准测试至少)快得多.

从设计角度来看,如果重载参数对应于同一个动作,则函数重载仅有效或逻辑.因此,有理由认为应该有一个仅涉及具体细节的基础方法,否则可能表明不适当的设计选择.因此,人们还可以通过将数据转换为相应的对象来解决函数重载的使用问题.当然,必须考虑问题的范围,因为如果你的意图只是打印一个名字,就不需要精心设计,但是对于框架和库的设计,这种思想是合理的.

我的例子来自Rectangle实现 - 因此提到了Dimension和Point.也许矩形可以添加一个GetRectangle()方法到DimensionPoint原型,然后超载问题的功能进行排序.原始人怎么样?好吧,我们有参数长度,现在是一个有效的测试,因为对象有一个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);



7> Matthew Crum..:

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

    使用可选参数,例如y = y || '默认'.如果你能做到这一点很方便,但它可能并不总是有效,例如当0/null/undefined是一个有效的参数时.

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

    检查参数的类型.这可以在参数数量相同的某些情况下起作用.如果无法可靠地确定类型,则可能需要使用不同的名称.

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



8> Aniket Thaku..:

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



9> chessweb..:

我不确定最佳实践,但这是我如何做到的:

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


@Luis:我添加了一些有希望的有用评论.

10> AntouanK..:

我刚试过这个,也许它适合你的需要.根据参数的数量,您可以访问其他功能.您在第一次调用时初始化它.并且函数映射隐藏在闭包中.

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



11> Vlad Bezden..:

由于JavaScript没有函数重载选项,因此可以使用对象.如果有一个或两个必需参数,最好将它们与options对象分开.下面是一个示例,说明如果在options对象中未传递value,如何将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 + "}";

}

这是一个关于如何使用options对象的示例

推荐阅读
放ch养奶牛
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有