this的指向总结

183 阅读3分钟

关于this

关于this的指向问题,很多像我一样从事前端工作不是很久的小伙伴总是一头雾水,在使用时分不清this到底指向什么。最近一段时间我下定决心好好学习一下this,在此进行一些记录。

一句话概括this的指向

通常,我们对this的指向描述为,谁调用this,他就指向谁。这句话表述起来没什么问题,但是不够规范和全面。我们用另一种方式来表达:

this的指向,是在调用函数时根据执行上下文所动态确定的。

this的使用场景总结

this的使用场景有很多,这里引用候策老师对于this使用的分类:

  • 全局环境中的this
  • 对象调用时的this
  • new操作符与this
  • bind/apply/call改变this的指向
  • 箭头函数中this 的指向
  • this的优先级

下面我们对这几种情况进行分析。

1.全局环境中的this

function fn(){
    console.log(this.a)
}
var a = 1
fn() //1

此处,当调用fn()时,this.a被解析为了全局变量a。这是因为在函数调用时,fn()是直接使用不带任何修饰符的函数进行调用的,所以this指向全局对象,同时,声明在全局作用域中的属性a,本质上就是全局变量的一个属性。

需要注意的是,如果使用严格模式,则不能使用全局变量用于默认绑定,此时this会绑定到undefined。

function fn(){
    'use strict'
    console.log(this.a)
}
var a = 1
fn()     //Uncaught TypeError: Cannot read property 'a' of undefined

2.对象调用时的this

function fn(){
    console.log(this.a)
}
var obj = {
    a: 2
    fn: fn
}
obj.fn() //2

此时,当fn被调用时,加上了obj的引用,this会绑定到这个上下文对象,此时this.a为obj.a。

是不是觉得很简单,别急,我们再来看一道例题:

function fn(){
  console.log(this.a)
}
var obj1 = {
    a: 23,
    fn: fn
}
var obj2 = {
   a: 2,
   obj1: obj1
}
obj2.obj1.fn()  //23

对象属性引用时,只有最后一层在调用位置中起作用

3.new绑定

使用new来调用函数,会发生下面的操作。(注意,此处也是一个常见的面试题哦,说一说new操作符都做了哪些工作?有没有觉得很熟悉)

  1. 创建一个新对象;
  2. 将构造函数的作用域赋给新对象(因此this就指向了这个新对象);
  3. 执行构造函数中的代码(为这个新对象添加属性);
  4. 返回新对象;
function fn(a){
    this.a = a
}
var o = new fn(5)
console.log(o.a)  //5

new 操作符会创建一个新对象,并将this指向这个新对象,从而改变this的指向。

4.bind/apply/call

function fn(){
    console.log(this.a)
}
var obj = {
    a: 2
}
fn.call(obj)    //2

从this绑定的角度来说,apply和call的作用是一样的,知识在参数传递上有一些差别。 下面我们来看看bind。

function fn(){
    console.log(this.a)
}
var obj = {
    a: 2
}
var b = fn.bind(obj)    
b()

bind与call和apply的区别在于,bind会返回一个已经绑定了this的函数,使用时需要进行手动调用。而call/apply会直接进行相关函数调用

5.箭头函数中的指向

箭头函数不适合上述this的判断方法,而是根据外层作用域来决定的。

const foo = {
    fn: function () {
             setTimeout(() => {
                console.log('箭头函数', this)
                    })
                setTimeout(function(){
                    console.log('常规函数', this)
                })
             }
           }
console.log(foo.fn()) 
//箭头函数 {fn: ƒ}
//常规函数 Window{}

6.this的优先级

在此先记录结论,判断this时可按照下面的顺序进行判断:

  1. 是否使用new绑定
  2. 是否通过call,apply或bind绑定
  3. 是否在某个上下文对象中调用
  4. 如果不是,使用默认绑定。严格模式下绑定到undefined,否则绑定到全局对象。

文章参考: 你不知道的JavaScript(上卷)