每日知识点:this 指向之谜——是谁在 call 我?

引言

"这个 this 怎么又 undefined 了?"

"为什么在对象方法里 this 指向对象,回调里就指向 window?"

"箭头函数里的 this 怎么跟我预想的不一样?它是不是有自己的想法?"

如果你被 this 折磨过,你不是一个人。我曾经在一个 Vue 组件里写了一个 setTimeout,里面用了 this.message,结果打印出来是 undefined。我对着屏幕怒吼:"这不科学!我明明就在组件实例内部!"

后来我才明白:this 不是看你在哪里定义的,而是看你怎么调用的。它就像一个社交达人,谁叫它,它就跟谁走。今天,我们就来扒一扒 this 的"社交规则"。

一、this 的四大绑定规则(法官宣判版)

1.1 默认绑定:单身狗模式

当你直接调用一个普通函数,且不在严格模式下,this 指向全局对象(浏览器里是 window,Node 里是 global)。严格模式下是 undefined

javascript 复制代码
function showThis() {
  console.log(this);
}
showThis(); // window(非严格模式)

这叫"默认绑定"------函数被独立调用,没有主人,this 就只好认全局做爹。

1.2 隐式绑定:有对象罩着

当函数作为对象的方法被调用时,this 指向那个对象。

javascript 复制代码
const obj = {
  name: '张三',
  greet() {
    console.log(this.name);
  }
};
obj.greet(); // '张三' ------ this 指向 obj

但如果把这个方法拿出来单独调用,就回到默认绑定:

javascript 复制代码
const fn = obj.greet;
fn(); // undefined(或 window.name,如果存在)

这就是传说中的"丢失 this"------你把方法从对象身上扒下来,它就失去了归属感。

1.3 显式绑定:硬塞给你

callapplybind 可以强行指定 this。

javascript 复制代码
function greet() {
  console.log(this.name);
}
const user = { name: '李四' };
greet.call(user); // '李四'

bind 会返回一个新函数,永久绑定 this,再也改不了(除非你用 new 去怼)。

1.4 new 绑定:造物主模式

当函数被 new 调用时,this 指向新创建的那个对象。

javascript 复制代码
function Person(name) {
  this.name = name;
}
const p = new Person('王五');
console.log(p.name); // '王五'

这四条规则优先级:new 绑定 > 显式绑定 > 隐式绑定 > 默认绑定

二、箭头函数:this 的"叛逆少年"

箭头函数不绑定自己的 this,它会捕获定义时所在的 this,并且永远不变。

javascript 复制代码
const obj = {
  name: '赵六',
  greet: () => {
    console.log(this.name);
  }
};
obj.greet(); // undefined(因为箭头函数的 this 是定义时的外层 this,此处外层是 window)

箭头函数最适合用在回调里,比如 setTimeout、事件监听,能保留外层 this。

javascript 复制代码
const obj = {
  name: '孙七',
  greet() {
    setTimeout(() => {
      console.log(this.name); // '孙七' ------ 箭头函数保住了 this
    }, 100);
  }
};

三、经典坑位大赏

坑1:回调函数里的 this

javascript 复制代码
const obj = {
  name: '周一',
  greet() {
    setTimeout(function() {
      console.log(this.name); // undefined 或 window.name
    }, 100);
  }
};

解决方案:用箭头函数,或者在外面缓存 const self = this

坑2:事件监听里的 this

javascript 复制代码
button.addEventListener('click', function() {
  console.log(this); // 指向 button 元素
});

如果用箭头函数,this 就变成外层(通常是 window),可能不是你想要的。

坑3:类方法作为回调

javascript 复制代码
class MyClass {
  constructor() {
    this.name = '实例';
  }
  handle() {
    console.log(this.name);
  }
}
const inst = new MyClass();
setTimeout(inst.handle, 100); // undefined ------ 方法被单独传递,丢失 this

修复:setTimeout(() => inst.handle(), 100)setTimeout(inst.handle.bind(inst), 100)

四、一句话记住 this

this 指向调用它的那个对象,箭头函数除外,它指向定义时的爹。

更精确版:谁调用我,我就指向谁;没人调用,我就指向全局(严格模式 undefined);箭头函数不跟别人走,我出生时的环境就是我的家。

五、实战:写出一个安全的 this 代码

javascript 复制代码
class SearchBox {
  constructor(inputEl) {
    this.inputEl = inputEl;
    this.inputEl.addEventListener('input', this.handleInput.bind(this));
    // 或者用箭头函数:this.handleInput = (e) => { ... }
  }
  handleInput(e) {
    console.log(this); // 指向 SearchBox 实例,而不是 input 元素
    this.search(e.target.value);
  }
  search(keyword) {
    console.log(`搜索: ${keyword}`);
  }
}

六、总结:别再被 this 坑了

this 是 JavaScript 中最容易出错的概念之一,但掌握了绑定规则,它就是个纸老虎。记住四个绑定(默认、隐式、显式、new),加上箭头函数的特例,你就能在面试和工作中游刃有余。

下次当你看到 this 变成 undefined 时,先问自己三个问题:

  1. 这个函数是怎么被调用的?(默认/隐式/显式/new)
  2. 是不是箭头函数?
  3. 是不是在严格模式下?

答完这三个问题,凶手就找到了。


每日一问:你曾经遇到过最诡异的 this 指向 bug 是什么?是在 React 组件里?还是在 Vue 的 methods 里?评论区分享你的"this 历险记"!

相关推荐
浩星2 小时前
electron系列6之性能优化:从启动慢到内存泄漏
前端·javascript·electron
飞Link2 小时前
pprint 全量技术手册:复杂数据结构的结构化输出引擎
开发语言·前端·python
前端那点事2 小时前
Vue3 代码编写规范 | 避坑指南+团队协作标准
vue.js
Ruihong2 小时前
Vue 迁移 React 实战:VuReact 一键自动化转换方案
前端·vue.js
opbr2 小时前
还在手写 env 类型定义?这个 Vite 插件帮你自动搞定!
前端·vite
Qinana2 小时前
前端正则表达式全解:从基础语法到实战应用
前端·javascript·面试
蜡台2 小时前
JavaScript Object Function ERROR
开发语言·javascript·ecmascript·error
烟话62 小时前
vue3响应式基础
前端·javascript·vue.js
boombb2 小时前
用户反馈入口
前端