吃透JS this指向:3分钟搞定面试高频坑,新手也能秒懂

做前端开发的同学,几乎都被this指向“坑过”——明明定义时逻辑没问题,一运行就报undefined;同一个函数,换种调用方式this就“跑偏”;面试时被问起this绑定规则,支支吾吾说不明白。

其实this的核心就一句话:this的指向,不是在函数定义时确定的,而是在函数调用时确定的,谁调用它,他就指向谁(箭头函数除外)。

一、4种核心场景:精准判断this指向

不用死记硬背,结合代码场景理解,每一种场景都是工作/面试高频考点,建议边看边敲代码验证。

场景1:普通函数调用(默认绑定)

函数直接调用,没有任何对象前缀,this默认指向全局对象(浏览器种是window,Node.js中是global);严格模式下,this指向undefined。

// 非严格模式
function sayHi() {
  console.log(this) // window (浏览器环境)
  console.log(this.name)  // 全局变量name Hello
}

window.name = 'Hello'
sayHi()  // 直接调用,无对前缀

// 严格模式下
function sayHiStrict() {
  “use strict” 
  console.log(this)  // undefined
}

sayHiStrict()

💡坑点:新手容易误导以为this指向函数本身,其实不然,普通调用时,函数与任何对象都无关联,自然绑定全局。

场景2:对象方法调用(隐式绑定)

函数作为对象的属性,通过“对象.方法()”的方式调用,this指向调用该方法的对象。

const user = {
  name: '掘金',
  greet: function() {
    console.log(`你好,${this.name}`)  // this的指向user
  }  
}
user.greet() // 输出: 你好,掘金

// 坑点: 方法赋值后调用,this会“丢失”
const greet = user.greet
greet() // 输出:你好,undefined (此时是普通调用,this指向window)

这是工作中最常踩的坑之一,比如把对象方法当作回调函数传递,很容易导致this指向跑偏。

场景3:new构造函数调用

用new关键字调用构造函数时,会创建一个新的对象实例,this直接指向这个新创建的实例。

function Person(name) {
  this.name = name // this指向new出来的实例
  this.sayName = function() {
    console.log(this.name)
  }
}
const person1 = new Person("张三")
person1.sayName() // 输出:张三

const person2 = new Person("李四")
person2.sayName() // 输出:李四

// 坑点:忘记写new,构造函数变成了普通函数
const person3 = Person("王五")
console.log(this.name)  // 输出:王五(this指向window)

记住:nwe关键字的核心作用,就是改变this指向,让它指向新创建的实例。

场景4:显示绑定(call/apply/bind)

当我们想手动指定this指向时,用call、apply、bing这三个方法,优先级高于默认绑定和隐式绑定,是解决this指向的”万能钥匙“。

const cat = { name: "小猫" }
const dog = { name: "小狗" }

function sayLove(...args) {
  console.log(`${this.name}喜欢吃${args.join("、")}`);
}

// call: 第一个参数是this指向,后面跟单个参数
sayLove.call(call,'小鱼干''罐头') // 输出: 小猫喜欢吃小鱼干、罐头 这时候函数里的this指向cat

// apply: 第一个参数是this的指向,后面跟参数数组
sayLove.apply(dog,["骨头", "肉"]) // 输出: 小狗喜欢吃骨头、肉,这时候函数里的this指向dog

// bind:返回一个新的函数,this固定指向,需要手动调用
const catLove = sayLove.bind(cat)
catLove("罐头") // 输出小猫喜欢吃罐头

💡区别:call和apply会立即执行函数,bind不会立即执行,适合需要延迟调用的场景。

二、特殊情况:箭头函数的this

箭头函数是ES6新增,它没有自己的this,this继承自外层的作用域(既定义时所处的环境),且一旦确定就无法改变(call/apply/bind也没用)。

const obj = {
  name: '张三',
  objThis: this,
  // 普通方法
  say1: function() {
    console.log(this.name,'1')
    setTimeout(function() {
      console.log(this.name,'2')
    },100)
  },
  // 箭头函数: this继承上下文的this,就是obj的this
  say2: () => {
    console.log(this === windowthis === obj.objThis)
  },
  say3:function() {
    setTimeout(() => {
      console.log(this)
    })
  }
}

obj.say1()  // 1.this指向obj 输出张三,2.this指向window
// 坑点:箭头函数的this是指向上下文的this,但是对象{}不算作用域,所以箭头函数的this指向window
obj.say2() // 输出true true
// 坑点: 因为say3是普通函数 obj.say3 调用 this指向obj,所以箭头函数的this指向obj,有些人会把第二种和第三种记混。
obj.say3() // 输出 obj对象

三. 一句话总结+面试避坑

判断this指向,按优先级排序:new绑定 > 显示绑定(call/apply/bind)> 隐式绑定(对象方法) > 默认绑定(普通调用);箭头函数特殊,继承外层this。

面试高频追问:为什么对象字面量中的箭头函数this指向全局? 答:对象字面量{}只是一个普通的对象定义,不算独立的词法作用域。而箭头函数的外层作用域是全局,因此this继承全局。