源码来袭:bind手写实现

理解建议:如果对this指向规则不了解的话,建议先了解this指向规则,最好还能对call和apply的使用和内部原理也有所了解,不然直接研究bind还是会有些难度的。

一、bind()的使用

//html
<button id="btn"></button>
//js
var list = {
    init:function(){
        this.ms = "duyi";
        this.dom = document.getElementById("btn");
        this.bindEvent();
    },
    bindEvent:function(){
        this.dom.onclick = this.showMessage.bind(this);
    },
    showMessage:function(){
        alert(this.ms);
    }
}
list.init();

在单对象编程中,有一种非常典型的bind()的应用,就以上面的示例来说,当出现给DOM绑定事件回调函数时,又还需要继续保持函数的this指向原来的对象,就可以按照示例的这种方式来实现:this.dom.onclick = this.showMessage.bind(this);

我们知道call和apply可以改变函数执行的this指向,但是call和apply都是立即执行该函数,而bind是将this指向绑定到指定的对象上,并且返回函数并维持this指向这个对象。接下来再来看看bind的参数设置示例:

function show(x,y,z,w){
    console.log(this,x,y,z,w);
}
var DuyiO = {
    x : 20
}
var newShow = show.bind(DuyiO,"1","2",3);
newShow(4);//Object {x: 20} "1" "2" 3 4

bind的参数和call非常类似,唯一的区别就在于除了调用bind时传入参数外,还可以在正式执行时传入参数,两次传入参数以拼接的方式作为函数执行的实参。但是需要注意的是,第一个参数作为函数的this指向对象必须要在调用bind方法时传入,如果调用bind方法不传入任何参数,函数的this指向就会绑定到window上。比如下面这种情况:

var newShow = show.bind();
newShow(DuyiO,"1","2",3,4);//Window {…} Object {x: 20} "1" "2" 3

最后还有一个基本上不会被应用到的功能,就是返回的函数被new关键字用来创建一个新的对象,而构造函数还是原函数本身(第二个示例中的show)。这个功能在模仿bind源码不能100%实现,但是也可以间接的实现其需要的功能。

二、bind手写实现

1.首先实现函数调用bind修改this指向即参数设置:

1 Function.prototype.MyBind = function(target){
2     var self = this;
3     var args = [].slice.call(arguments,1);
4     var f = function(){
5         return self.apply( target || window,args );
6     }
7     return f;
8 }

2.接着再来实现函数正式调用执行时传入设置:

1 Function.prototype.MyBind = function(target){
2     var self = this;
3     var args = [].slice.call(arguments,1);
4     var f = function(){
5         var _arg = [].slice.call(arguments,0);
6         return self.apply( target || window,args.concat(_arg) );
7     }
8     return f;
9 }

3.最后实现当返回函数被new操作符引用作为构造函数依然指向原函数(模拟实现功能):

 1 Function.prototype.MyBind = function(target){
 2     var self = this;
 3     var args = [].slice.call(arguments,1);
 4     var temp = function(){};
 5     var f = function(){
 6         var _arg = [].slice.call(arguments,0);
 7         return self.apply(this instanceof temp ? this : ( target || window ),args.concat(_arg) );
 8     }
 9     temp.prototype = self.prototype;
10     f.prototype = new temp();
11     return f;
12 }

这个模拟实现主要有两个关键点需要重点理解:

a.代码第七行中的this instanceof temp ? this : ( target || window ):当返回函数f被new作为构造函数引用时,这时候this指向了函数执行时内部隐式添加在变量对象上的this(这里不清楚的话可以参考JavaScript中的this指向规则),当然普通调用执行就是指向self。

b.代码第九行和第十行为什么需要改变f的原型,这就是我前面讲的模拟实现方法构造,我们知道bind在JS内部实现的是其返回函数还是那个原来的函数,这里我们多加了一层f来实现的,所以在函数被当做构造函数的时候,将f的原型指向self也可以实现其功能,但是构造的实例对象是基于f实现的,最终构造原型链还是指向self原型,该有的方法属性依然都会有。只是在原型链上多了f这个包装层。

原文地址:https://www.cnblogs.com/ZheOneAndOnly/p/10423825.html

时间: 2024-10-11 11:44:44

源码来袭:bind手写实现的相关文章

基于海思Hi3716cv200的Android源码编译及烧写

