深入学习 JavaScript —— this 绑定
栏目: JavaScript · 发布时间: 5年前
内容简介:这里直接列出 this 绑定的四大规则,这些规则都是《你不知道的JavaScript》一书中提到的关键词,个人觉得比较好理解。因为这里只涉及常识性的介绍,如果已经了解的话可以快速跳过,进入下一小节。如果你还不熟悉,可能会碰到很多陌生又让你心痒的概念。这里建议你先暂时放下,我会在后文详细说明,或贴上我觉得不错的博客。又可以叫case1.1.1
这里直接列出 this 绑定的四大规则,这些规则都是《你不知道的JavaScript》一书中提到的关键词,个人觉得比较好理解。因为这里只涉及常识性的介绍,如果已经了解的话可以快速跳过,进入下一小节。如果你还不熟悉,可能会碰到很多陌生又让你心痒的概念。这里建议你先暂时放下,我会在后文详细说明,或贴上我觉得不错的博客。
- 默认绑定
- 隐式绑定
- 显式绑定
- new绑定
默认绑定
又可以叫 函数调用
。正如其名,它指的是在没有其它规则的情况下默认使用的绑定规则,一般就是直接调用函数的情况。在一般情况下,默认绑定的对象是全局对象 window
;当函数内部处于严格模式下时,它绑定的是 undefined
。
case1.1.1
// 这里是通常情况下的默认绑定 var a = 0; function foo() { var a = 10; console.log(“a=” + this.a); // window.a } foo(); // a=0 复制代码
case1.1.2
// 严格模式下的默认绑定(注意 use strict 位置) var a = 0; function foo() { "use strict"; var a = 10; console.log(“a=” + this.a); // window.a } foo(); // a=0 复制代码
可以看到,这里的 this.a
绑定的对象是全局对象。
隐式绑定
隐式绑定又可以叫 对象方法调用 ,即作为对象方法使用。既然是对象调用,那么就少不了对象的使用。具体的使用情况如下:
case1.2
var a = 0; var b = { a: 10, foo: function() { var a = 20; console.log("a=" + this.a); // b.a } }; b.foo(); // a=10 复制代码
其实挺好理解,这里面 this 的绑定对象是 b。但是,使用隐式绑定时,我们仍然很容易踩到一些坑,这个坑后文会提到。
显式绑定
显式绑定看似和上面的隐式绑定相对应,其实没太大关系。它又可以叫做 apply/call 调用 ,其实就是使用了 JavaScript 中很强大的两个方法。这两个方法使用效果,简而言之是强行为所指定函数绑定所指定对象。这里贴上相关的补充链接,不过即使你不看也不影响浏览本文: JS 中的 call、apply、bind 方法详解
假如你差不多了解清楚了,看看下面的例子。
case1.3
var a = 0; var b = { a: 10 }; var c = { a: 20, // c.a === 20 foo: function() { console.log("a=" + this.a); } }; c.foo.call(b); // 为foo的this指定绑定对象b // a=10 复制代码
上面是显式绑定的一个例子。可以看到,首先 foo
是由 c
作为对象方法进行调用的,然后使用了 call
方法显式绑定到 b
上,最后的输出结果就如上。大家也可以猜到,这里显式绑定的优先级高于隐式绑定,至于其它几种组合方式,也等到后文再详细介绍。
new 绑定
也叫 构造函数调用 。不过,对于 JS 的构造函数,不熟悉的同学千万别将之与 C/Java 之类的构造函数相提并论,两者实质是完全不同的(虽然下例看起来好像很相似)!这里先上代码,更深层次的内容后文再说~
case1.4
var a = 0; function Foo() { this.a = 10; // 当作为构造函数时,这里可以看作 a: 10 的声明形式 } var foo = new Foo(); console.log(foo.a); // foo 此时是一个对象而不是函数 // 10 console.log(Foo.a); // 将 Foo 作为对象调用试试看? // undefined 复制代码
看起来和上面那三种毫无联系,实际上它的原理与显式绑定紧密相关。如果你看到这里还没懵的话,那就接着下一节看吧~(如果懵的话,可以将上面四个示例在codepen 敲一遍)
剖析 this 绑定
上面总结的四个绑定规则,在实际应用中有各种各样的变式出现。如果对这些意外情况进行仔细分析,也能帮助我们更好地理解 JS。
绑定优先级和 new 绑定原理
上文其实部分涉猎了绑定优先级的问题。很显然,默认绑定的优先级一定是最低的;在 case1.3 中,我们又知道了显式绑定优先级高于隐式绑定。但是,我们如何确定 new 绑定的优先级呢?
在 case1.4 中,我们了解到构造函数 new 返回的是一个对象,但需要注意的是, 这个返回对象是新构造的,即不与原来的任何函数或对象产生联系 。怎么理解这一点呢?看下面这个例子
case2.2.1
var obj = { a: 10, foo: function(a) { this.a = a; } } obj.foo(20); console.log(obj.a); // 20 var bar = new obj.foo(30); console.log(bar.a); // 30 console.log(obj.a); // 20 复制代码
case2.2.1 可以印证之前所说的。同时,通过这个例子我们也可以看到,对于 bar
接受的返回值来说,new 绑定的优先级高于隐式绑定。
那么,new 绑定与显式绑定的优先级如何对比呢?《你不知道的 JavaScript》一书提到一个例子,但这个例子个人觉得还不足以说明问题,所以这里将原文的例子进行了改进和补充。
case2.2.2
function foo(a) { this.a = a; } var obj = {}; var bar = foo.bind(obj); // bind 和 apply/call 功能类似,但返回的是一个函数引用 bar(2); // 执行了 this.a = 2 console.log(obj.a); // 2 var baz = new bar(3); // 将显式绑定返回的函数作为构造函数使用,看看结果怎样? console.log(obj.a); // 前面提到的,构造函数返回的对象不影响原来的对象或函数 // 2 console.log(baz.a); // 这里说明,new 绑定的结果不受显式绑定的影响 // 3 bar(4); // 假如再执行一次呢?如果无效,说明 new 绑定优先级比较高 console.log(obj.a); // 这里说明,两种绑定实际上互不干扰 // 4 复制代码
其实上面绕来绕去的,理解问题的一个关键就是 new 绑定的原理到底是什么?这是一个写起来又能撑一篇的问题,老惯例,贴出我认为写得比较好的博客。
绑定丢失和 this 底层原理
这是《你不知道的 JavaScript》中提到的例子,这里将之收集起来,做一个统一汇总。
case2.1.1
var b = { a: 10, foo: function () { console.log(this.a); } } var fn = b.foo; fn(); // undefined 复制代码
case2.1 中,fn 引用的是一个 foo 函数,而不是 b.foo 的对象方法。也就是说,在赋值操作后,foo 所处的环境是全局域而不是 b 的词法作用域内。
case2.1.2
function foo() { console.log(this.a); } var a = 2; var o = {a: 3, foo: foo}; var p = {a: 4}; o.foo(); // 3 (p.foo = o.foo)(); // 2 复制代码
case2.1.2 的原因与 case2.1.1 是一样的。看到此处,有些人可能对 JS 中奇怪的各种作用域产生疑惑, 为什么一个赋值操作会改变所处作用域呢?
这里涉及的问题其实是 JS 的内存数据结构设计的问题。具体的可以点下面链接,看完这个链接,相信你之前的一些疑惑都会得到解答。
后记
这篇文章攒了比较久,说实话自己想写的很多点都还没能写出来。一方面是因为避免写得太杂,所以有一些问题我会贴出博客,如果还是不能解决可以私信我;另一方面也是能力有限,即使只是做一些整理性的工作,仍然耗费了我大量的时间,而且再拖下去也没太大意义。所以,这篇我认为还算有深度,勉强算原创的文章就写到这吧。
今天是劳动节,作者没有放假而是一下午肝了这篇,因为这算是上周欠下的了。同时,我计划会在劳动节假期再更一篇,要写哪些方向已经想好了,到时候会从下面两条中挑一条(我希望我能挑战好第二条,所以给个鼓励吧~)
- 原型链
- 从闭包、this绑定角度、原型链剖析 bind、jQuery 事件绑定机制以及柯里化
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 深入解析Cocos Creator JSB绑定原理以及应用实践
- 深入理解es6读后总结--块级作用域绑定
- Golang Echo数据绑定中time.Time类型绑定失败
- 如何在Symfony的表单中添加一个未绑定字段,否则绑定到一个实体?
- js双向绑定
- 延迟静态绑定——static
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Go程序设计语言
艾伦 A. A. 多诺万 / 李道兵、高博、庞向才、金鑫鑫、林齐斌 / 机械工业出版社 / 2017-5 / 79
本书由《C程序设计语言》的作者Kernighan和谷歌公司Go团队主管Alan Donovan联袂撰写,是学习Go语言程序设计的指南。本书共13章,主要内容包括:Go的基础知识、基本结构、基本数据类型、复合数据类型、函数、方法、接口、goroutine、通道、共享变量的并发性、包、go工具、测试、反射等。 本书适合作为计算机相关专业的教材,也可供Go语言爱好者阅读。一起来看看 《Go程序设计语言》 这本书的介绍吧!