zoukankan      html  css  js  c++  java
  • js 高程 22.1.4 函数绑定 bind() 封装分析

    js 高程 书中原话(斜体表示):

    22.1.4 函数绑定
    另一个日益流行的高级技巧叫做函数绑定。函数绑定要创建一个函数,可以在特定的this 环境中
    以指定参数调用另一个函数。该技巧常常和回调函数与事件处理程序一起使用,以便在将函数作为变量
    传递的同时保留代码执行环境。请看以下例子:

    var handler = {
        message: "Event handled",
        handleClick: function(event) {
            console.log(this);           //改编,方便测试,原文是下面一句
            //alert(this.message);        
        }
    };
    document.body.onclick=handler.handleClick;       //改编,方便测试,原文是后面两句
    //var btn = document.getElementById("my-btn");
    //EventUtil.addHandler(btn, "click", handler.handleClick);

    看起来,当我们点击 body 的时候,应该输出 handler 这个对象,但是实际上输出的是 body 元素

    image

    为什么呢?书上原话:

    这个问题在于没有保存 handler.handleClick() 的环境,所以 this 对象最后是指向了 DOM 按钮而非 handler

    我理解就是就是,上面的事件绑定代码其实就相当于:

    document.body.onclick = function(event) {
        console.log(this);
    };

    那么这个地方,事件绑定在谁身上, this 当然就指向谁(事件处理程序中的 this 不清楚的点这里),所以这里输出 body 对象也是很合理的,如果要输出 handler 对象,单独执行 handler.handleClick() 就可以了

    image

    这里也很好理解,handleClick 作为 handler 对象的方法执行的,那么其中的 this 当然指向的是 handler,结合这种情况,要改上面的代码就要这样:

    document.body.onclick = function(event) {
        console.log(this);  //这里的 this 当然还是 body
        handler.handleClick(); // 但是这里面执行的 this 就是 handler 对象了
    };

    image

    但是这样写的缺点很明显,原本就是想通过下面一行代码实现的,如图:

    document.body.onclick=handler.handleClick; //最初
    document.body.onclick = function(event) {  //现在
         handler.handleClick();
    };

    现在明显繁琐了些,而且最初的一行代码理论上也应该是可以实现的,所以现在的代码需要优化封装下,来达到同样的效果,封装的时候,要实现两个目的

    1. 调用函数的时候执行 handler.handleClick(); 且要保存它的执行环境(也就是要确定执行时的 this ,指向的是 handler,而不会变成其他的对象)
    2. 要把 event 对象传递过去

    看到需要改变 this 和保存执行环境的时候,应该想到 js 的函数都有内置的 apply(),call()方法,且 arguments 对象都保存了函数的参数(这个不理解可以看 这里),所以这里封装函数也不是很难了,假设封装的函数名叫 bind ,整个封装逻辑就是下面这样:

        //执行 bind 函数需要返回的匿名函数
        function(event) {
            handler.handleClick();
        };
        //抽象化
        function(event) {
            fn();
        }
        //推算 bind 函数的原型(此原型非js中的原型)
        function bind(fn) {
            return function(event) {
                fn();
            };
        }
        //为了保证 this 的正确传递,需要用 apply 方法,这里用法不理解,请看 这里
        function bind(fn,context) {
            return function(event) {
                fn.apply(context,参数);
            };
        }    
        //为了传递 event 参数,这里的参数不太好理解,需要好好想想
        function bind(fn,context) {
            return function() {
                fn.apply(context,arguments);
            };
        }

    书上原文的结果也是一样的:

    image

    bind 函数 推导出来后,就简单了,原来的代码就变成了

    document.body.onclick=handler.handleClick; //最初错误代码
    document.body.onclick = function(event) {  //后来改善代码
         handler.handleClick();
    };
    document.body.onclick = bind(handler.handleClick,handler); //最后封装 bind 函数

    点击 body 后,就返回的 handler 对象,这样就可以访问 handler 的属性了;

    image

    不过,ES5 已经为所有函数定义了一个原生的 bind() 方法,进一步简单了操作,像上面的代码用 ES5 实际上就是一句

    document.body.onclick = handler.handleClick.bind(handler); 

    所以前面都是废话,- -

  • 相关阅读:
    [置顶] 文件批量重命名(工具)
    zookeeper源码分析之三客户端发送请求流程
    java set转list,数组与list的转换
    分布式电子邮件系统设计--转载
    redis 模糊删除实现
    eclipse 使用jetty调试时,加依赖工程的源码调试方法
    社交产品后端架构设计--转载
    solr服务器的查询过程
    What is corresponding Cron expression to fire in every X seconds, where X > 60? --转载
    zookeeper源码分析之二客户端启动
  • 原文地址:https://www.cnblogs.com/xianshenglu/p/8044012.html
Copyright © 2011-2022 走看看