JavaScript中的函数重载(Function overloading)

说明

JavaScript 中没有真正意义上的函数重载。javascript

函数重载

函数名相同,函数的参数列表不一样(包括参数个数和参数类型),根据参数的不一样去执行不一样的操做。 css

咱们举个例子看看html

function overload(a){
    console.log('一个参数')
}

function overload(a,b){
    console.log('两个参数')
}

// 在支持重载的编程语言中,好比 java
overload(1);         //一个参数
overload(1,2);    //两个参数


// 在 JavaScript 中
overload(1);         //两个参数
overload(1,2);    //两个参数

在JavaScript中,同一个做用域,出现两个名字同样的函数,后面的会覆盖前面的,因此 JavaScript 没有真正意义的重载。前端

可是有各类办法,能在 JavaScript 中模拟实现重载的效果。java

先看第一种办法,经过 arguments 对象来实现

arguments 对象,是函数内部的一个类数组对象,它里面保存着调用函数时,传递给函数的全部参数。jquery

function overload () {
  if (arguments.length === 1) {
    console.log('一个参数')
  }
  if (arguments.length === 2) {
    console.log('两个参数')
  }
}

overload(1);      //一个参数
overload(1, 2);  //两个参数

这个例子很是简单,就是经过判断 arguments 对象的 length 属性来肯定有几个参数,而后执行什么操做。git

可是参数少的状况下,还好,若是参数多一些,if 判断就须要写好多,就麻烦了。github

因此,咱们再来看一个经典的例子
在看这个例子以前,咱们先来看一个需求,咱们有一个 users 对象,users 对象的values 属性中存着一些名字。
一个名字由两部分组成,空格左边的是 first-name ,空格右边的是 last-name,像下面这样。编程

var users = {
  values: ["Dean Edwards", "Alex Russell", "Dean Tom"]
};

咱们要在 users 对象 中添加一个 find 方法,segmentfault

当不传任何参数时, 返回整个users .values
当传一个参数时,就把 first-name 跟这个参数匹配的元素返回;
当传两个参数时,则把 first-name 和 last-name 都匹配的返回。

这个需求中 find方法 须要根据参数的个数不一样而执行不一样的操做,下来咱们经过一个 addMethod 函数,来在 users 对象中添加这个 find 方法。

function addMethod (object, name, fn) {
  // 先把原来的object[name] 方法,保存在old中
  var old = object[name];

  // 从新定义 object[name] 方法
  object[name] = function () {
    // 若是函数须要的参数 和 实际传入的参数 的个数相同,就直接调用fn
    if (fn.length === arguments.length) {
      return fn.apply(this, arguments);

      // 若是不相同,判断old 是否是函数,
      // 若是是就调用old,也就是刚才保存的 object[name] 方法
    } else if (typeof old === "function") {
      return old.apply(this, arguments);
    }
  }
}

addMethod 函数,它接收3个参数
第一个:要绑定方法的对象,
第二个:绑定的方法名称,
第三个:须要绑定的方法

这个 addMethod 函数在判断参数个数的时候,除了用 arguments 对象,还用了函数的 length 属性。

函数的 length 属性,返回的是函数定义时形参的个数。

简单说 函数的 length 是,函数须要几个参数,而 arguments.length 是调用函数时,真的给了函数几个参数

function fn (a, b) {
  console.log(arguments.length)
}
console.log(fn.length);  // 2
fn('a');    // 1

下来咱们来使用这个 addMethod 函数

// 不传参数时,返回整个values数组
function find0 () {
  return this.values;
}
// 传一个参数时,返回firstName匹配的数组元素
function find1 (firstName) {
  var ret = [];
  for (var i = 0; i < this.values.length; i++) {
    if (this.values[i].indexOf(firstName) === 0) {
      ret.push(this.values[i
      ]);
    }
  }
  return ret;
}
// 传两个参数时,返回firstName和lastName都匹配的数组元素
function find2 (firstName, lastName) {
  var ret = [];
  for (var i = 0; i < this.values.length; i++) {
    if (this.values[i
    ] === (firstName + " " + lastName)) {
      ret.push(this.values[i
      ]);
    }
  }
  return ret;
}
// 给 users 对象添加处理 没有参数 的方法
addMethod(users, "find", find0);

// 给 users 对象添加处理 一个参数 的方法
addMethod(users, "find", find1);

// 给 users 对象添加处理 两个参数 的方法
addMethod(users, "find", find2);

// 测试:
console.log(users.find()); //["Dean Edwards", "Alex Russell", "Dean Tom"]
console.log(users.find("Dean")); //["Dean Edwards", "Dean Tom"]
console.log(users.find("Dean","Edwards")); //["Dean Edwards"]

addMethod 函数是利用了闭包的特性,经过变量 old 将每一个函数链接了起来,让全部的函数都留在内存中。

