美文网首页
JavaScript函数重载

JavaScript函数重载

作者: Mica_马超 | 来源:发表于2019-05-22 13:07 被阅读0次

    说明

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

    函数重载

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

    我们举个例子看看:

    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 中模拟实现重载的效果。

    第一种方法,通过 arguments 对象来实现

    function overLoading() {
      // 根据arguments.length,对不同的值进行不同的操作
      switch(arguments.length) {
        case 0:
          /* 操作1的代码写在这里 */
          break;
        case 1:
          /* 操作2的代码写在这里 */
          break;
        case 2:
          /* 操作3的代码写在这里 */
           
          // 后面还有很多的case......
        }
    }
    

    这个例子非常简单,就是通过判断 arguments 对象的 length 属性来确定有几个参数,然后执行什么操作。
    但是参数少的情况下,还好,如果参数多一些,if 判断就需要写好多,就麻烦了。

    第二种方法,通过闭包来实现

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

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

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

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

    这个需求中 find方法 需要根据参数的个数不同而执行不同的操作,下来我们通过一个 addMethod函数,来在 users 对象中添加这个find方法。

    function addMethod (object, name, fn) {
      // 先把原来的object[name] 方法,保存在old中
      const 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 函数是利用了闭包的特性,通过变量 old 将每个函数连接了起来,让所有的函数都留在内存中

    全部代码

    const users = {
      values: ["Dean Edwards", "Alex Russell", "Dean Tom"]
    };
    
    
    function addMethod (object, name, fn) {
      // 先把原来的object[name] 方法,保存在old中
      const 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);
        }
      }
    }
    
    
    // 不传参数时,返回整个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.dir(users.find);
    
    // 测试:
    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,形成一个闭包。
    我们可以通过 console.dir(users.find) ,把 find 方法打印到控制台看看

    QQ截图20190522125722.png

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

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

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

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

    39023891-5b45c7d6b26fa.png

    我们可以看到 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( ) 方法依赖于三个方法:

    1、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( ) 方法判断第一个参数是字符串还是对象的。

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

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

    3、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函数重载
    JavaScript函数重载
    JavaScript中的函数重载(Function overloading)

    相关文章

      网友评论

          本文标题:JavaScript函数重载

          本文链接:https://www.haomeiwen.com/subject/njtdzqtx.html