this

JavaScript中的this关键字一直都是学习JS的一个难题,我们往往不能准确的理解this在上下文中的含义,往往导致一些问题的发生。对于this关键字,我们需要知道的是:this即不指向函数自身也不指向函数的词法作用域,this在函数调用时被绑定,也就是说,它指向什么完全取决于函数在哪里被调用。

绑定规则

在探讨具体的绑定规则时,我们先来看一个函数调用位置的例子:

1
2
3
4
5
6
7
8
9
10
11
12
function foo() {
console.log('call foo');
bar();
}
function bar() {
console.log('call bar');
baz();
}
function baz() {
console.log('call baz');
}
foo();

我们声明了三个函数,相互嵌套。现在来寻找各个函数的调用位置,foo在全局作用域中被调用,bar的调用位置则是

1
2
3
4
5
6
7
8
9
10
> 下面来看看几种一般性的绑定规则

#### 默认绑定
看一个例子:
```javascript
var name = 'limoer';
function showName(){
console.log(this.name);
}
showName(); //limoer

这里

1
2
3
4
5
6
7
8
9
10
11
12
13
#### 隐式绑定
隐式绑定的规则是调用位置是否有上下文对象,即是否被某个对象拥有或者包含。

来看一个例子:
```javascript
function showName(){
return this.name
}
var obj = {
name: 'limoer',
msg: showName
}
obj.msg(); // limoer

对于函数showName而言,其在obj对象中被引用,即函数此时引用有上下文的对象,此时使用隐式规则,this将被绑定到这个上下文对象,所以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

两点注意:
(1)属性引用链只有或则说是最后以层才会影响调用位置,修改下上面的例子:
```javascript
function showName(){
return this.name
}
var obj = {
name: 'limoer',
msg: showName
}
var obj2 = {
name: 'lindo',
obj: obj
}
obj2.obj.msg(); //limoer

(2)this的绑定与函数的调用位置息息相关,一个常见的问题就是被隐式绑定的函数会丢失绑定对象,即应用了默认绑定规则。

1
2
3
4
5
6
7
8
9
10
var name = 'lindo';
function showName() {
console.log(this.name);
}
var obj = {
name: 'limoer',
msg: showName
}
var show = obj.msg;
show(); // lindo

函数show虽然函数showName的一个引用,但是由于其指向的是函数的本身,此时bar是一个不带任何修饰的函数调用,应用了默认绑定规则。

显式绑定

和隐式绑定相反,显式绑定是一种“强制性”手段,把this绑定到某个对象上,JavaScript提供了这样的函数,call()&apply(),这两个函数是如何使用的呢?它们的第一个参数是一个对象,它们会把这个对象绑定到 this,接着在调用函数时指定这个 this。

1
2
3
4
5
6
7
var obj = {
msg: 'inner'
}
function showMsg(){
console.log(this.msg);
}
showMsg.call(obj)

call 和 apply方法的区别仅仅是参数上的问题,除了第一个参数外,apply可以使用数组来传入剩余参数,而call则是以多参数的形式写出来。除了call和apply以外,还有一个bind方法,它接收一个对象,返回绑定了该对象的这个函数,此种方法是硬绑定,也就意味着,绑定不可更改,这里不再多做介绍。

new 绑定

这是最后一种绑定方式,即“构造函数绑定”。会在将new关键字的时候在阐述此种绑定。

小结

如果要判断一个运行中函数的 this 绑定,就需要找到这个函数的直接调用位置。找到之后
就可以顺序应用下面这四条规则来判断 this 的绑定对象。

  1. 由new调用?绑定到新创建的对象。
  2. 由call或者apply(或者bind)调用?绑定到指定的对象。
  3. 由上下文对象调用?绑定到那个上下文对象。
  4. 默认:在严格模式下绑定到undefined,否则绑定到全局对象。
    一定要注意,有些调用可能在无意中使用默认绑定规则。如果想“更安全”地忽略 this 绑 定,你可以使用一个DMZ对象,比如ø = Object.create(null),以保护全局对象。
    ES6 中的箭头函数并不会使用四条标准的绑定规则,而是根据当前的词法作用域来决定 this,具体来说,箭头函数会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。这 其实和ES6之前代码中的self = this机制一样。
分享到 评论