【问题标题】:No concept of overloading in JavaScriptJavaScript 中没有重载的概念
【发布时间】:2014-04-27 07:03:40
【问题描述】:

查看fiddle 或以下代码:

 function abc(s) {
     console.log('in abc(s)');
 }

 function abc(s, t) {
     console.log('in abc(s,t)');
 }

 abc('1');

这个问题的输出总是in abc(s,t)

谁能解释一下这里发生了什么以及为什么?

【问题讨论】:

  • 发生了什么是第二个函数声明完全覆盖了第一个。原因是JS中没有重载这样的概念。
  • 最新定义的函数会覆盖符号表中的前一个函数
  • 那么它总是调用它找到的最后一个函数吗??
  • 它总是调用最后定义的函数。

标签: javascript overloading


【解决方案1】:

在 Javascript 中没有重载的概念。

但是,您可以编写一个函数来检查通过使用 arguments 值传递了多少参数。

function foo(s, t) {
    if (arguments.length == 2) {
        ...
    } else {
        ...
    }
}

函数期望在签名中但未由调用者传递的所有参数都作为undefined 接收。您还可以通过简单地访问arguments[i] 传递的第 n 个参数来编写可变参数函数。但请注意,arguments 不是 Javascript 数组,因此并非所有数组方法都可用于它。

关于能够多次重新定义同一个函数而不会出错,因为规则很奇怪,所以解释起来有点复杂。

您可以想到的一个简单解释是function 是一个可执行语句,就像它在 Python 中一样,因此最后一个函数定义获胜。然而这是错误的,因为与 Python 不同的是,以下是合法的 Javascript 代码:

console.log(square(12));
function square(x) { return x*x; }

即您可以在定义之前的行中调用函数(在脚本中:当然在 Javascript 控制台中键入这两行是行不通的)。

更正确的解释是编译器首先解析所有函数定义(最后获胜),然后开始执行代码。如果您不将functions 放在if 中,则此思维模型有效,因为在这种情况下实际发生的情况取决于实现(我不是在谈论疯狂的 IE,但即使是 FF 和 Chrome 也会做不同的事情)。只是不要那样做。

你甚至可以使用表单

var square = function(x) { return x*x; }

在这种情况下,它是一个简单的“函数表达式”分配给一个变量,当流通过它时执行(因此可以将函数的不同实现放在不同的if 分支中,但你不能在为其分配实现之前调用该函数)。

【讨论】:

  • 注意事项:虽然arguments.forEach(func)确实不能做,但[].forEach.call(arguments, func)仍然可以做
【解决方案2】:

首先,JavaScript 中不支持方法重载(请参阅 @6502 workaround)。

其次,为了描述您正在经历的事情,在 JavaScript 中,调用了最后声明的函数(具有 相同的 名称),因为前者已被覆盖,它与 JavaScript Hoisting 有关。

尝试重新排序函数声明并再次查看输出结果:

 function abc(s, t) {
     console.log('in abc(s,t)');
 }

function abc(s) {
     console.log('in abc(s)');
 }

 abc('1');

【讨论】:

    【解决方案3】:

    在 javascript 中,只有一个函数具有任何给定名称,如果声明了多个具有相同名称的函数,则最后声明的函数将是活动的。

    但是,您可以测试传递给您的函数的参数并实现许多与函数重载旨在处理的相同类型的行为。事实上,在某些情况下,您可以做得更多。

    在您的具体示例中:

    function abc(s, t) {
        // test to see if the t argument was passed
        if (t !== undefined) {
           console.log('was called as abc(s,t)');
        } else {
           console.log('was called as abc(s)');
        }
    }
    
    abc('1');       // outputs 'was called as abc(s)'
    abc('1', '2');  // outputs 'was called as abc(s,t)'
    

    但是,您还可以获得更多、更多的创意(并且有用)。

    例如,jQuery .css() 方法可以有五种不同的调用方式。

    .css( propertyName )
    .css( propertyNames )
    .css( propertyName, value )
    .css( propertyName, function(index, value) )
    .css( properties )
    

    .css() 方法中的代码检查参数的类型和数量,以确定调用它的方式,从而确定要执行的操作。

    让我们看看如何做到这一点,以确定正在使用该函数的 5 种形式中的哪一种:

    css: function(prop, value) {
        // first figure out if we only have one argument
        if (value === undefined) {
            if (typeof prop === "string") {
                // we have a simple request for a single css property by string name
                // of this form: .css( propertyName )
            } else if (Array.isArray(prop)) {
               // we have a request for an array of properties
               // of this form: .css( propertyNames )
    
            } else if (typeof prop === "object") {
               // property-value pairs of css to set
               // of this form: .css( properties )
            }
        } else {
            if (typeof value === "function") {
               // of this form: .css( propertyName, function(index, value) )
            } else {
               // of this form: .css( propertyName, value )
            }
        }
    }
    

    您还可以实现可选参数。例如,jQuery 的.hide() 可以接受多种形式。其中一种形式是.hide( [duration ] [, complete ] ),其中持续时间和完成功能都是可选的。您可以不传递任何内容,只传递一个持续时间或同时传递一个持续时间和完成回调函数。可以这样实现:

     hide: function(duration, fn) {
         // default the duration to zero if not present
         duration = duration || 0;
         // default the completion function to a dummy function if not present
         fn = fn || function() {};
    
         // now the code can proceed knowing that there are valid arguments for both
         // duration and fn whether they were originally passed or not
     }
    

    我发现使用这些变量参数最有用的方法之一是允许代码支持各种不同的参数类型,这样无论你的参数处于什么状态,你都可以直接传递它们,而无需将它们转换为某种通用类型。例如,在 javascript 中的 this implementation of a set object 中,.add() 方法可以采用所有这些不同形式的参数:

    s.add(key)
    s.add(key1, key2, key3)
    s.add([key1, key2, key3])
    s.add(key1, [key8, key9], key2, [key4, key5])
    s.add(otherSet)              // any other set object
    s.add(arrayLikeObject)       // such as an HTMLCollection or nodeList
    

    这两者都接受可变数量的参数,并且它接受每个参数的许多不同类型,并且它将根据传递给它的内容进行调整。因此,您可以通过键列表、键数组、另一个集合、伪数组或这些类型的任何混合来初始化一个集合。在内部,代码只是遍历传递给函数的每个参数,检查参数的类型并采取相应的行动。

    您可以查看代码here on GitHub 以了解有关如何完成此操作的更多信息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-11
      • 2016-02-08
      • 1970-01-01
      相关资源
      最近更新 更多