首先,什么是软绑定?

所谓软绑定,是和硬绑定相对应的一个词,在详细解释软绑定之前,我们先来看看硬绑定。在JavaScript中,this的绑定是动态的,在函数被调用的时候绑定,它指向什么完全取决于函数在哪里调用,情况比较复杂,光是绑定规则就有默认绑定、隐式绑定、显式绑定、new绑定等,而硬绑定是显式绑定中的一种,通常情况下是通过调用函数的 apply() 、 call() 或者ES5里提供的 bind() 方法来实现硬绑定的。

硬绑定有什么问题,为什么需要软绑定

上述三个方法好是好,可以按照自己的想法将函数的this强制绑定到指定的对象上(除了使用new绑定可以改变硬绑定外),但是硬绑定存在一个问题,就是会降低函数的灵活性,并且在硬绑定之后无法再使用隐式绑定或者显式绑定来修改this的指向。

在这种情况下,被称为软绑定的实现就出现了,也就是说,通过软绑定,我们希望this在默认情况下不再指向全局对象(非严格模式)或 undefined (严格模式),而是指向两者之外的一个对象(这点和硬绑定的效果相同),但是同时又保留了隐式绑定和显式绑定在之后可以修改this指向的能力。

软绑定的具体实现

在这里,我用的是《你不知道的JavaScript 上》中的软绑定的代码实现:

 if(!Function.prototype.softBind){
Function.prototype.softBind=function(obj){
var fn=this;
var args=Array.prototype.slice.call(arguments,1);
var bound=function(){
return fn.apply(
(!this||this===(window||global))?obj:this,
args.concat.apply(args,arguments)
);
};
bound.prototype=Object.create(fn.prototype);
return bound;
};
}

我们先来看一下效果,之后再讨论它的实现。

 function foo(){
console.log("name: "+this.name);
} var obj1={name:"obj1"},
obj2={name:"obj2"},
obj3={name:"obj3"}; var fooOBJ=foo.softBind(obj1);
fooOBJ();//"name: obj1" 在这里软绑定生效了,成功修改了this的指向,将this绑定到了obj1上 obj2.foo=foo.softBind(obj1);
obj2.foo();//"name: obj2" 在这里软绑定的this指向成功被隐式绑定修改了,绑定到了obj2上 fooOBJ.call(obj3);//"name: obj3" 在这里软绑定的this指向成功被硬绑定修改了,绑定到了obj3上 setTimeout(obj2.foo,1000);//"name: obj1"
/*回调函数相当于一个隐式的传参,如果没有软绑定的话,这里将会应用默认绑定将this绑定到全局环
境上,但有软绑定,这里this还是指向obj1*/

可以看到软绑定生效了。下面我们来具体看一下 softBind() 的实现。

在第一行,先通过判断,如果函数的原型上没有 softBind() 这个方法,则添加它,然后通过 Array.prototype.slice.call(arguments,1) 获取传入的外部参数,这里这样做其实为了函数柯里化,也就是说,允许在软绑定的时候,事先设置好一些参数,在调用函数的时候再传入另一些参数(关于函数柯里化大家可以去网上搜一下详细的讲解)最后返回一个 bound 函数形成一个闭包,这时候,在函数调用 softBind() 之后,得到的就是 bound 函数,例如上面的 var fooOBJ=foo.softBind(obj1) 。
 
在 bound 函数中,首先会判断调用软绑定之后的函数(如fooOBJ)的调用位置,或者说它的this的指向,如果 !this (this指向undefined)或者 this===(window||global) (this指向全局对象),那么就将函数的this绑定到传入 softBind 中的参数obj上。如果此时this不指向undefind或者全局对象,那么就将this绑定到现在正在指向的函数(即隐式绑定或显式绑定)。 fn.apply 的第二个参数则是运行 foo 所需要的参数,由上面的args(外部参数)和内部的arguments(内部参数)连接成,也就是上面说的柯里化。
 
其实在第一遍看这个函数时,也有点迷,有一些疑问,比如 var fn=this 这句,在 foo 通过 foo.softBind() 调用 softBind 的时候,fn到底指向谁呢?是指向foo还是指向softBind?我们可以写个demo测试,然后可以很清晰地看出fn指向什么:
 var a=2;
function foo(){
}
foo.a=3;
Function.prototype.softBind=function(){
var fn=this;
return function(){
console.log(fn.a);
}
};
Function.prototype.a=4;
Function.prototype.softBind.a=5; foo.softBind()();//
Function.prototype.softBind()();//
可以看出,fn(或者说this)的指向还是遵循this的绑定规则的, softBind 函数定义在Function的原型 Function.prototype 中,但是JavaScript中函数永远不会“属于”某个对象(不像其他语言如java中类里面定义的方法那样),只是对象内部引用了这个函数,所以在通过下面两种方式调用时,fn(或者说this)分别隐式绑定到了 foo 和 Function.prototype ,所以分别输出3和4。后面的 fn.apply() 也就相当于 foo.apply() 。
 
