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 元素

为什么呢?书上原话:

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

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

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

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

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

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

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

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);
};
}

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

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

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

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

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

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

所以前面都是废话,- -

js 高程 22.1.4 函数绑定 bind() 封装分析的更多相关文章

  1. 函数绑定 bind

    函数拓展-bind bind实现的是:对函数绑定作用域 更改作用域的方法:call,apply,with,eval,bind call 和 apply 的比较 相同点:1.都是在使用时候(使用即执行) ...

  2. js 高程 函数节流 throttle() 分析与优化

    在 js 高程 22.3.3章节 里看到了 函数节流 的概念,觉得给出的代码可以优化,并且概念理解可以清晰些,所以总结如下: 先看 函数节流 的定义,书上原话(斜体表示): 产生原因/适用场景: 浏览 ...

  3. 22.1 高级函数【JavaScript高级程序设计第三版】

    函数是JavaScript 中最有趣的部分之一.它们本质上是十分简单和过程化的,但也可以是非常复杂和动态的.一些额外的功能可以通过使用闭包来实现.此外,由于所有的函数都是对象,所以使用函数指针非常简单 ...

  4. js函数绑定同时,如何保留代码执行环境?

    经常写js的程序员一定不会对下面这段代码感到陌生. var EventUtil = { addHandler : function(element, type, handler){ if(elemen ...

  5. js 五种绑定彻底弄懂this,默认绑定、隐式绑定、显式绑定、new绑定、箭头函数绑定详解

     壹 ❀ 引 可以说this与闭包.原型链一样,属于JavaScript开发中老生常谈的问题了,百度一搜,this相关的文章铺天盖地.可开发好几年,被几道this题安排明明白白的人应该不在少数(我就是 ...

  6. JS五种绑定彻底弄懂this,默认绑定、隐式绑定、显式绑定、new绑定、箭头函数绑定详解(转载)

    目录 壹 ❀ 引 贰 ❀ this默认绑定 叁 ❀ this隐式绑定 1.隐式绑定 2.隐式丢失 肆 ❀ this显式绑定 伍 ❀ new绑定 陆 ❀ this绑定优先级 柒 ❀ 箭头函数的this ...

  7. JS事件之自建函数bind()与兼容性问题解决

    JavaScript事件绑定常用方法 对象.事件 = 函数; 它只能同时为一个对象的一个事件绑定一个响应函数 不能绑定多个,如果有多个,后面的会覆盖前面的 addEventListener() 此方法 ...

  8. jQuery 2.0.3 源码分析 事件绑定 - bind/live/delegate/on

    事件(Event)是JavaScript应用跳动的心脏,通过使用JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应 事件的基础就不重复讲解了,本来是定位源码分析 ...

  9. JS通用事件监听函数

    JS通用事件监听函数 版本一 //把它全部封装到一个对象中 var obj={ readyEvent:function (fn){ if(fn==null){ fn=document; } var o ...

随机推荐

  1. Angularjs Directive - Compile vs. Link

    如果我想实现这样一个功能,当一个input失去光标焦点时(blur),执行一些语句,比如当输入用户名后,向后台发ajax请求查询用户名是否已经存在,好有及时的页面相应. 输入 hellobug  失去 ...

  2. dataGridViewX操作

    private void dataGridView1_RowStateChanged(object sender, DataGridViewRowStateChangedEventArgs e) { ...

  3. struts2设置非默认路径的struts.properties以及.properties文件解决方案

    一.web应用的classpath简介    classpath路径在每个J2ee项目中都会用到,即WEB-INF下面的classes目录,所有src目录下面的java.xml.properties等 ...

  4. nyoj983 首尾相连数组的最大子数组和

    首尾相连数组的最大子数组和 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描述 给定一个由N个整数元素组成的数组arr,数组中有正数也有负数,这个数组不是一般的数组,其首尾是 ...

  5. css 盒子垂直居中

    面试的时候经常会被问到这样一个题目:让一个元素中内容垂直居中怎么做.其实之前,我就会两种,line-height和table-cell,今天做项目,遇到了这个问题,就系统的查了一下,总结一下方法: 1 ...

  6. [linux]signal函数不起作用

    #include "apue.h" #include <sys/wait.h> static void sig_int(int); /* our signal-catc ...

  7. 【持续更新】GDB使用笔记

    多文件程序的调试,例子: 文件结构: /demo Makefile /src demo.cpp util.cpp /include util.h 截图: ----------------------- ...

  8. Git中保存用户名和密码

    每次操作都需要输入用户名和密码感觉很繁琐,解决方法,在本地的工程文件夹的.git下打开config文件添加: [credential]     helper = store 再输入一次用户名密码后就可 ...

  9. js监听 window.open 关闭事件

    转载自:http://blog.csdn.net/hanshileiai/article/details/41346729 首先创建一个新的对象,这将打开一个弹出这样的: var winObj = w ...

  10. 正则 群组 Group

    static void Main(string[] args) { string strUrl = "<a href=\"user.php?act=order_detail& ...