本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
相信大家在写JS函数的方法时,都用过this
关键字来给构造函数增加变量。但我们用this
时我们都疑惑过,this
到底绑定的是什么呢?this
关键字是JS中最复杂的机制之一。它是一个很特别的关键字,在我们使用它时,它便被自动定义在了所有函数的作用域中。下面我就来和大家聊聊this
的用法。
this的绑定规则
- 默认绑定
- this所处的词法作用域在哪里生效了,this就绑定在哪里。
- 在严格模式下,全局对象无法进行默认绑定,所以导致this只能绑定在undifined身上 我们来看这段代码:
function baz(){
console.log('baz');
bar()
}
function bar(){
console.log('bar');
foo()
}
function foo(){
console.log('foo');
console.log(this);
}
baz()
我们定义了三个函数foo
、bar
、baz
,我们在foo
函数中打印this
,再在bar
中调用foo
函数,再在baz
中调用bar
函数,最后我们执行baz
。因为这baz
函数是在全局对象window
上定义的,所以这里的打印的this
指代的就是全局对象window
。这便是是this
的默认绑定规则。但要注意的一点就是如果在baz函数中加入'use strict'
代码进入严格模式时,this
将无法默认绑定在全局对象中,导致this
只能绑定在undifind
身上.
,
- 隐式绑定
- 当函数引用有上下文对象时,隐式绑定的规则就会把函数调用中的this绑定到这个上下文对象。 我们来看这段代码
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo: foo
}
obj.foo()
我们定义了foo
函数、obj
对象。我们在obj
对象中定义foo:foo
键值对,将foo
函数在obj
对象中引用,之后我们在来在外面执行foo
函数。在这种情况下,我们this.a
打印的值就是2
了。这就是因为当我们在obj
对象引用foo
函数时,this
会隐式绑定在这个有执行上下文的obj
对象上。所以当我们执行foo
函数时,this.a
自然就找到了obj
对象中定义的a
。这便是是this
的隐式绑定规则。
- 隐式丢失
- 当隐式绑定的函数丢失了绑定对象,就会应用默认绑定。 我们来看这段代码
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
}
var bar = obj.foo
var a = 'global'
bar()
我们定义了foo
函数、obj
对象,但在外面我们又定义bar
赋值obj.foo
,之后我们在调用bar
执行foo
函数。在这种情况下this.a
打印出来结果就是global
。那为啥这里打印不是2
呢?因为我们在obj
对象中引用foo
函数后,obj
并没有执行foo
函数,而是把它又丢给了bar
,让bar
变量执行了foo
函数。this
的隐式绑定就丢失了对象,从而又默认绑定在了window
上了。自然在打印this.a
时,this
找到的a
就是在全局定义的a
了。这便是this
的隐式丢失规则。
- 显式绑定
- call,apply,bind方法可以强行指定函数的this对象 我们看这段代码:
function foo(){
console.log(this.a);
}
var obj = {
a:2,
}
foo.call(obj)
||foo.apply(obj)
||var bar = foo.bind(obj)
bar()
我们定了foo
函数、obj
对象,如果现在直接调用foo
函数,打印出来的a
就是undifind
。但是如果我们使用foo.call(obj)
|| foo.apply(obj)
|| foo.bind(obj)
就可以强行将this
指代的对象改为obj
。让this.a
打印出2
。这便是this
的先绑定规则。要注意的一点就是使用bind
方法时返回的是个新的对象,要再次调用bar
。
小结
在我们使用this
指代对象时,我们要分清到底是调用还是引用。只要我们能搞清this
到底在什么地方引用了又在设么地方调用了。我们就可以清楚的知道this
指代的对象到底是谁。自然也就能灵活运用this
。