美文网首页
JavaScript第二十三篇 技巧篇之函数篇(下)

JavaScript第二十三篇 技巧篇之函数篇(下)

作者: 27亿光年中的小小尘埃 | 来源:发表于2019-11-16 22:35 被阅读0次

    函数篇(下)

    函数绑定

    函数绑定要创建一个函数,可以在特定的 this 环境中以指定参数调用另一个函数。

    为了理解,我们先看一个这样的例子
    示例:

    var handler = {
         message: "Event handled",
         handleClick: function(event){
         alert(this.message);
         }
    };
    
    var btn = document.getElementById("my-btn");
    EventUtil.addHandler(btn, "click", handler.handleClick); 
    
    

    在这个例子中创建了一个handler对象,当点击Id为"my-btn"的DOM节点以后会触发点击事件,触发的函数为handler.handleClick,然后这个函数本应该显示警告框Event handled,然后实际上却是弹出警告框undefined

    因为在调用handler.handleClick的时候this对象最后是指向了 DOM 按钮而非 handler

    那么如何解决这个问题?

    第一种方案
    示例:

    var handler = {
     message: "Event handled",
     handleClick: function(event){
     alert(this.message);
     }
    };
    var btn = document.getElementById("my-btn");
    EventUtil.addHandler(btn, "click", function(event){
     handler.handleClick(event);
    }); 
    
    

    用了一个闭包来直接调用handler.handleClick(event)。并且把event对象传递进去。

    第二种方案:创建一个bind()方法,接受一个函数和一个环境,并返回一个在给定环境中调用给定函数的函数,并且将所有参数原封不动传递过去。

    示例:

    function bind(fn, context){
         return function(){
            return fn.apply(context, arguments);
         };
    } 
    
    var handler = {
         message: "Event handled",
         handleClick: function(event){
            alert(this.message + ":" + event.type); 
         }
    };
    var btn = document.getElementById("my-btn");
    EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler)); 
    

    利用apply把handler.handleClick的作用域绑定在handler上,并且把bind事件上的event参数传递进去

    现在直接调用bind()原生函数即可,不需要我们自己实现了,支持原生bind函数的有 IE9+、Firefox 4+和 Chrome。

    只要是将某个函数指针以值的形式进行传递,同时该函数必须在特定环境中执行,被绑定函数的效用就突显出来了。它们主要用于事件处理程序以及setTimeout()和setInterval()。然而,被绑定函数与普通函数相比有更多的开销,它们需要更多内存,同时也因为多重函数调用稍微慢一点,所以最好只在必要时使用。

    函数柯里化

    与函数绑定紧密相关的主题是函数柯里化(function currying),它用于创建已经设置好了一个或多个参数的函数。函数柯里化的基本方法和函数绑定是一样的:使用一个闭包返回一个函数。两者的区别在于,当函数被调用时,返回的函数还需要设置一些传入的参数。请看以下例子。

    function add(num1, num2){
        return num1 + num2;
    }
    function curriedAdd(num2){
        return add(5, num2);
    }
    alert(add(2, 3)); //5
    alert(curriedAdd(3)); //8 
    
    

    这段代码定义了两个函数:add()和curriedAdd()。后者本质上是在任何情况下第一个参数为 5的 add()版本。尽管从技术上来说curriedAdd()并非柯里化的函数,但它很好地展示了其概念。

    柯里化函数通常由以下步骤动态创建:调用另一个函数并为它传入要柯里化的函数和必要参数。下面是创建柯里化函数的通用方式。

    function curry(fn){
        //获取第一个参数后面的参数
         var args = Array.prototype.slice.call(arguments, 1);
         return function(){
            //获取所有参数
             var innerArgs = Array.prototype.slice.call(arguments);
             //最终形成一个数组
             var finalArgs = args.concat(innerArgs);
             return fn.apply(null, finalArgs);
         };
    } 
    
    

    curry()函数的主要工作就是将被返回函数的参数进行排序。

    curry()的第一个参数是要进行柯里化的函数,其他参数是要传入的值。

    为了获取第一个参数之后的所有参数,在arguments对象上调用了slice()方法,并传入参数 1 表示被返回的数组包含从第二个参数开始的所有参数。

    然后 args 数组包含了来自外部函数的参数。在内部函数中,创建了 innerArgs 数组用来存放所有传入的参数(又一次用到了slice())。

    有了存放来自外部函数和内部函数的参数数组后,就可以使用 concat()方法将
    它们组合为 finalArgs,然后使用apply()将结果传递给该函数。

    注意这个函数并没有考虑到执行环境,所以调用 apply()时第一个参数是 null。curry()函数可以按以下方式应用。

    function add(num1, num2){
     return num1 + num2;
    }
    var curriedAdd = curry(add, 5);
    alert(curriedAdd(3)); //8 
    
    

    但传一个参数时,先获取5,再获取3,最后执行add(5+3)
    也可以传2个参数

     return num1 + num2;
    }
    var curriedAdd = curry(add, 5, 12);
    alert(curriedAdd()); //17 
    
    

    这样的话先直接就获取了5,12,然后再获取的时候没有参数,所以直接就是add(5+12)

    JavaScript 中的柯里化函数和绑定函数提供了强大的动态函数创建功能。使用bind()还是 curry()要根据是否需要object对象响应来决定。它们都能用于创建复杂的算法和功能,当然两者都不应滥用,因为每个函数都会带来额外的开销。

    相关文章

      网友评论

          本文标题:JavaScript第二十三篇 技巧篇之函数篇(下)

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