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 元素
为什么呢?书上原话:
这个问题在于没有保存 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();
};
现在明显繁琐了些,而且最初的一行代码理论上也应该是可以实现的,所以现在的代码需要优化封装下,来达到同样的效果,封装的时候,要实现两个目的
- 调用函数的时候执行 handler.handleClick(); 且要保存它的执行环境(也就是要确定执行时的 this ,指向的是 handler,而不会变成其他的对象)
- 要把 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() 封装分析的更多相关文章
- 函数绑定 bind
函数拓展-bind bind实现的是:对函数绑定作用域 更改作用域的方法:call,apply,with,eval,bind call 和 apply 的比较 相同点:1.都是在使用时候(使用即执行) ...
- js 高程 函数节流 throttle() 分析与优化
在 js 高程 22.3.3章节 里看到了 函数节流 的概念,觉得给出的代码可以优化,并且概念理解可以清晰些,所以总结如下: 先看 函数节流 的定义,书上原话(斜体表示): 产生原因/适用场景: 浏览 ...
- 22.1 高级函数【JavaScript高级程序设计第三版】
函数是JavaScript 中最有趣的部分之一.它们本质上是十分简单和过程化的,但也可以是非常复杂和动态的.一些额外的功能可以通过使用闭包来实现.此外,由于所有的函数都是对象,所以使用函数指针非常简单 ...
- js函数绑定同时,如何保留代码执行环境?
经常写js的程序员一定不会对下面这段代码感到陌生. var EventUtil = { addHandler : function(element, type, handler){ if(elemen ...
- js 五种绑定彻底弄懂this,默认绑定、隐式绑定、显式绑定、new绑定、箭头函数绑定详解
壹 ❀ 引 可以说this与闭包.原型链一样,属于JavaScript开发中老生常谈的问题了,百度一搜,this相关的文章铺天盖地.可开发好几年,被几道this题安排明明白白的人应该不在少数(我就是 ...
- JS五种绑定彻底弄懂this,默认绑定、隐式绑定、显式绑定、new绑定、箭头函数绑定详解(转载)
目录 壹 ❀ 引 贰 ❀ this默认绑定 叁 ❀ this隐式绑定 1.隐式绑定 2.隐式丢失 肆 ❀ this显式绑定 伍 ❀ new绑定 陆 ❀ this绑定优先级 柒 ❀ 箭头函数的this ...
- JS事件之自建函数bind()与兼容性问题解决
JavaScript事件绑定常用方法 对象.事件 = 函数; 它只能同时为一个对象的一个事件绑定一个响应函数 不能绑定多个,如果有多个,后面的会覆盖前面的 addEventListener() 此方法 ...
- jQuery 2.0.3 源码分析 事件绑定 - bind/live/delegate/on
事件(Event)是JavaScript应用跳动的心脏,通过使用JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应 事件的基础就不重复讲解了,本来是定位源码分析 ...
- JS通用事件监听函数
JS通用事件监听函数 版本一 //把它全部封装到一个对象中 var obj={ readyEvent:function (fn){ if(fn==null){ fn=document; } var o ...
随机推荐
- JVM虚拟机(五):JDK8内存模型—消失的PermGen
一.JVM 内存模型 根据 JVM 规范,JVM 内存共分为虚拟机栈.堆.方法区.程序计数器.本地方法栈五个部分. 1.虚拟机栈: 每个线程有一个私有的栈,随着线程的创建而创建.栈里面存着的是一种叫“ ...
- Nginx 1.5.2 + PHP 5.5.1 + MySQL 5.6.10 在 CentOS 下的编译安装
最近配置了几台Web服务器,将安装笔记贴出来吧.没时间像以前那样,将文章写的那样系统了,请见谅.详细配置,可以看以前的旧文章: http://blog.zyan.cc/nginx_php_v6 .安装 ...
- 危险系数 set容器
题目描述 抗日战争时期,冀中平原的地道战曾发挥重要作用. 地道的多个站点间有通道连接,形成了庞大的网络.但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系. 我们来定义一个危险系数DF( ...
- mysql 索引 大于等于 走不走索引 最左前缀
你可以认为联合索引是闯关游戏的设计 例如你这个联合索引是state/city/zipCode 那么state就是第一关 city是第二关, zipCode就是第三关 你必须匹配了第一关,才能匹配第二关 ...
- Python教程:[43]Word基本操作
使用python操作Word用到了win32com模块,我们现在就要介绍一下python对Word的基本操作,文章涉及到如何与Word程序建立连接.如果与Word文档建立连接的,以及对Word文档的基 ...
- C语言 · P1001(大数乘法)
算法提高 P1001 时间限制:1.0s 内存限制:256.0MB 当两个比较大的整数相乘时,可能会出现数据溢出的情形.为避免溢出,可以采用字符串的方法来实现两个大数之间的乘法.具体 ...
- IE9 BUG overflow :auto 底部空白解决方案
今天去升级了到IE9,运行项目的时候发现,我的div显示滚动条时候,用js动态加载进去的内容在光标移动的时候,底部自动被撑大留着空白, IE8 Chrome这些以前都试过 没发现这个问题 研究了好久 ...
- librtmp将本地FLV文件发布到RTMP流媒体服务器
没有用到ffmpeg库 可以将本地FLV文件发布到RTMP流媒体服务器 使用librtmp发布RTMP流可以使用两种API:RTMP_SendPacket()和RTMP_Write(). 使用RTMP ...
- 让 MySQL 支持 emoji 存储
要让 MySQL 开启 utf8mb4 支持,需要一些额外的设置. 1. 检查 MySQL Server 版本 utf8mb4 支持需要 MySQL Server v5.5.3+ 2. 设置表的 CH ...
- [android] AndroidManifest.xml - 【 manifest -> 其他次要配置】
<uses-sdk> 作用:使应用程序的兼容性更好,指明应用程序需要的最小API,编译API以及最大支持的API.值都是整数 <uses-sdk android:minSdkVers ...