1.What's is 函数重载?

 Function or method overloading is declaring functions with the same name that accept different arguments thus have different behaviors depending on passed arguments.
函数重载(方法重载)是指声明一组具有不同数量或者不同类型的参数的同名函数,这些同名函数会根据参数的不同表现出不同的行为。(翻译的竟然词不达意,罪过罪过)

2.静态语言的重载

像Java、C++这样的静态语言具有天然的函数重载特性,以C++为例,你一定理解下面的代码。
  1. #include <iostream>
  2. using namespace std;
  3. void print(int i) {
  4. cout <<"Here is num " << i << endl;
  5. }
  6. void print(string str) {
  7. cout << "Here is string " <<str<< endl;
  8. }
  9. int main() {
  10. print(10);//Here is int 10
  11. print("ten");//Here is string ten
  12. }

可以发现在C++中会根据参数的类型自动选择合适的函数执行,如果把上面的代码改造成Javascript代码如下:
  1. function print(i) {
  2. console.log("Here is num"+i);
  3. }
  4. function print(str) {
  5. console.log("Here is string "+str);
  6. }
  7. print(100);//Here is string 100
  8. print("ten");//Here is string ten

显然,这个例子中声明了两个同名函数,而结果则是后面的函数覆盖了前面的函数。那Javascript中到底有没有函数重载特性,
 
 
正确答案是:绝逼没有,但是可以模拟重载的行为

 
在《JS高程》中有这样一段总结:Javascript函数不能像传统意义上那样实现重载。而在其他语言(如 Java、C++)中,可以为一个函数编写两个定义,只要这两个定义的签名(接受的参数的类型和数量)不同即可。但是Javascript函数没有签名,因为其参数是由包含零或多个值的数组来表示的。而没有函数签名,真正的重载是不可能做到的。如果在 Javascript中定义了两个名字相同的函数,则该名字只属于后定义的函数。
也就是说:虽然Javascript不能实现真正的函数重载特性,但是通过检查传入函数中的参数的类型和数量并作出不同的反应,也能模仿方法的重载(想想前面的关于函数重载的定义,重载的结果是同名函数会根据参数的不同表现出不同的行为,既然不能变成真正的“鸭子”,但是能模仿“鸭子”叫,我们就姑且把它看作是“鸭子”了)。

3.如何实现具有Javascript特色的函数重载

Stack Overflow上有人总结了一下几种方法:
  • Using different names in the first place(使用不同的函数名)
  1. function printNum(num) {
  2. console.log("Here is num "+num);
  3. }
  4. function printString(str) {
  5. console.log("Here is string "+str);
  6. }
  7. printNum(100);//Here is num 100
  8. printString("ten");//Here is string ten
上述这种做法实际上已经违背了函数重载的基本特性,因为已经出现了不同的函数名。
  • Using optional arguments like y = y || 'default'(检测是否提供实参,否则采用默认值)

举例说明,现在我们要同时打印一个数字和一个字符串

  1. function print(num,str){
  2. var inner_num=num||0;
  3. var inner_str=str||"default";
  4. console.log("Here is num "+inner_num);
  5. console.log("Here is string "+inner_str);
  6. }
  7. print();
  8. //Here is num 0
  9. //Here is string default
  10. print(10);
  11. //Here is num 10
  12. //Here is string default
  13. print(10,"ten");
  14. //Here is num 10
  15. //Here is string ten
使用这种方法的结果也是显而易见的,实现起来也是相当简单,在Javascript程序中被大量采用,但是执行print( ,"ten");这样的操作会发生错误,在某些情况下如果0/null/undefined为有效值时,执行y = y || 'default'会不分青红皂白的把这些值过滤掉。
  • Using number of arguments(根据参数数量来实现重载)
  1. function print(num,str){
  2. var len=arguments.length;
  3. if (len===1) {
  4. console.log("Here is num "+num);
  5. }
  6. else if (len===2) {
  7. console.log("Here is num "+num);
  8. console.log("Here is string "+str);
  9. }
  10. else{
  11. console.log("看....,飞碟!");
  12. }
  13. }
  14. print();//看....,飞碟!
  15. print(10);//Here is num 10
  16. print(10,"ten");
  17. //Here is num 10
  18. //Here is string ten
