一篇文章带你搞懂js的this指向

24 阅读4分钟

一、this的四种绑定方式

1.默认绑定

1.非严格模式

  1. 函数独(普通函数)立调用时,this指向全局对象(浏览器指向window,node指向global)
function fun() {
    console.log(this)
}
fun() //window
  1. 对象的方法调用时,this指向调用该方法的对象
const obj = {
    name: "张三",
    fun() {
        console.log(this)
    }
}
obj.fun() //obj
  1. 杯全剧变量引用时,this指向全局对象
const obj2 = {
    fun() {
        console.log(this)
    }
}

const fun2 = obj2.fun
fun2() //window
  1. 闭包调用时,this指向全局对象
const obj3 = {
  bar: function () {
    return function () {
      console.log(this)
    }
  }
}
obj3.bar()() // window
  1. script 标签中,this指向全局对象(无论是严格模式还是非严格模式)

2.严格模式

  1. 函数独立(普通函数)调用时,this指向undefined
  2. 对象的方法调用时,this指向调用该方法的对象
  3. 全局变量引用时,this指向undefined
  4. 闭包调用时,this指向undefined
  5. script 标签中,this指向undefined

2. 隐式绑定

对象的方法是对另一个函数的引用,当调用这个方法时,this指向这个对象

function foo() {
  console.log(this)
}

const  obj1 = {
  name: 'obj1',
  foo: foo
}
obj1.foo()

const  obj2 = {
  name: 'obj2',
  bar: function () {
    console.log(this)
  }
}
obj2.bar()

const  obj3 = {
  name: 'obj3',
  baz: obj2.bar
}
obj3.baz()

3.显式绑定

1.call

function fun() {
    console.log(this)
}
fun.call({name: "张三"}) //{name: "张三"}

2.apply

function fun() {
    console.log(this)
}
fun.apply({name: "张三"}) //{name: "张三"}

3.bind

function fun() {
    console.log(this)
}
const fun2 = fun.bind({name: "张三"})
fun2() //{name: "张三"}

4. new 绑定

函数访问this,如果使用new调用,this指向新创建的对象 class 定义的类,new调用时,this指向新创建的对象两者类似

function Student () {
    console.log(this)
}
new Student() //Student {}
class Student {
    constructor() {
        console.log(this)
    }
}
new Student() //Student {}

二、this指向的优先级

1.new 关键字 (优先级最高)

new 关键字调用函数生成对象时,this一定指向新创建的对象,这一点是无法被改变的,尽管使用显示绑定函数去主动绑定this,也无法改变this指向。

2.显示绑定 (优先级极高)

使用call、apply、bind绑定this,this指向绑定对象,这个方式制定this优先级方式极高。且只会在new关键字调用函数时失效。

3.隐式绑定 (优先级较高)

对象的方法调用时,this指向调用该方法的对象,这个方式制定this优先级方式较高。

4.默认绑定 (优先级较低)

函数独立调用时,this指向全局对象,这个方式制定this优先级方式较低。

三、 特殊情况

1.箭头函数

箭头函数本作用域内是没有this的,在箭头函数中访问this,会沿着作用域链向上级作用域中查找this,直到找到为止。

const obj = {
    fun: () => {
        console.log(this)
    }
}

obj.fun() //window

2. 显示绑定特殊值

显示绑定特殊值null,undefined时,this指向绑定不会生效,this会指向全局对象。但是在严格模式下,显示绑定就会生效。

function fun() {
    console.log(this)
}
fun.call(null) //window
fun.call(undefined) //window
'use strict'
function fun() {
    console.log(this)
}
fun.call(null) // null
fun.call(undefined) // undefined

3. 间接函数引用 (最没有想到的一种)

const obj = {
    fun() {
        console.log(this)
    }
}
const obj2 = {
    fun2: obj.fun
}
obj2.fun() //obj2
(obj2.fun2 = obj.fun)() // window

(obj2.fun2 = obj.fun)返回的是函数本身,所以函数独立调用时,this指向全局对象。 但注意,这个表达式有可能会出现变量提升的问题,是否出现取决于obj.fun的声明方式,如果obj.fun 是普通函数,那么会出现变量提升,如果obj.fun 是箭头函数,那么不会出现变量提升。

总结

基于以上的分析,我们可以简单的得出this指向简单记忆方式

  1. script 标签中,this指向全局对象
  2. 函数作用域中,this指向函数调用对象,如果函数独立调用会出现特殊情况(严格模式指向undefined,非严格指向全局)
  3. 箭头函数中,this指向会沿着作用域链向上级作用域中查找this,直到找到为止。
  4. new 关键字调用函数生成对象时,this一定指向新创建的对象,这一点是无法被改变的,尽管使用显示绑定函数去主动绑定this,也无法改变this指向。
  5. 使用call、apply、bind绑定this,this指向绑定对象,这个方式制定this优先级方式极高。且只会在new关键字调用函数时失效。且如果绑定的是null,undefined时会出现特殊情况(严格模式正常,非严格模式指向全局)

在分析this指向时更加关注这个this所在的作用域,所在的函数是被谁调用的,是否是严格模式等等问题。