妈妈再也不担心面试官问我this了🤡

187 阅读3分钟

一、this 是什么?
很多初学者对 this 的理解是 "指向自己",这是完全错误的!
this 的本质是 动态绑定 的指针,指向 调用它的那个对象。就像打电话:

  • 你直接拨打(直接调用函数),接电话的是全局对象(window
  • 你通过朋友转接(通过对象调用),接电话的是那个朋友(对象)
  • 你强制指定转接(call/apply),接电话的是你指定的人

记住这句话:this 永远指向最后调用它的那个对象


二、四大绑定规则
判断 this 指向,按优先级从高到低:

1. new 绑定(最高优先级)

使用 new 调用函数时,this 会绑定到新创建的对象上:

function Person(name) {
  this.name = name; // this → 新对象
}
const p = new Person('小明'); 

2. 显式绑定(call/apply/bind)

通过 call, apply, bind 强制指定 this

function sayHi() { console.log(this.name) }
const obj = { name: '小红' };
sayHi.call(obj); // 输出:小红

3. 隐式绑定(对象方法调用)

函数作为对象属性被调用时,this 指向该对象:

const cat = {
  name: '喵喵',
  eat: function() { console.log(this.name + '吃鱼') }
};
cat.eat(); // 喵喵吃鱼

隐式丢失的坑!
当函数被赋值给变量后调用,this 会丢失原对象,指向全局:

const eat = cat.eat;
eat(); // undefined吃鱼(严格模式报错)

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

独立函数调用时,this 指向全局(非严格模式)或 undefined(严格模式):

function foo() { console.log(this) }
foo(); // window(浏览器环境)

三、箭头函数——打破规则的特例
箭头函数没有自己的 this,会 继承外层作用域的 this,且无法被修改:

const obj = {
  name: '外层',
  func: function() {
    const arrow = () => console.log(this.name);
    arrow();
  }
};
obj.func(); // 外层(箭头函数继承 func 的 this)

对比普通函数:

const obj = {
  func: function() {
    setTimeout(function() {
      console.log(this); // 普通函数调用,this指向window
    }, 100);
    setTimeout(() => {
      console.log(this); // obj(箭头继承外层 func 的 this)
    }, 200);
  }
};

四、改变 this 指向的三种方法

在 JavaScript 中,this 的指向是动态的,但我们可以通过 显式绑定 强制指定它的值。显式绑定有三种方法:callapplybind,它们的作用和用法各有特点。

1. call:立即调用,参数逐个传递

call 的作用是 立即执行函数,并明确指定 this 的值,同时将参数逐个传递。比如:

function greet() { console.log(`Hello, ${this.name}`) }
const obj = { name: '小明' };
greet.call(obj); // Hello, 小明

call 的底层原理是临时将函数挂载到目标对象上,然后通过对象调用函数。

2. apply:立即调用,参数数组传递

applycall 的功能几乎一样,唯一的区别是 参数传递方式apply 接受一个数组作为参数。比如:

function add(a, b) { console.log(a + b) }
add.apply(null, [1, 2]); // 3

apply 常用于参数数量不确定的场景,比如将数组元素作为参数传递给函数。

3. bind:返回新函数,延迟调用

bind 的作用是 创建一个新函数,并将 this 永久绑定到指定的值。与 callapply 不同,bind 不会立即执行函数,而是返回一个绑定了 this 的新函数。比如:

function greet() { console.log(`Hello, ${this.name}`) }
const obj = { name: '小明' };
const boundFunc = greet.bind(obj);
boundFunc(); // Hello, 小明

bind 还可以提前绑定部分参数,实现函数柯里化。

总结

  • callapply 是立即调用,区别在于参数传递方式。
  • bind 是延迟调用,返回一个新函数。
  • 显式绑定是 JavaScript 中非常强大的工具,熟练掌握后可以大幅提升代码的灵活性和可读性!

五、实战练习题
题目1:

var name = '全局';
const obj = {
  name: '对象',
  func: function() {
    setTimeout(function() {
      console.log(this.name);
    }, 0);
  }
};
obj.func(); // 输出?

题目2:

const button = document.querySelector('button');
button.addEventListener('click', function() {
  setTimeout(() => {
    console.log(this); // this 指向?
  }, 100);
});

题目3:

function Foo() {
  this.value = 1;
  setTimeout(function() {
    this.value++;
  }, 100);
}
const obj = new Foo();
setTimeout(() => console.log(obj.value), 200); // 输出?

答案解析:

  1. 输出:全局

    • setTimeout 的回调是普通函数,this 默认指向 window
  2. 指向 button 元素

    • 外层 functionthisaddEventListener 绑定为触发元素,箭头函数继承
  3. 输出:1

    • setTimeout 中的 this 指向 window,未修改 obj.value

总结口诀:

  • 普通函数看调用,谁点出来就是谁
  • 箭头函数看外层,定义位置定终身
  • new/显式优先级高,四大规则要记牢

理解 this 的关键在于分析 函数的调用方式,多写多练才能形成直觉!