注:这篇文章我首载于掘金,由于一些个人原因现在才发到博客园,原文可见于https://juejin.im/post/5b0417af518825426c692d54

关于JavaScript中this的软绑定的更多相关文章

  1. javascript中onload事件如何绑定及执行顺序

    onload事件,顾名思义就是为了js程序能够在网页加载成功过后进行执行.对于放在head里面的js来说,非常必要. 如何给网页绑定onload,主要有三种方式: window.onload=func ...

  2. javascript - 工作笔记 (事件绑定)

    背景: 目前所做的项目,只能使用的是原生的javascript.对于javascript的事件绑定想必大家都懂得语法: 1,在标签中使用属性调用方法:<div onclick="AAA ...

  3. JavaScript中事件绑定的方法总结

    最近收集了一些关于JavaScript绑定事件的方法,汇总了一下,不全面,但是,希望便于以后自己查看. JavaScript中绑定事件的方法主要有三种: 1 在DOM元素中直接绑定 2 JavaScr ...

  4. js实例分析JavaScript中的事件委托和事件绑定

    我们在学习JavaScript中,难免都会去网上查一些资料.也许偶尔就会遇到“事件委托”(也有的称我“事件代理”,这里不评论谁是谁非.以下全部称为“事件委托”),尤其是在查JavaScript的事件处 ...

  5. JavaScript中,有三种常用的绑定事件的方法

    要想让 JavaScript 对用户的操作作出响应,首先要对 DOM 元素绑定事件处理函数.所谓事件处理函数,就是处理用户操作的函数,不同的操作对应不同的名称. 在JavaScript中,有三种常用的 ...

  6. JavaScript中this的绑定规则

    JavaScript中this的绑定规则 前言 我们知道浏览器运行环境下在全局作用域下的this是指向window的,但是开发中却很少在全局作用域下去使用this,通常都是在函数中进行使用,而函数使用 ...

  7. 了解javascript中的事件(二)

    本文目录如下: 零.寒暄 一.事件的分类 二.事件代理 2.1 问题引出 2.2 什么是事件代理 2.3 完整示例 二.事件代理 三.事件代理思想的用处 四.总结 零.寒暄 这篇博客本该出现在两个月以 ...

  8. JavaScript中的this—你不知道的JavaScript上卷读书笔记(三)

    this是什么? this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件.this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式.当一个函数被调用时,会 ...

  9. 【你不知道的javaScript 上卷 笔记5】javaScript中的this词法

    function foo() { console.log( a ); } function bar() { var a = 3; foo(); } var a = 2; bar(); 上面这段代码为什 ...

随机推荐

  1. 使用eclipse阅读java源码

    很多时候想要阅读java源代码(无论是jdk自带的,还在第三方的),但是我们使用的jar包都是编译好的class包,无法在eclipse中直接打开,此时需要下载源码包. 本文以查看HttpClient ...

  2. 我是怎样和Linux系统结缘并通过红帽RHCE认证的

    我高考完当时就是选择的计算机科学与技术专业,上大学以后联想到的和计算机相关的就只有写代码,开发,网站,网页设计,就没有其他的了,当时学习写代码也都是在Windows上,什么C#.C++之类的?大约在大 ...

  3. vue将表格导出为excel

    vue将表格导出为excel 一:在项目中需要安装2个依赖项,如下命令: npm install --save file-saver xlsx 二:在vue文件中如下使用即可: <templat ...

  4. 博客搬家了qwq

    呃,其实也不是搬家了,应该算是逐渐过渡qwq \[\color{skyblue}{Orchid} \color{purple}{any}\] 好的,我在学校里并不可以用Hexo,因为deploy总是挂 ...

  5. 动手动脑(lesson2)

    一·随机数生成问题 //信1605-3 20163683 蔡金阳 package lesson2; public class Suiji { private static final int N = ...

  6. P4208 [JSOI2008]最小生成树计数

    现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)输出方案数对31011 ...

  7. day88

    ContentType 场景需求:比方说我们现有两种商品,但是他们对应着一个价格策略表,为了防止数据库的浪费,我们在价格策略中加入一个表名字段,一个表明对应的id字段,这样的设计既优化了数据库,还不怕 ...

  8. linux安装jdk与配置-centos7版本

    1.Linux安裝jdk 1.如果電腦沒有wget命令的,先使用yum安裝wget命令. eg: yum install wget 2.安裝好后就可以直接使用wget命令去下載jdk. 附:打開官網連 ...

  9. 计算机网络协议OSI TCP/IP协议--001

    网桥:连接同构的LAN的网络互联设备,(同构的LAN 网是,应用层到逻辑层)   实  现的功能是:MAC子层和物理层.1.帧的发送与接收.2.缓冲的管理.3.协议转换. 路由器:在网络层实现互联,他 ...

  10. HTML5 读取上传文件(转载)

    另参考 http://www.jianshu.com/p/46e6e03a0d53 1 filelist对象与file对象: <input type="file" id=&q ...