ham*_*guz 739 javascript overloading
在Javascript中伪造函数重载的最佳方法是什么?
我知道不可能像在其他语言中一样重载Javascript中的函数.如果我需要一个函数有两个用途foo(x)和foo(x,y,z)这是最好的/优选的方法:
y = y || 'default'epa*_*llo 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"});
Run Code Online (Sandbox Code Playgroud)
然后你可以在你的方法中处理它.[切换,if-else等]
roc*_*ast 162
我经常这样做:
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
这个特殊的例子在javascript中实际上比C#更优雅.未指定的参数在javascript中为'undefined',在if语句中计算结果为false.但是,函数定义不传达p2和p3是可选的信息.如果你需要大量的重载,jQuery决定使用一个对象作为参数,例如,jQuery.ajax(options).我同意他们这是最强大且可清晰记录的重载方法,但我很少需要一个或两个以上的快速可选参数.
编辑:根据伊恩的建议改变了IF测试
Mar*_*rco 57
正确的答案是在JAVASCRIPT中没有超载.
检查/切换功能内部不是OVERLOADING.
重载的概念:在一些编程语言中,函数重载或方法重载是能够使用不同的实现创建多个同名方法.对重载函数的调用将运行适合于调用上下文的该函数的特定实现,允许一个函数调用根据上下文执行不同的任务.
例如,doTask()和doTask(对象O)是重载方法.要调用后者,必须将对象作为参数传递,而前者不需要参数,并使用空参数字段调用.常见的错误是在第二种方法中为对象分配一个默认值,这会导致一个模糊的调用错误,因为编译器不知道要使用哪两种方法.
https://en.wikipedia.org/wiki/Function_overloading
所有建议的实现都很棒,但事实上,没有JavaScript的原生实现.
Kel*_*yne 19
这是一种允许使用参数类型进行实际方法重载的方法,如下所示:
Func(new Point());
Func(new Dimension());
Func(new Dimension(), new Point());
Func(0, 0, 0, 0);
Run Code Online (Sandbox Code Playgroud)
编辑(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 arg和arg.hasOwnProperty('param'),和函数重载比传递对象和属性检查(在这个基准测试至少)快得多.
从设计角度来看,如果重载参数对应于同一个动作,则函数重载仅有效或逻辑.因此,有理由认为应该有一个仅涉及具体细节的基础方法,否则可能表明不适当的设计选择.因此,人们还可以通过将数据转换为相应的对象来解决函数重载的使用问题.当然,必须考虑问题的范围,因为如果你的意图只是打印一个名字,就不需要精心设计,但是对于框架和库的设计,这种思想是合理的.
我的例子来自Rectangle实现 - 因此提到了Dimension和Point.也许矩形可以添加一个GetRectangle()方法到Dimension和Point原型,然后超载问题的功能进行排序.原始人怎么样?好吧,我们有参数长度,现在是一个有效的测试,因为对象有一个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);
Run Code Online (Sandbox Code Playgroud)
Mat*_*ley 16
最好的方法实际上取决于函数和参数.在不同情况下,您的每个选项都是个好主意.我通常按以下顺序尝试这些,直到其中一个工作:
使用可选参数,例如y = y || '默认'.如果你能做到这一点很方便,但它可能并不总是有效,例如当0/null/undefined是一个有效的参数时.
使用参数数量.与最后一个选项类似,但在#1不起作用时可能有效.
检查参数的类型.这可以在参数数量相同的某些情况下起作用.如果无法可靠地确定类型,则可能需要使用不同的名称.
首先使用不同的名称.如果其他选项不起作用,不实用或与其他相关功能保持一致,则可能需要执行此操作.
Ani*_*kur 14
如果我需要一个有两个使用foo(x)和foo(x,y,z)的函数,这是最好/首选的方法吗?
问题是JavaScript本身不支持方法重载.因此,如果它看到/解析两个或多个具有相同名称的函数,它将只考虑最后定义的函数并覆盖之前的函数.
我认为适合大多数情况的方式之一是 -
让我们说你有方法
function foo(x)
{
}
Run Code Online (Sandbox Code Playgroud)
您可以定义一个新方法,而不是在javascript中无法实现的重载方法
fooNew(x,y,z)
{
}
Run Code Online (Sandbox Code Playgroud)
然后修改第一个函数如下 -
function foo(arguments)
{
if(arguments.length==2)
{
return fooNew(arguments[0], arguments[1]);
}
}
Run Code Online (Sandbox Code Playgroud)
如果您有许多这样的重载方法,请考虑使用switch而不仅仅是if-else语句.
(更多细节)
PS:以上链接转到我的个人博客,其中包含其他详细信息.
Luc*_*oli 13
并不是每个人都知道可以直接在函数签名中进行解构赋值。
因此,您可以轻松定义非常灵活的方法签名,恕我直言,这优于 Java 方法重载。
例子:
const myFunction = (({a, b, c}) => {
console.log(a, b, c);
});
myFunction({a: 1, b: 2});
myFunction({a: 1, b: 2, c: 3});
Run Code Online (Sandbox Code Playgroud)
您甚至不需要遵守参数的顺序,并且调用语句和目标方法签名之间存在命名一致性。
您还可以指定默认值:
const myFunction = (({a = 1, b = 2, c} = {}) => {
console.log(a, b, c);
});
Run Code Online (Sandbox Code Playgroud)
我不确定最佳实践,但这是我如何做到的:
/*
* 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"
Run Code Online (Sandbox Code Playgroud)
我刚试过这个,也许它适合你的需要.根据参数的数量,您可以访问其他功能.您在第一次调用时初始化它.并且函数映射隐藏在闭包中.
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;
};
Run Code Online (Sandbox Code Playgroud)
测试一下.
TEST.multiFn("0")
.multiFn()
.multiFn("0","1","2");
Run Code Online (Sandbox Code Playgroud)
由于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 + "}";
}
Run Code Online (Sandbox Code Playgroud)
这是一个关于如何使用options对象的示例
介绍
到目前为止,阅读这么多答案会让任何人头疼。任何试图了解这个概念的人都需要了解以下先决条件。
Function overloading Definition, Function Length property,Function argument property
Function overloading最简单的形式意味着一个函数根据传递给它的参数数量执行不同的任务。值得注意的是,下面突出显示了 TASK1、TASK2 和 TASK3,它们是根据arguments传递给同一函数的数量执行的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
Run Code Online (Sandbox Code Playgroud)
注意 - JS 不提供内置的函数重载能力。
选择
John E Resig(JS 的创建者)已经指出了一个替代方案,它使用上述先决条件来实现实现函数重载的能力。
下面的代码通过 using if-elseorswitch语句使用了一种简单但朴素的方法。
argument-length财产。// 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
Run Code Online (Sandbox Code Playgroud)
另一种技术更加干净和动态。这种技术的亮点是addMethod泛型函数。
我们定义了一个函数addMethod,用于向同名但功能不同的对象添加不同的功能。
在addMethod函数下面接受三个参数对象名object、函数名name和我们要调用的函数fn。
addMethod定义var old在function闭包的帮助下存储对先前存储的引用- 一个保护性气泡。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 ...
}
}
}Run Code Online (Sandbox Code Playgroud)
addMethod添加了三个函数,当使用ninja.whatever(x)参数的数量调用时,这些函数x可以是任何参数,即空白或一个或多个,调用在使用该addMethod函数时定义的不同函数。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);
};
};Run Code Online (Sandbox Code Playgroud)