从上面的结果看,如果出入的参数只有一个字符串,仍然无法打印。

  • Checking types of arguments (根据参数类型来实现重载)
  1. function print(num,str){
  2. if (typeof num=="number") {
  3. console.log("Here is num "+num);
  4. }
  5. if (typeof str=="string") {
  6. console.log("Here is string "+str);
  7. }
  8. }
  9. print();//无输出
  10. print(10);//Here is num 10
  11. print('',"ten");//Here is string ten
  12. print(10,"ten");
  13. // Here is num 10
  14. // Here is string ten
这种方法的结果算是比较满足函数重载的行为了,但是有没有发现如果只想打印字符串时,也不得不传入两个参数,并且要求开发人员知道函数内部能过滤掉第一个参数,同时检测参数数量和参数类型可以解决这个问题。
下面的例子中直接借用了arguments数组
 
  1. function print(num,str){
  2. var len=arguments.length;
  3. if((len===1)&& (typeof arguments[0]==="number")){
  4. console.log("Here is num "+arguments[0]);
  5. }
  6. else if ((len===1)&& (typeof arguments[0]==="string")) {
  7. console.log("Here is string "+arguments[0]);
  8. }
  9. else if((len===2)&& (typeof arguments[0]==="number")&& (typeof arguments[1]==="string")) {
  10. console.log("Here is num "+arguments[0]);
  11. console.log("Here is string "+arguments[1]);
  12. }
  13. else{
  14. console.log("看....,飞碟!");
  15. }
  16. }
  17. print();//看....,飞碟!
  18. print(10);//Here is num 10
  19. print("ten");//Here is string ten
  20. print(10,"ten");
  21. // Here is num 10
  22. // Here is string ten

在预期参数较多的情况下,上述的所有方法可能都不尽如人意,检测参数类型会拖慢执行速度,并且并且需要检测的类型过多, Arrays, nulls, Objects, etc.而且如果有几个参数的类型相同呢,那岂不是容易混淆,这时Javascript中有一种代码风格被广泛使用:使用对象作为函数的最后一个参数,对象可以承载任何类型的键值对。
  1. function foo(a, b, opts) {
  2. }
  3. foo(1, 2, {"method":"add"});
  4. foo(3, 4, {"test":"equals", "bar":"tree"});
在代码中只需要检测对象的某个键值是否存在来做相应处理。

至此,前面模拟函数重载的方法的基本思想都是基于传递的参数定义一个有很多不同功能的函数,通过使用if-then或者switch子句处理不同的行为,但是一旦事情开始变得的复杂,大量的分支语句就会导致代码笨拙,John Resig在《Secrets of the JavaScript Ninja》(中译名:JavaScript忍者秘籍)中提到一种基于函数的length属性的重载方法
ECMAScript 中的函数是对象,因此函数也有属性和方法。每个函数都包含两个属性:length 。其中,length 属性表示函数希望接收的命名参数的个数,如下面的例子所示(见《Js高程P116页》)。
  1. function sayName(name){
  2. alert(name);
  3. }
  4. function sum(num1, num2){
  5. return num1 + num2;
  6. }
  7. function sayHi(){
  8. alert("hi");
  9. }
  10. alert(sayName.length); //1
  11. alert(sum.length); //2
  12. alert(sayHi.length); //0
因此,对于一个函数,在参数方面,我们可以确定两件事
■ 通过其length属性,可以知道声明了多少命名参数
■ 通过arguments.length,可以知道在调用时传入了多少参数
下面介绍一下大牛是如何实现借助这个特性实现函数的重载的
  1. var ninja = {};
  2. addMethod(ninja,'whatever',function(){ /* do something */ });
  3. addMethod(ninja,'whatever',function(a){ /* do something else */ });
  4. addMethod(ninja,'whatever',function(a,b){ /* yet something else */ });
