this全面解析(二)
在上一节中我们详细介绍了this的两种绑定方式,默认绑定
和隐式绑定
,在这一节我们继续介绍this的另外两种绑定方式显示绑定
和new绑定
。那么,我们要解决的问题当然就是上一节中我们提到的:this丢失!
显式绑定
在隐式绑定中,我们必须在一个对象的内部包含一个指向函数的属性,并通过这个属性间接引用函数,从而把this间接绑定到这个对象上。那么如果我们不想在每个对象内部包含函数引用,而想在每个对象上强制调用函数,该怎么做呢?
这时就需要 call(绑定this, 其他参数...)
和apply(绑定this, 其他参数...)
方法出场了,这两个方法的第一个参数都是给this准备的,不同之处在于其他参数的形式上,他们两的其他参数对比如下:
call(绑定this, "参数1","参数2","参数3","参数4");
apply(绑定this, ["参数1","参数2","参数3","参数4"]);
apply的其他参数是以数组序列形式存在的,它会在执行时将其解析成单个的参数再依次的传递到调用的函数中,这有什么用处呢?加入我们有一个数组:
var arr = [1,2,3,4,5,6];
现在我要找到其中的最大值,当然这里有很多方法了。既然这里讲到apply那么我们就用apply方法来解决这个问题。如果想要找到一组数中最大的一个,有一个简单的方法,使用Math.max(...)。但是,该方法并不能找出一个数组中的最大值,也就是说:
Math.max(1,2,3,4,5); // 可以找到最大值5
Math.max([1,2,3,4,5]); // 这就不行了,因为不接受以数组作为参数
我们的做法就是通过:
Math.max.apply(null, [1,2,3,4,5]); //得到数组中的最大值5
还有很多其他方面的用处,比如push等等,似乎有点跑题了!!!
不过我想说的就是通过call()和apply()这两种方法我们可以显式的绑定this到指定的对象!
function foo(){console.log(this.a);}var obj = {a: 2}foo.call(obj);//2
但是,显式绑定仍旧无法解决this丢失绑定的问题。
硬绑定
显式绑定的一个变种可以解决这个问题。
function foo(){console.log(this.a);}var obj = {a: 2}var bar = function(){foo.call(obj);}bar();// 2setTimeout(bar, 100); // 2bar.call(window); // 2
看看它是如何工作的:我们创建了一个函数bar(),并在他的内部手动调用foo.call(obj)。因此,强制把foo的this绑定到了obj,无论之后如何调用函数bar,它总会手动在obj上调用foo。这样的形式我们称之为硬绑定
。
硬绑定的典型应用场景就是创建一个包裹函数,负责接收参数并返回值:
function foo(something){console,log(this.a, something);return this.a + something;}var obj = {a: 2}var bar = function(){return foo.apply(obj, arguments);}var b = bar(3); //2, 3console.log(b); //5
另一个常用的方法是创建一个可以重复使用的辅助函数:
function foo(something){console,log(this.a, something);return this.a + something;}//简单的辅助函数function bind(fn, obj){return function(){return fn.apply(obj, arguments);}}var obj = {a:2}var bar = bind(foo, obj);var b = bar(); //2, 3console.log(b); //5
硬绑定是一种非常常用的模式,所以ES5提供了内置的方法Function.prototype.bind,它的用法如下:
function foo(something){console,log(this.a, something);return this.a + something;}var obj = {a:2}var bar = foo.bind(obj);var b = bar(3); //2, 3console.log(b); //5
bind(...)会返回一个硬编码的心函数,它会把指定的参数设置为this的上下文并调用原始函数。
new 绑定
第四条规则,也是最后一条规则,在讲解他之前我们首先要澄清一个非常常见的关于javascript中函数和对象的误解。
在传统的面向类的语言中,“构造函数”是类中的一些特殊方法,使用new初始化类是会调用类中的构造函数。通常的形式是这样:
someThinges = new MyClass(...)
javascript中也有个new操作符,但javascript中的new操作符的机制与面向类的语言完全不同。首先我们重新定义一下JavaScrit中的“构造函数”。在Javascript中,构造函数只是一些使用new操作符时被调用的函数。它并不会属于某个类,也不会实例化一个类。实际上它甚至都不能说是一种特殊的函数类型,它们只是被new操作符调用的普通函数而已。
举例来说,思考一下Number()作为构造函数时的行为,ES5.1中这样描述它:
Number构造函数
当Number在new表达式中被调用时,它是一个构造函数:它会初始化新建的对象。
所以,包括内置对象函数在内的所有函数都可以用new来调用,这种函数被称为构造函数调用,这有个非常细微的区别:实际上并不存在所位的“构造函数”,只有对于函数的“构造调用”。
使用new来调用函数,会自动执行下面的操作:
创建一个全新的对象
这个新对象会被执行[[prototype]]连接(之后会细说)
这个新对象会绑定到函数调用的this
如果函数没有返回其他对象,那么new表达式中的函数会自动返回这个对象。
function foo(a){this.a = a}var bar = new foo(2);console.log(bar) // foo {a: 2}console.log(bar.a); //2
使用new 来调用foo(...)时,我们会构造一个新的对象,并把它绑定到foo(...)调用中的this上。new是最后一种可以影响函数调用时this绑定行为的方法。我们称之为new绑定。
箭头函数
我们之前介绍的四条规则已经可以包含所有正常是有的函数。但是在ES6中介绍了一种无法使用这些规则的特殊函数类型:箭头函数
箭头函数不是使用function关键字定义的,而是使用“ => ”定义。箭头函数不使用this的四种标准规则,而是根据外层作用域(函数或全局)来决定this。
function foo(){//返回一个箭头函数return (a) =>{//this继承自foo()console.log(this.a);}}var obj1 = {a: 2}var obj2 = {a: 3}var bar = foo.call(obj1);bar.call(obj2); //2 不是3
foo()内部创建的箭头函数会捕获调用时foo()的this,由于foo()的this绑定到obj1,bar的this也会绑定到obj1上,而且箭头函数的绑定无法被修改!
箭头函数最常用与回调函数中,例如事件处理器或者定时器:
function foo(){setTimeot(()=>{//这里的this在词法上继承自foo(),也就是说只要foo()绑定到了obj1上,箭头函数的this也就绑定到了obj1上console.log(this.a)},100)}var obj1 = {a: 2}foo.call(obj1); //2
箭头函数可以像bind(..)一样确保函数的this被绑定到指定的对象,此外,其重要性还体现在他用更常见的词法作用域取代了传统的this机制。实际上在,ES6之前我们就已经使了用一种几乎和箭头函数完全一样的模式。
function foo(){console.log(this); //Object {a: 2}var self = this; //词法作用域捕获thissetTimeout(function(){console.log(this); // Window {external: Object, chrome: Object, document: document, obj1: Object, obj2: Object…}console.log(self.a);}, 100);}var obj1 = {a: 2}foo.call(obj1); //2
我分别在这段代码中foo()
的内部,和setTimeout()
的内部加了两行代码console.log(this)
,当调用foo()
函数并将其this绑定到obj1
上时(即执行foo.call(obj1)
),foo()
内的this此时是Object {a: 2}
,说明foo()
函数中的this已经绑定到了obj1
上,setTimeout()
内的结果是Window...
,如果你看了上一节《this全面解析(一)》的内容应该会很好理解,因为在setTimeout()
方法中,函数传参相当于隐式赋值,调用方式自然运用默认规则
,setTimeout()
方法中函数的this指向window
。为了让我们得到预期的结果,我们将foo()
中的this保存下来(即var self = this
),然后通过词法作用域的在setTimeout()
方法中的函数中引用self
变量。读者可以自行测试,如果不这样做得出的结果会是什么(undifined吗?自行验证一下吧!)
好吧!一不小心又啰嗦的讲了这么多。虽然,self = this和箭头函数看起来都可以取代bind(),但本质上来说,他们想取代的是this机制。
小结
如果要判断一个运行中函数的this绑定,就需要找到这个函数的直接调用位置。找到后就可以顺序应用下面这四条规则来判断this的绑定对象
是否由new调用?绑定到新创建的对象
是否由call()或apply()调用?绑定到指定的对象
是否由上下文对象调用?绑定到那个上下文对象
默认:严格模式undifined,非严格绑定到全局对象
ES6中的箭头函数不会使用四条标准的绑定规则,而是根据词法作用域来决定this,具体来说,箭头函数会继承外层函数调用的this绑定(无论this绑定到了什么),这其实和ES6之前代码中的self = this 机制一样。
this全面解析(二)相关推荐
- asp.net C#生成和解析二维码代码
类库文件我们在文件最后面下载 [ThoughtWorks.QRCode.dll 就是类库] 使用时需要增加: using ThoughtWorks.QRCode.Codec; using Though ...
- C语言文件操作解析(二)【转载】
http://www.cnblogs.com/dolphin0520/archive/2011/10/05/2199598.html C语言文件操作解析(二) C语言中对文件进行操作必须首先打开文件, ...
- C语言文件操作解析(二)
C语言文件操作解析(二) C语言中对文件进行操作必须首先打开文件,打开文件主要涉及到fopen函数.fopen函数的原型为 FILE* fopen(const char *path,const cha ...
- java生成二维码/java解析二维码
二维码的优缺点 优点:1. 高密度编码,信息容量大:2.编码范围广:3.容错能力强:4.译码可靠性高:5.可引入加密措施:6.成本低,易制作,持久耐用. 缺点:1.二维码技术成为手机病毒.钓鱼网站传播 ...
- opencv图像处理中的一些滤波器+利用滤波器提取条形码(解析二维码)+公交卡倾斜矫正+物体尺寸丈量
一般来说,图像的能量主要集中在其低频部分,噪声所在的频段主要在高频段,同时图像中的细节信息也主要集中在其高频部分,因此,如何去掉高频干扰同时又保持细节信息是关键.为了去除噪声,有必要对图像进行平滑,可 ...
- Unity的Json解析二–写Json文件
本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/50378805 作者:car ...
- 嵌入式使用Zbar解析二维码
引言 上篇文章介绍了使用Quirc解析二维码,Quirc是一个轻量级的二维码解析库,效率太低,不符合使用条件,作者使用Zbar来进行二维码的解析.Zbar算法是现在网上开源的条形码,二维码检测算法,这 ...
- 使用摄像头解析二维码,且可以生成含具体信息的二维码
开源一份早些时候写过的代码,关于二维码在PC端的应用. 二维码的具体应用范围,这里不详述. 之前项目主要用于朋友一个牙科的平台,不过后面没有采用,也就没有往后继续开发,大家要是觉得有意思,可以自己拿去 ...
- Android超方便 集成 Zxing实现扫一扫,闪光灯,生成二维码图片,解析二维码(条码)等功能
之前我写过一篇博客是关于如何将zxing集成到Android Studio中,以及简单的实现扫一扫功能. 详情请看:Android Studio集成Zxing扫一扫 但是,上面那篇博客只有有一个扫一扫 ...
- llqrcode.js识别二维码,解析二维码信息
llqrcode.js具有扫描二维码功能,用来进行从图片中识别二维码,可解析二维码的信息. 直接上代码 <!DOCTYPE html> <html> <head>& ...
最新文章
- RedHat8 配置本地yum源
- android 动态修改控件的宽高
- UI设计素材模板|设计良好的教育网站:3个快捷技巧
- sql获取某列出现频次最多的值_那些SQL里面踩过的坑
- 相关系数excel_《从EXCEL到PYTHON数据分析进阶指南》终结篇
- Windows2000资源工具包工具对于管理任务2
- python编程实战(三):暴力破解WIFI密码!亲测运行有效!
- BZOJ4198: [Noi2015]荷马史诗(哈夫曼树)
- 网站建设 之 CSS渐变
- Dell服务器的iDRAC虚拟控制台无法连接
- 递归解决卖桃子问题java
- 怎么关闭火狐浏览器的百度辅助模式(无障碍服务)
- android 外边距,外边距(padding)重叠的及解决办法
- RIPS-0.55 对securing fuction的检查方法
- red hat linux 9.0下载地址集合,Red Hat Linux 9.0 iso最新下载地址
- Vue+Echarts+百度地图 小例子
- 提供一些网上webservice的WSDL地址
- Qt的TQTreeWidget控件
- 彩色星球科技冠名著名钢琴艺术家吴牧野世界巡演《肖邦圆舞曲全集》长沙站演出...
- 55、【图】Dijkstra求最短路径(单源最短路径+边权重为正数)(C/C++版)
热门文章
- NumSharp  - Numerical .NET
- zabbix数据库分表的实现
- com.mysql.jdbc.PacketTooBigException,及mysql 设置 max_allow_packet
- Nginx日志配置及分割
- CentOS 7 安装无线驱动
- 【转】总结oninput、onchange与onpropertychange事件的用法和区别
- Excel 2010 打开后灰色
- [原创]Aster剧情介绍和简单评价
- extract-text-webpack-plugin
- 01 Angular