近些天在阅读《你不知道的JavaScript》的过程中,解决了一个一直以来都很困惑的问题:this指向。
这篇博客用来记录JavaScript中this的绑定规则备忘。
绑定规则
默认绑定
function foo() {
console.log(this.a);
}
var a = 2;
foo(); // 2
上面这段代码中,foo()
中的this
指向了全局对象(window
),原因是什么呢?
我们看到foo()
被调用的位置,很显然是在全局作用域下被调用,没有使用任何修饰,因此只能使用默认绑定规则,指向全局对象。
foo()
是运行在非严格模式下的,若是运行在严格模式下,则全局对象无法使用默认绑定规则,this
就会被绑定到undefined
function foo() {
"use strict";
console.log(this.a);
}
var a = 2;
foo(); // TypeError: this is undefined
隐式绑定
function foo() {
console.log(this.a);
}
var obj1 = {
a:2,
foo:foo
}
obj1.foo(); // 2
当函数引用存在上下文对象时,隐式绑定规则会将函数调用中的this
绑定到这个上下文对象。
这句话的意思是什么呢?
就是函数调用时是哪个对象的属性,那么函数中的this
就指向这个对象。
假定我们还有一个obj2
对象:
var obj2 = {
a:5,
foo:foo
}
obj2.foo(); // 5
则函数foo()
输出的结果就为5
。
隐式绑定存在一个问题:隐式丢失
隐式丢失
function foo() {
console.log(this.a);
}
var obj = {
a:2,
foo:foo
}
var a = 'global';
var bar = obj.foo;
bar(); // global
为什么这里输出的是global
?bar
不是obj.foo
的引用吗?
原因是bar
只引用了函数foo()
本身,仅此而已,并没有将上下文对象也包括在内。
类似的情况也经常发生在回调函数中,例如:
setTimeout(obj.foo, 100); // global
如何解决隐式丢失问题呢?接下来介绍到的显式绑定就能比较方便地解决这个问题。
显式绑定
JavaScript中提供了三个可以实现this
绑定的方法,分别是call()
、apply()
和bind()
。
function foo() {
console.log(this.a);
}
var obj = {
a:2,
foo:foo
}
var a = 'global';
var bar = foo.bind(obj);
bar(); // 2
function foo() {
console.log(this.a);
}
var obj = {
a:2,
foo:foo
}
var a = 'global';
foo.call(obj); // 2
apply()
方法的使用方式与call()
方法类似,两者的区别就在于接收参数的方式不同,前者是以数组的形式接收,而后者是按顺序逐个接收。
至于三个方法的详细区别及用法,可以移步这里:https://juejin.im/post/582bcd36d203090067edb8a0
三个方法都能手动改变this
的指向。
new绑定
使用new
来调用函数,或者说发生构造函数调用时,会自动执行下面的操作:
- 创建一个全新的对象。
- 这个新对象会被执行原型连接。
- 这个新对象会被绑定到函数调用的
this
。 - 如果函数没有返回其他对象,那么
new
表达式中的函数调用会自动返回这个新对象。
上面这段描述可能看了很懵逼,我们来看个例子就明白了:
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 2
使用new
来调用foo()
是,会构造一个全新的对象并将其绑定到foo()
调用中的this
上。
以上就是所有关于JavaScript中this
指向问题的绑定规则,后续关于绑定优先级的问题,将会另写一篇博客记录。