在这里,先创建一个对象,然后使用同样的名称(whatever)将方法添加到该对象上,只不过每个重载的函数都是单独的,注意每个重载的参数个数都不相同。通过这种方式,真正为每个重载都创建了一个独立的匿名函数。漂亮且整洁!现在的关键就是实现addMethod()函数,大牛的实现相当巧妙:
  1. function addMethod(object, name, fn) {
  2.   var old = object[name]; //保存原有的函数,因为调用的时候可能不匹配传入的参数个数
  3.   object[name] = function() { // 重写了object[name]的方法,是一个匿名函数
  4.     // 如果该匿名函数的形参个数和实参个数匹配,就调用该函数
  5.     if(fn.length === arguments.length) {
  6.       return fn.apply(this, arguments);
  7.     // 如果传入的参数不匹配,就调用原有的参数
  8.     } else if(typeof old === "function") {
  9.       return old.apply(this, arguments);
  10.     }
  11.   }
  12. }
 现在,我们一起来分析一个这个addMethod函数,它接收3个参数:
  • 第一个为要绑定方法的对象,
  • 第二个为绑定方法所用的属性名称,
  • 第三个为需要绑定的方法(一个匿名函数)
 好吧,一言不合就举个例子吧

  1. //addMethod
  2. function addMethod(object, name, fn) {
  3.   var old = object[name];
  4.   object[name] = function() {
  5.     if(fn.length === arguments.length) {
  6.       return fn.apply(this, arguments);
  7.     } else if(typeof old === "function") {
  8.       return old.apply(this, arguments);
  9.     }
  10.   }
  11. }
  12. var printer= {};
  13. // 不传参数时,打印“飞碟”
  14. addMethod(printer, "print", function() {
  15.   console.log("看,飞碟!");
  16. });
  17. // 传一个参数时,打印数字
  18. addMethod(printer, "print", function(num) {
  19. console.log("Here is num "+num);
  20. });
  21. // 传两个参数时,打印数字和字符串
  22. addMethod(printer, "print", function(num,str) {
  23.  console.log("Here is num "+ num);
  24. console.log("Here is string "+str);
  25. });
  26. // 测试:
  27. printer.print(); //看,飞碟!
  28. printer.print(10); //Here is num 10
  29. printer.print(10, "ten");
  30. //Here is num 10
  31. //Here is string ten
对上面的思想稍微改造一下,我们可以直接把重载功能放在函数对象原型上。
  1. // Function.prototype.overload - By 沐浴星光
  2. Function.prototype.overload = function(fn) {
  3. var old= this;
  4. return function() {
  5. if (fn.length===arguments.length) {
  6. return fn.apply(this,arguments);
  7. }else{
  8. return old.apply(this,arguments);
  9. }
  10. }
  11. }
  12. // 不传参数时,打印“飞碟”
  13. function printNone() {
  14.   console.log("看,飞碟!");
  15. }
  16. // 传一个参数时,打印数字
  17. function printInt(num) {
  18. console.log("Here is num "+num);
  19. }
  20. // 传两个参数时,打印数字和字符串
  21. function printBoth(num,str) {
  22. console.log("Here is num "+ num);
  23. console.log("Here is string "+str);
  24. }
  25. print=function(){};
  26. print=print.overload(printNone).overload(printInt).overload(printBoth);
  27. print();//看,飞碟!
  28. print(10);//Here is num 10
  29. print(10,"ten");
  30. // Here is num 10
  31. // Here is string ten
 
 

参考:
《Javascript高级程序设计-第三版》
《JavaScript忍者秘籍》
扩展阅读:
Function Overloading in JavaScript | Chris West's Blog




