掌握 JavaScript 的“变色龙”——this 关键字完全指南

"this 到底指向谁?"

几乎每个前端都在控制台里抓狂过。本文带你逐条拆解 this 的四条核心规则、箭头函数特例与常见坑点,附赠记忆口诀与实战 Demo,一次性扫清疑惑。


一、this 是什么?

  • 不是函数本身,不是函数词法作用域
  • 是"调用者"在运行时偷偷塞进来的隐藏参数------谁把我喊出来,我就指向谁
  • 因此 this 与函数声明位置无关,与调用方式有关

二、四条黄金规则(涵盖 99% 场景)

规则 触发场景 this 指向 严格模式差异

  1. 默认绑定 普通函数直接调用 foo() 浏览器:window;Node:global 严格 → undefined
  2. 隐式绑定 对象方法调用 obj.foo() 方法所属对象 丢失场景见下文
  3. 显式绑定 call/apply/bind 手动指定的第一个参数 传 null/undefined 会被忽略 → 回退到全局
  4. new 绑定 new Foo() 新创建的实例 构造器默认 return this

记忆口诀:"无点默认,有点隐式,call 显式,new 实例"


三、规则详解 + 代码示例

① 默认绑定

js 复制代码
function foo() {
  console.log(this.a);
}
var a = 1;
foo(); // 1 (非严格)
js 复制代码
'use strict';
foo(); // undefined → 不再指向 window

② 隐式绑定 & 常见的"丢失"陷阱

js 复制代码
const obj = {
  a: 2,
  foo: foo
};
obj.foo(); // 2 → this === obj

隐式丢失三件套(面试高频):

js 复制代码
// 1. 赋值
const bar = obj.foo;
bar(); // 1 → 回到默认绑定

// 2. 传参
setTimeout(obj.foo, 0); // 1 → 回调里丢失 obj

// 3. 运算符
(false || obj.foo)(); // 1

破解办法:bind / 箭头函数 / 包装函数


③ 显式绑定

js 复制代码
const obj = { a: 3 };
foo.call(obj); // 3
foo.apply(obj, []); // 3

const bar = foo.bind(obj);
bar(); // 永远 3

bind 柯里化:

js 复制代码
function add(a, b) { return a + b + this.c; }
const bound = add.bind({ c: 10 }, 5); // 固定 this 与第一个参数
console.log(bound(2)); // 5+2+10 = 17

④ new 绑定

js 复制代码
function Animal(name) {
  this.name = name;
}
const dog = new Animal('旺财');
console.log(dog.name); // 旺财

构造器显式返回对象会覆盖默认 this:

js 复制代码
function Foo() {
  this.x = 1;
  return { y: 2 }; // 返回新对象 → this 被丢弃
}
console.log(new Foo()); // { y: 2 }

四、箭头函数:this 的"静态快照"

箭头函数没有自己的 this,它捕获外层函数执行上下文的 this(词法作用域)。

js 复制代码
function timer() {
  this.seconds = 0;
  setInterval(() => {
    this.seconds++; // 继承 timer 的 this
  }, 1000);
}
new timer();

对比普通函数:

js 复制代码
setInterval(function () {
  this.seconds++; // 丢失 → window.seconds 或报错
}, 1000);

结论

  • 事件回调 / 定时器想保留外部 this → 箭头函数最简洁
  • 需要动态 this(如 jQuery 插件)→ 不能用箭头函数

五、常见场景速查表

代码片段 this 指向
obj.method() obj
(obj.method)() obj(括号不改变)
(obj.method = obj.method)() 默认绑定
事件侦听器 el.addEventListener('click', handler) el(DOM 元素)
内联事件 <button onclick="handler()"> 全局(非严格)
箭头函数 const f = () => this 外层 this
类方法 class A { m() {} } 实例(需 new A().m())

六、调试技巧

  1. 控制台打印:
js 复制代码
console.log(this);
debugger; // 断点查看调用栈
  1. 显式绑定调试:
js 复制代码
function logThis() {
  console.log(this);
}
logThis.call({ id: 42 }); // 立即确认指向
  1. 开发环境开启严格模式:
js 复制代码
'use strict'; // 让默认绑定变成 undefined,更快暴露错误

七、一句话总结(背下来)

"this 永远指向调用它的对象;没有调用者就回退到全局(或 undefined);箭头函数除外,它只看出生环境。"


八、互动练习

js 复制代码
const obj = {
  fn: function () {
    return this;
  },
  arrow: () => this
};
console.log(obj.fn());   // ?
console.log(obj.arrow()); // ?
const { fn, arrow } = obj;
console.log(fn());        // ?
console.log(arrow());     // ?

答案:

obj → window → undefined → window


把这篇文章收藏起来,下次再遇到 this 疑难杂症,先问"调用点在哪",再看"四条规则",90% 的问题都能秒解。祝你早日驯服这只"变色龙"!

相关推荐
研☆香10 小时前
jQuery + Swiper.js 实现动态渲染轮播图
前端·javascript·jquery
特立独行的猫A14 小时前
仓颉版 Tauri:基于轻量化线程与纯血鸿蒙架构的下一代 Web 混合开发利器
javascript
李游Leo15 小时前
HarmonyOS 7 ArkTS + ohpm:三方 JS 库副作用隔离与 API 26 升级回归【鸿蒙心迹】
javascript·回归·harmonyos
你挚爱的强哥15 小时前
【sgKeyboard】自定义组件:虚拟键盘
前端·javascript·计算机外设
锅里游的鱼吖15 小时前
echarts自定义折线图
前端·javascript·数据库
何何____16 小时前
Vue 全局事件总线详解
前端·javascript·vue.js
u01110267517 小时前
工具页案例图如何编写 alt让图片说明与处理场景对应
java·前端·javascript·图像处理·人工智能·算法·ai作画
事圆则缓17 小时前
Flutter 状态管理框架对比(五):GetX 的响应式、依赖注册与生命周期
前端·javascript·flutter
凌云志.18 小时前
【protobuf】proto3语法详解&通讯录2.x实现
java·前端·javascript
ss27318 小时前
AI全栈实战 | 2.1-03 JavaScript 核心:单线程+事件循环为什么不等同于多线程,原型链 vs Java 继承
java·开发语言·javascript