再也不怕面试官问我this了

1,832 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第32天,点击查看活动详情

this初探

首先,我们得知道一个蛋疼的事实,js的this是所有语言里面比较变态的知识点,其它语言如c,java的this都是十分简单的,是直接动态的绑定到类上。对于js的this,我们首先牢记几个点

1. 普通函数的this是动态绑定的,谁调用this指向谁

栗子1

function fn() {
  console.log(this)
}
fn()//window

相比初学js的小盆友们都被这个答案震惊过,这不是是函数fn里面的?为啥this却指向了window。嗯仔细看口诀1,this在普通函数上是动态绑定的,谁调用这个函数,this就是谁的。fn是window调用的,所以是window

栗子2

 function fn1() {
  console.log(this)
}
let obj = {
  fn1:fn1
}
obj.fn1()//obj

看了栗子1,你就很容易清楚此处为什么是obj了。因为fn1是obj调用的,所以this是obj对象的

栗子3

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

let obj = {
  fn1:fn1
}

let fn2 = obj.fn1
fn2()//window

此处可能大家会骂娘,说能不能说说this的隐式和默认调用?嗯,说实话,我感觉那个不好描述,大家也看的头疼,就直接说口诀吧。此处用口诀,直接看fn2()的执行,它是window调用的,所以是window

栗子4

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

这个题目,大家第一个this基本都可以回答正确,obj调用的fn所以this肯定是obj。但是第二个大家就容易失误,注意func()是谁调用的?不是obj调用的哦,它前面没有obj.func。因此它只能是window的,this.a就输出2

2.this的丢失问题

经过上面的练习大家基本可以解决相当部分的this指向,但是有一部分需要单独说,这部分靠口诀一是无法辨别的,因为this在传递的过程中发生了改变。

2.1 定时器setTimeout系列

定时器里面的this十分容易出错,本质原因是我们忘了定时器它其实也是一个函数.看下面的栗子

栗子5

const obj = {
  a:1,
  fn1:function(){
    console.log(this.a)
  },
  fn2:function(){
    setTimeout(function(){
      console.log(this.a)
    }, 1000);
  }
}
var a = 2
obj.fn1()//1
obj.fn2()//2

首先第一个很简单,obj.fn1的调用直接是通过obj。因此this的指向就是obj,第一个输出就是1 第二个obj.fn2(),此时fn2里面的this确实是obj,但是定时器里面是一个函数,这个函数是直接调用的,不是obj调用的,因此this是window

栗子6

定时器的简写是一个坑,它的this永远是window

const obj = {
  fn:function(){
    console.log(this)
  }
}
setTimeout(obj.fn,1000)//window

大家看到这个可能很容易说this输出的是obj。此时就错了,上了面试官的当了。这个是定时器的简写它本质还原如下

 //简写
 setTimeout(obj.fn,1000)
 
 //还原后,其实是将obj.fn传递给了参数fn。所以fn它是单独调用,fn函数内部永远指向window
 setTimeout((fn) => {
   fn()
 }, timeout);

2.2 函数作为参数

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

首先执行obj的func函数,因此this.a输出的是obj的2。但是fn()的执行是不属于obj对象,因此fn的this是window,此处输出1

2.3 函数作为返回值

var a = 1
const obj = {
  a:2,
  func:function(){
    console.log(this.a)
    return function(){
      console.log(this.a)
    }
  } 
}
obj.func()()//2,1

这个和上面的分析基于一致,首先是obj.func()的执行,所以this.a的就是obj的a输出了2。 但是后面返回了一个函数,这个函数不是obj调用的,是属于window的,因此输出了1

3.多层this时:找最近的

 const obj = {
  a:1,
  info:{
    a:2,
    func:function(){
      console.log(this.a)//2
    }
  }
}
obj.info.func()

看这个栗子,对象出现了两层嵌套,this的输出就找最近的调用,就是info对象,因此输出2

4.new的this问题

new是比较特殊的函数,其内部的this指向的是函数本身。看下面的例子,无论是普通函数还是箭头函数,其this都是函数本身。(自己按上面的分析很好解决)

 function Peo () {
  this.age = 1
  this.info = function() {
    console.log(this.age)
  }
  this.other = () => {
    console.log(this.age)
  }
}
let p = new Peo()
p.info()
p.other()
this.age = 2

5.箭头函数

箭头函数的this和普通函数的this是不同的,普通函数的this是动态的,根据调用情况的不同,this的展现就不同。但是箭头函数的this可以理解成伪静态的(不是完全静态,还取决于它的上一层正常函数被谁调用了,箭头函数的this就是它)

箭头函数的this永远是当前它所处作用域的上一层

如果感觉这句话不好理解,就记住箭头函数的this永远看它的上一个正常函数的this指向,它指向了谁,箭头函数的this就是谁。

var name = 'tom'
const obj = {
    name: 'zc',
    intro:function ()  {
        return () => {
            console.log('My name is ' + this.name)
        }
    },
    intro2:function ()  {
        return function() {
            console.log('My name is ' + this.name)
        }
    }
}
obj.intro2()()//my name is tom
obj.intro()()//my name is zc

首先第一个obj.intro2它返回一个函数,这个函数的调用是window,不是obj。第一个输出就是tom。 第二个是一个箭头函数,它的this要看上面第一个正常函数的this指向,也就是obj.intro因此输出的是zc

6.强绑定this(call,bind,apply)

普通函数和new

强绑定可以改变普通函数和new函数的this指向。 下面的例子大家自己分析,不会的可以评论区分享。

例子1

function Peo () {
  this.age = 1
  this.info = function() {
    console.log(this.age)
  }
  this.other = () => {
    console.log(this.age)
  }
}
const obj = {
  age:3
}
let p = new Peo()
p.info()//1
p.other()//1
p.info.call(obj)//3
p.other.call(obj)//1

例子2

var name = 'window'
var obj1 = {
  name: 'obj1',
  intro: function () {
    console.log(this.name)
    return () => {
      console.log(this.name)
    }
  }
}
var obj2 = {
  name: 'obj2'
}
obj1.intro.call(obj2)()//obj2,obj2

箭头函数

此处只需记住强绑定对箭头函数是无效的

总结

  • 普通函数的this是动态的,谁调用这个函数,这个函数的内部this就指向了谁
  • 当函数作为参数,函数作为返回值,定时器。要留意this丢失
  • 箭头函数的this是取决于它的上一个正常函数的this指向
  • 使用call,bind,apply无法改变箭头函数的this
  • 只要细心,灵活运用上面的三条规则,基本this都可以解决了