JS语言的一座小山坡——‘this’

875 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

相信大家在写JS函数的方法时,都用过this关键字来给构造函数增加变量。但我们用this时我们都疑惑过,this到底绑定的是什么呢?this关键字是JS中最复杂的机制之一。它是一个很特别的关键字,在我们使用它时,它便被自动定义在了所有函数的作用域中。下面我就来和大家聊聊this的用法。

this的绑定规则

- 默认绑定

  1. this所处的词法作用域在哪里生效了,this就绑定在哪里。
  2. 在严格模式下,全局对象无法进行默认绑定,所以导致this只能绑定在undifined身上 我们来看这段代码:
function baz(){
    console.log('baz');
    bar()
}
function bar(){
    console.log('bar');
    foo()
}
function foo(){
    console.log('foo');
    console.log(this);
}
baz()

我们定义了三个函数foobarbaz,我们在foo函数中打印this,再在bar中调用foo函数,再在baz中调用bar函数,最后我们执行baz。因为这baz函数是在全局对象window上定义的,所以这里的打印的this指代的就是全局对象window。这便是是this的默认绑定规则。但要注意的一点就是如果在baz函数中加入'use strict'代码进入严格模式时,this将无法默认绑定在全局对象中,导致this只能绑定在undifind身上. , 屏幕截图(75).png

- 隐式绑定

  1. 当函数引用有上下文对象时,隐式绑定的规则就会把函数调用中的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的隐式绑定规则。

屏幕截图(77).png

- 隐式丢失

  1. 当隐式绑定的函数丢失了绑定对象,就会应用默认绑定。 我们来看这段代码
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的隐式丢失规则。

屏幕截图(76).png

- 显式绑定

  1. 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

屏幕截图(78).png

小结

在我们使用this指代对象时,我们要分清到底是调用还是引用。只要我们能搞清this到底在什么地方引用了又在设么地方调用了。我们就可以清楚的知道this指代的对象到底是谁。自然也就能灵活运用this