深入学习 JavaScript —— this 绑定
栏目: JavaScript · 发布时间: 6年前
内容简介:这里直接列出 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
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Learn Python the Hard Way
Zed Shaw / Example Product Manufacturer / 2011
This is a very beginner book for people who want to learn to code. If you can already code then the book will probably drive you insane. It's intended for people who have no coding chops to build up t......一起来看看 《Learn Python the Hard Way》 这本书的介绍吧!