JavaScript中的this到底指向谁?一文带你读懂

334 阅读5分钟

前言

什么是this关键字

在JavaScript中,this是最复杂的机制之一,会被自动定义在所有函数的作用域中,且它代表了当前执行上下文中的对象。
在本文中,Virtual09将会为大家解读this的指向问题,希望大家能够喜欢!

正文

this就是指向函数自己吗?

根据this其本身中文翻译的角度来说,确实会很容易被理解成为指向函数自己。下面我们看一下一个非常经典的例子

function foo(num){
    console.log("foo" + num);
    this.count++
}

foo.count = 0

var i;
for(i = 0;i<10;i++){
    if(i>5){
        foo(i)
    }
}
console.log(foo.count);

我们手动给函数foo添加了一个count的属性,如果按照对this的字面意思理解,这个this.count就是指向foo.count,所以打印结果是多少呢?是foo.count = 4的结果吗?

image.png

但是答案是0。也就是说这个this.count并没有指向到foo.count,所以仅仅靠字面理解是错误的。由此this关键字是不单纯的理解为指向函数自己。 那么我们如何可以让这里的this指向函数foo呢?这时就要介绍一下this的显示绑定了.

this的显示绑定

function.call(thisArg,arg1,arg2...)

call()中的第一个参数是指在函数调用时,this关键字所绑定的对象,thisArg会决定函数内部的this值。arg1是函数

function foo(num){
   console.log("foo" + num);
   this.count++
}

foo.count = 0

var i;
for(i = 0;i<10;i++){
   if(i>5){
       foo.call(foo,i)
   }
}
console.log(foo.count);

所以这里我们显示绑定了函数对象foo这时this.count就会指代的是foo.count

image.png

function.apply(thisArg,[argsArray])

apply()的使用方法和call()类似,不同的是,它的参数是以一个数组的形式传递。我们来看看代码

var obj = {
    a: 1
  }
  
  function foo(x, y) {
    console.log(this.a, x + y);
  }
  foo.apply(obj, [1, 2])

这里我们函数foothis显示绑定在了对象obj上面,接收参数[1,2]也就是说x=1,y=2,这样子.执行结果如下

image.png

function.bind(thisArg,args1,args2...)

使用bind()会返回一个新的函数,当这个新函数被调用时,它的this会被绑定到指定的thisArg上,同时也可以接受一些参数

var obj = {
    a: 1
  }
  
  function foo(x, y) {
    console.log(this.a, x + y);
  }
  var bar = foo.bind(obj, 3, 4)
  bar(2)

这里我们创建一个新的变量bar来接受bind()返回的新函数,并将新函数的this绑定在obj上。且bin()返回的新函数长得和调用者函数一样,这里我们使用的是foo调用,所以bind()返回的函数也和foo长得一样。执行结果如下:

image.png

this的隐式绑定

在JavaScript中,隐式绑定(Implicit Binding)指的是当一个函数作为某个对象的方法被调用时,函数内部的this值会自动绑定到该对象。

var obj = {
  a: 1,
  foo: function() {
    console.log(this.a); // obj
  }
}

obj.foo()

这里我们在对象obj中声明一个函数,并调用,这个函数本身的this就会被隐式绑定在对象obj上。具体来说当函数引用有上下文对象时,隐式绑定和规则会把函数调用中的this绑定到该上下文对象中,也就是说,在上述代码中foo的上下文对象就是obj,当foo被调用时,this就会被绑定在obj中,这里的this.a就是obj.a.

隐式丢失

this的隐式调用中存在一种情况被称为隐式丢失,也就是当一个函数被多个对象链式调用时,函数的this指向就近的那个对象,下面我们看看隐式丢失。

var obj = {
    a: 1,
    b: function() {
      foo()
    }
  }
  function foo() {
    console.log(this.a)
  }
  obj.b()

那么什么是链式调用呢?链式调用(chaining)是指在一个对象的方法调用之后返回该对象本身,以便可以继续调用该对象的其他方法。这里我们让函数b调用的是函数foo,但是函数b又是由对象obj调用,这样也就形成了链式调用。所以这里我们这里打印结果并不是1,而是undefined;

image.png

New绑定

在JavaScript中也存在new操作符,使用方法看起来也跟面向类的语言一样,绝大部分人会认为JavaScript中的new机制和其他语言一样,但是实事就是JavaScript中的new和其他语言完全不同。对于new来调用函数的执行过程是这样的。首先new调用函数会先创建一个全新的对象,那么这个新对象会通过原型链连接,这个新对象会绑定到函数调用的this.

function Person() {
    this.name = '曹总'
  }
  let p = new Person()
  console.log(p.name);

在上述代码中,我们使用new调用函数Person(),创建了一个新的实例对象p然后通过原型链的关系 p.__proto__ = Person.prototype,所以在新创建的p.name会等于this.name = '曹总'. 事实上new绑定的规则是由原型链绑定的。

箭头函数

箭头函数没有this这个机制,写在箭头函数中的this也是它外层非箭头函数的this.通俗来讲箭头函数中的this并不是指向它自己的,而是指向的是外层的,只要包裹的它的函数不是箭头函数,那么箭头函数的this就是指向它的外层函数.

var obj = {
    a: 1,
    b: function() {
      const fn = () => {
        console.log(this.a);
      }
      fn()
    }
  }
  obj.b()

我们来看看这串代码,fn是一个箭头函数,它的this会指向的是b(),然后b()又会通过隐式绑定,让this指向对象obj,所以打印结果是:

image.png

文章到此也就结束了,希望对大家有帮助,若有不足,恳请指出!!谢谢大家!