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. mybatis-spring 项目简介

    mybatis-spring 主要是为了解决 spring3.0 中支持 mybatis3.0 1 mybatis-spring 主要包含以下几个部分 数据源 SqlSessionFactory Sq ...

  2. layui数据表格自定义每页条数limit

    table.render({ elem: '#data_grid' //,width: 900 //,height: 274 ,cols: [[ //标题栏 {field: 'id', title: ...

  3. SQLSERVER中的timestamp 和 C#中的byte[] 转换

    项目中由于需求设计,数据库中需要一个timestamp时间戳类型的字段来作为区别数据添加和修改的标识.由于timestamp在SQL SERVER 2005数据库中,不可为空的timestamp类型在 ...

  4. Ruby入门笔记

    Ruby入门笔记 一切皆为对象 “Hello”.length 方法 定义:def开头 end结尾 命名一般采用下划线分隔单词

  5. 466. Count Linked List Nodes【Naive】

    Count how many nodes in a linked list. Example Given 1->3->5, return 3. 解法一: /** * Definition ...

  6. swift 函数.和匿名函数

    函数 注意: 没有定义返回类型的函数会返回特殊的值,叫 Void.它其实是一个空的元组(tuple),没有任何元素,可以写成(). 使用元组作为返回参数,返回多个参数 func count(strin ...

  7. Windows Phone 解析手机型号DeviceStatus.DeviceName

    问题的产生 在WP开发过程中难免遇到统计等相关的数据回收,那么当使用DeviceStatus.DeviceName这个来获取设备的名称时经常会得到类似下面的设备信息:     可以看出这样的数据很不直 ...

  8. error: No implicit Ordering defined for Any

    scala中经常遇到最头疼的问题,就是类型不匹配或者带Any,Option的提示错误信息. 最近碰到的是取最大值,但是明明已经Long类型的,却提示下面这个错误信息. 相关的源程序如下: // 获取o ...

  9. 关于报错“syntax error near unexpected token `”和回车换行

    本来是很简单一个事情,转过来是因为打字机这事比较有趣…… http://blog.csdn.net/xyp84/archive/2009/08/11/4435899.aspx 摘要一下: 回车 换行 ...

  10. 微信小程序6 - 页面之间传参及通知系统封装

    1. 简单传参 wx.navigateTo({ url: '/pages/demo/index/index?id=1' }) /pages/demo/index/index.js 中 onLoad(o ...