每调用一次 addMethod 函数,就会产生一个 old,造成一个闭包。
咱们能够经过 console.dir(users.find) ,把 find 方法打印到控制台看看。

图片描述

上面这个例子是 jQuery 之父 John Resig 写的,他在他的博客和他写的书 《secrets of the JavaScript ninja》初版中都有提到过,在书中的第4章中也有讲解 Function overloading,文中的 addMethod 函数 就是书中的例子 4.15,感兴趣的朋友能够去看看。

上面的例子,本质都是在判断参数的个数,根据不一样的个数,执行不一样的操做,而下来举的例子是经过判断参数的类型,来执行不一样的操做。

咱们看看 jQuery 中的 css( ) 方法

css( ) 方法返回或设置匹配的元素的一个或多个样式属性。

css(name|pro|[,val|fn])

图片描述

咱们能够看到 css( ) 方法,有5种 参数状况,其中3种是一个参数,另外两种是两个参数。
而在只有一个参数的状况下,若是参数类型是字符串或者数组就是获取属性值,而若是参数是对象,就是是设置属性值。

jQuery 的 css( ) 方法就是经过判断参数的类型,来肯定执行什么操做。

咱们来看看jQuery 3.3.1中的源码

// name 表示属性名
// value 表示属性值
css: function( name, value ) {
    return access( this, function( elem, name, value ) {
        var styles, len,
            map = {},
            i = 0;

        // 判断属性名是否是数组
        // 是数组就遍历,调用jQuery.css 方法传入每一个属性名,获取样式
        if ( Array.isArray( name ) ) {
            styles = getStyles( elem );
            len = name.length;

            for ( ; i < len; i++ ) {
                map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
            }

            return map;
        }

        // 若是value 不等于 undefined 就调用jQuery.style 方法设置样式
        // 若是value 等于 undefined 就调用jQuery.css 方法获取样式
        return value !== undefined ?
            jQuery.style( elem, name, value ) :
            jQuery.css( elem, name );
    }, name, value, arguments.length > 1 );
}

css( ) 方法依赖于三个方法:

一、jQuery.access( ) 方法,这个方法能够获取 或 设置,一个或者多个属性值

jQuery.access( ) 方法里有这样的代码

// 设置多个属性值
// 若是属性名(key)的类型是 object,就遍历这个对象
// 遍历一次就调用一次 access()方法,并传入此次的属性名和属性值
if ( jQuery.type( key ) === "object" ) {
    chainable = true;
    for ( i in key ) {
        jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
    }

// 设置一个值
} else if ( value !== undefined ) {
    ......
}

也就是这个方法,在帮 css( ) 方法判断第一个参数是字符串仍是对象的。

二、jQuery.style( ) 方法:在DOM节点上读取或设置样式属性

在css( )方法中,若是有传第二个参数,也就是有要设置的属性值时,那就会调用 jQuery.style( ) 方法设置样式

三、jQuery.css( ) :在DOM元素上读取DOM样式值

这里的 jQuery.css( ) 是经过 jQuery.extend( ) 添加的方法,而咱们最开始提到的 css( )方法,是经过 jQuery.fn.extend( ) 添加的方法,他们不是同一个方法。

jQuery.extend( )与 jQuery.fn.extend( )的区别

jQuery.extend( )是为jQuery类添加类方法(静态方法),须要经过jQuery类来调用(直接使用 $.xxx 调用);

jQuery.fn.extend( )是为jQuery类添加成员数(实例方法),全部jQuery实例均可以直接调用(须要使用 $().xxx 调用)。

重载的好处

重载实际上是把多个功能相近的函数合并为一个函数,重复利用了函数名。
假如jQuery中的css( )方法不使用 重载,那么就要有5个不一样的函数,来完成功能,那咱们就须要记住5个不一样的函数名,和各个函数相对应的参数的个数和类型,显然就麻烦多了。

总结

虽然 JavaScript 并无真正意义上的重载,可是重载的效果在JavaScript中却很是常见,好比 数组的 splice( )方法,一个参数能够删除,两个参数能够删除一部分,三个参数能够删除完了,再添加新元素。
再好比 parseInt( )方法 ,传入一个参数,就判断是用十六进制解析,仍是用十进制解析,若是传入两个参数,就用第二个参数做为数字的基数,来进行解析。

文中提到的实现重载效果的方法,本质都是对参数进行判断,无论是判断参数个数,仍是判断参数类型,都是根据参数的不一样,来决定执行什么操做的。

虽然,重载能为咱们带来许多的便利,可是也不能滥用,不要把一些根本不相关的函数合为一个函数,那样并无什么意义。

参考

浅谈JavaScript函数重载

js如何实现重载

JavaScript函数重载

前端简单说

相关文章
相关标签/搜索