1.先说说硬件配置,我最开始是在Win7(I7 4770,8G RAM,2T硬盘)里装ubuntu10.10 64位系统(4G RAM,200G 硬盘)进行源码编译,大概花了3个多小时.而且还多次出现outofmemoryerror 错误.最后将硬件升级为I7 4770.16G内存.2T硬盘,不装虚拟机,直接跑Ubuntu 10.10系统,Swap分区为16G,编译一次大概花了40分钟,没有报任何错误. 所以不推荐在虚拟机里面跑. 2.根据官方文档配置编译环境: 官方文档说的是在"发布包/Sof

基于海思Hi3716cv200的Android源码编译及烧写之内置存储卡

问题:把编译好的镜像烧写到小板上后,发现设置--->存储 里只显示0.98G的系统空间,而且在设置--->应用 点开后会崩溃.崩溃时的日志信息如下: time( 3407): at com.android.settings.applications.ManageApplications$TabInfo.updateStorageUsage(ManageApplications.java:333) E/AndroidRuntime( 3407): at com.android.settings.

源码来袭:call、apply手写实现与应用

关于this指向可以了解我的另一篇博客:JavaScript中的this指向规则. 一.call与apply的使用 回顾call与apply的this指向: var value = "window"; var obj = { value:"obj" } fonction show(){ console.log(this.value); } show();//window show.call(obj);//obj show(null);//window 回顾call与

BIND简易教程(0):在Ubuntu下源码安装BIND(其实跟前面的教程没太大关系)

之前介绍过BIND的基本使用啦.关于BIND的入门级使用方法见:http://www.cnblogs.com/anpengapple/p/5877661.html简易教程系列,本篇只讲BIND安装. 本来源码安装不想介绍了,但是最近重装的时候,还是爆出来好多问题.唉,apt方式装习惯了,生成配置文件的脚本都是按照apt的目录和用户来的,源码方式有些坑.所以,还是介绍一下吧(有些部分是直接照前面扒下来的). 首先,因为需要开启DNSSec,所以必须要安装openssl.如果不需要装,或者已经安装好

源码安装Bind 9.10 正式版 开启DLZ数据库支持

昨天看见新闻,说Bind 9.10.3版本已经正式发布了,迫不及待安装试试,,, 我前面的文章已经体验过 bind 9.10的RC版的个别新功能, 见文 Bind 9.10 源码安装 以及 新增redirect 类型 以及$GENERATE指令用法 系统环境:CentOS 6.6 x86_64 1,下载bind 9.10.3的源码包. http://isc.org 2,添加用户,和编译安装bind # tar xf bind-9.10.3.tar.gz # cd bind-9.10.3 # gr

Hadoop源码分析——数据节点写数据1

即使不考虑数据节点出错后的故障处理,文件写入也是HDFS中最复杂的流程.本章以创建一个新文件并向文件中写入数据,然后关闭文件为例,分析客户端写文件时系统各节点的配合,如下图所示. 客户端调用DistributedFileSystem的create()方法创建文件,上图的步骤1,这时,DistributedFileSystem创建DFSOutputStream,并由远程过程调用,让名字节点执行同名方法,在文件系统的命名空间中创建一个新文件.名字节点创建新文件时,需要执行各种各样的检查,如名字节点处

jQuery 2.0.3 源码分析 bind/live/delegate/on

传统的时间处理: 给某一个元素绑定一个点击事件,传入一个回调句柄处理 element.addEventListener('click',doSomething,false); 这样的绑定如果页面上面有几百个需要绑定,那么就要绑定几百多次. 问题: 大量的事件绑定,性能消耗,而且还需要解绑(IE会泄露) 绑定的元素必须要存在. 后期生成HTML会没有事件绑定,需要重新绑定. 语法过于繁杂 事件委托 DOM有个事件流的特性,在页面上触发节点的时候事件都会上下或者向上传播,事件捕捉和事件冒泡. 事件传

Hadoop源码分析——数据节点写数据2

数据接收 客户端写往数据节点的数据由org.apache.hadoop.hdfs.server.datanode.BlockReceiver.java中的receiveBlock方法接收 void receiveBlock( DataOutputStream mirrOut, // output to next datanode DataInputStream mirrIn, // input from next datanode DataOutputStream replyOut, // ou

DT930源码(为公司仓库写的,可参考)

** Header File ** *-----------------------*/ #include <stdio.h> #include <string.h> #include "itron.h" #include "cmndef.h" #include "bios1mac.h" /*----------------------* ** Prototype define ** *------------------