this指向问题如何判断?

0 阅读2分钟

一句话核心原则

this 永远看函数“怎么调用”,而不是“在哪定义”。


一、判断 this 的完整步骤(通用解法)

当看到一个函数时,按下面顺序判断:

1️⃣ 是箭头函数吗?
2️⃣ 是 new 调用吗?
3️⃣ 是 call / apply / bind 吗?
4️⃣ 是对象调用吗?
5️⃣ 默认调用?

二、五大规则(按优先级)


① 箭头函数(最高优先级)

箭头函数没有自己的 this
它的 this 继承自外层作用域

const obj = {
  name: "Tom",
  say: () => {
    console.log(this.name)
  }
}

obj.say()

输出:

undefined

因为箭头函数的 this 不是 obj,而是外层(全局)。


正确写法

const obj = {
  name: "Tom",
  say() {
    console.log(this.name)
  }
}

输出:

Tom

② new 调用

new 会创建新对象,并把 this 指向它

function Person(name) {
  this.name = name
}

const p = new Person("Tom")
console.log(p.name)

this → 新创建的对象


new 做了什么?

等价于:

const obj = {}
obj.__proto__ = Person.prototype
Person.call(obj)
return obj

③ call / apply / bind

显式绑定优先级高于普通调用

function say() {
  console.log(this.name)
}

const obj = { name: "Tom" }

say.call(obj)   // Tom
say.apply(obj)  // Tom

const fn = say.bind(obj)
fn()            // Tom

this → 指向传入的对象


④ 对象调用

谁调用,this 指向谁

const obj = {
  name: "Tom",
  say() {
    console.log(this.name)
  }
}

obj.say()

this → obj


⚠ 注意:

const fn = obj.say
fn()

这里是普通调用,不是对象调用。

this → window(或 undefined 严格模式)


⑤ 默认绑定(最容易踩坑)

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

say()

非严格模式:

window

严格模式:

undefined

三、优先级总结(面试高频)

箭头函数
   ↓
newcall / apply / bind
   ↓
对象调用
   ↓
默认绑定

谁优先级高,谁生效。


四、常见面试陷阱


1️⃣ 回调里的 this

const obj = {
  name: "Tom",
  say() {
    setTimeout(function () {
      console.log(this.name)
    }, 1000)
  }
}

obj.say()

输出:

undefined

因为 setTimeout 里的函数是普通函数。


解决方式 1:箭头函数

setTimeout(() => {
  console.log(this.name)
})

解决方式 2:bind

setTimeout(function () {
  console.log(this.name)
}.bind(this))

2️⃣ 多层对象

const obj = {
  name: "A",
  foo: {
    name: "B",
    say() {
      console.log(this.name)
    }
  }
}

obj.foo.say()

this → foo
输出:B


3️⃣ 丢失 this(最常见)

const obj = {
  name: "Tom",
  say() {
    console.log(this.name)
  }
}

setTimeout(obj.say, 1000)

this → window

因为本质:

const fn = obj.say
setTimeout(fn)

4️⃣ 链式调用

const obj = {
  name: "Tom",
  say() {
    return function () {
      console.log(this.name)
    }
  }
}

obj.say()()

this → window


五、终极判断口诀

看到 this,问自己三件事:

① 是箭头函数吗?
② 是 new 调用吗?
③ 是谁在点它?

六、面试标准回答

可以这样说:

this 的指向由函数的调用方式决定,而不是定义位置。

判断顺序:

  1. 箭头函数继承外层 this
  2. new 绑定到实例
  3. call/apply/bind 显式绑定
  4. 对象调用绑定到调用对象
  5. 默认绑定到 window(严格模式为 undefined)

优先级:箭头函数 > new > 显式绑定 > 隐式绑定 > 默认绑定。


七、高阶一点(你这个水平要知道)

1️⃣ 箭头函数不能被 new

const fn = () => {}
new fn() // 报错

2️⃣ bind 不能被二次修改

function say() {
  console.log(this.name)
}

const fn = say.bind({ name: "A" })
fn.call({ name: "B" })  // 仍然是 A

3️⃣ 箭头函数 + new 的优先级

function Person() {
  this.say = () => {
    console.log(this)
  }
}

const p = new Person()
p.say()

this → p

因为箭头函数继承的是构造函数的 this。


八、一句话终极理解

this = 函数执行时的“调用者上下文”

你想往哪个方向深入?