Javascript函数重载,存在呢—还是存在呢?的更多相关文章

  1. javascript 函数重载 overloading

    函数重载 https://en.wikipedia.org/wiki/Function_overloading In some programming languages, function over ...

  2. JavaScript函数重载

    译者按: jQuery之父John Resig巧妙地利用了闭包,实现了JavaScript函数重载. 原文: JavaScript Method Overloading 译者: Fundebug 为了 ...

  3. [转载]浅谈JavaScript函数重载

     原文地址:浅谈JavaScript函数重载 作者:ChessZhang 上个星期四下午,接到了网易的视频面试(前端实习生第二轮技术面试).面了一个多小时,自我感觉面试得很糟糕的,因为问到的很多问题都 ...

  4. javascript arguments与javascript函数重载

    1.所 有的函数都有属于自己的一个arguments对象,它包括了函所要调用的参数.他不是一个数组,如果用typeof arguments,返回的是’object’.虽然我们可以用调用数据的方法来调用 ...

  5. JavaScript “函数重载”

    函数重载(function overloading)必须依赖两件事情:判断传入参数数量的能力和判断传入参数类型的能力. JavaScript的每个函数都带有一个仅在这个函数范围内作用的变量argume ...

  6. 浅谈JavaScript函数重载

    上个星期四下午,接到了网易的视频面试(前端实习生第二轮技术面试).面了一个多小时,自我感觉面试得很糟糕的,因为问到的很多问题都很难,根本回答不上来.不过那天晚上,还是很惊喜的接到了HR面电话.现在HR ...

  7. javascript 函数重载另一种实现办法

    最近在读javascript忍者 感受下jquery作者 john Resig对于js的独到见解. 先上代码: function addMethod(object,name,fn){ var old ...

  8. 理解javascript函数的重载

        javascript其实是不支持重载的,这篇文章会和java语言函数的重载对比来加深对javascript函数重载的理解.       以下我会假设读者不了解什么是重载所以会有一些很基础的概念 ...

  9. JavaScript模拟函数重载

    JavaScript是没有函数重载的,但是我们可以通过其他方法来模拟函数重载,如下所示: <!DOCTYPE html> <html> <head> <met ...

随机推荐

  1. mybatis一个怪异的问题: Invalid bound statement not found

    ssm中报一下错误: invalid bound statement (not found): me.tspace.pm.dao.userdao.getuser    at org.apache.ib ...

  2. python 之 PIP 安装

    1.安装的必备包 pip 和 setuptool 都要有, 这个可以从python.org网站下载 //  setuptool 安装  下载地址 https://pan.baidu.com/s/1gf ...

  3. Linux下因为系统编码问题造成乱码的解决办法

    2016年12月13日18:34:32 -------------------------------- 最近一段时间遇到一些润乾报表的应用在linux系统下面乱码的问题,最后检查后都发现是客户的li ...

  4. js 中关于this用变量存起来的原因

    近来,不忙,总是在网上看大牛的博客,发现关于this指向的情况,有的说是4种,也对,有的就是总结一句话,也对,但是我发现一种特殊情况,例子如下 var show={ btn:$('.div1'), i ...

  5. asp.net读取execl模板并填充数据,关闭进程

    <head runat="server"> <title></title> <script src="Scripts/jquer ...

  6. 2015.4.25-2015.5.1 字符串去重,比例圆设计,中奖机和canvas橡皮擦效果等

    1.字符串去重,html模板取值   2.javascript正则表达式之$1...$9   3.jquery插件   4.返回上一页并刷新 解决方法: <a href ="javas ...

  7. PHP中静态(static)调用非静态方法详解

    1.PHP中可以静态调用非静态方法么? 今天我被问到PHP中可不可以使用 className::methodName() 的方法来调用一个没有声明static的方法.在我的印象中,我好像是见过这种用法 ...

  8. Linux/CentOS优化配置 汇总

    [强烈要求做的]CentOS启用sudo,禁用root远程登录 Linux命令行修改IP.网关.DNS.主机名 的方法 Linux开机直接进入“命令行”模式 更改CentOS 6.x yum源为国内1 ...

  9. 【Alpha版本】冲刺阶段——Day 10

    我说的都队 031402304 陈燊 031402342 许玲玲 031402337 胡心颖 03140241 王婷婷 031402203 陈齐民 031402209 黄伟炜 031402233 郑扬 ...

  10. Linux ftp访问控制配置,包括访问ftp权限和访问ftp目录权限

    在Linux 上建立用户为website1 home目录是/data/home/website1 但是用ftp登录以后,路径可以随便切换,并且可以进入别的站点下 进行增.删.改 本篇的目的是:在lin ...