解密 JavaScript 中的 this:谁才是真正的调用者?

彻底搞懂 JavaScript 中的 this 关键字

从一个实战场景说起

最近在做一个基于 localStorage 的待办事项应用,核心功能是通过表单收集用户输入并保存到本地存储。在处理表单提交事件时,遇到了一个经典问题:this 的指向到底是谁?

html 复制代码
<form class="add-items">
  <input type="text" name="item" placeholder="Add a new tapas" required>
  <input type="submit" value="+ Add Item">
</form>
<a href="#" class="lnk">跳转百度</a>
javascript 复制代码
document.querySelector(".lnk").addEventListener("click", goBaidu);

function goBaidu(e) {
  console.log(this);  // this 指向什么?
  e.preventDefault();
}

如果您也经常遇到类似困惑,那么这篇文章将帮助您彻底理解 this 的绑定规则。

this 的核心原则

this 是在函数运行时确定的,而非声明时!

this 的指向取决于调用函数的方式 ,而非函数定义的位置。这是理解 this 的关键。

六种绑定规则

规则一:普通函数调用

当函数作为普通函数被调用时,this 指向全局对象 window(非严格模式下)。

javascript 复制代码
var name = "李刚";

function sayName() {
  console.log(this.name);  // 输出:李刚
}

sayName();  // this 指向 window

严格模式下的变化:

javascript 复制代码
"use strict";

function sayName() {
  console.log(this);  // 输出:undefined
}

sayName();

启用严格模式 "use strict" 可以避免 this 意外指向 window,是现代 JavaScript 开发的最佳实践。

规则二:对象方法调用

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

javascript 复制代码
let obj = {
  name: "王文",
  say: function() {
    console.log(this.name);  // 输出:王文
  }
};

obj.say();  // this 指向 obj

注意:引用式赋值的陷阱

javascript 复制代码
const fn = obj.say;  // 引用式赋值,函数脱离了对象
fn();  // this 指向 window(非严格模式)

当对象的方法被单独赋值给变量后,this 的绑定关系会丢失。

规则三:构造函数调用

当函数作为构造函数使用 new 关键字调用时,this 指向新创建的实例对象。

javascript 复制代码
function Person(name) {
  this.name = name;  // this 指向新创建的实例
}

const person = new Person("张三");
console.log(person.name);  // 输出:张三

规则四:事件处理函数调用

在 DOM 事件处理函数中,this 指向触发事件的元素。

javascript 复制代码
document.querySelector(".lnk").addEventListener("click", function(e) {
  console.log(this);  // this 指向 <a> 元素
  e.preventDefault();
});

规则五:手动指定 this(call/apply/bind)

call 和 apply

两者都能手动指定 this 指向,区别在于参数传递方式:

javascript 复制代码
let obj = {
  name: "王文",
  say: function() {
    console.log(this.name);
  },
  speak: function(a, b) {
    console.log(a, b);
    console.log(this);
  }
};

let obj2 = {
  name: "金大轩"
};

obj.say.call(obj2);     // 输出:金大轩
obj.say.apply(obj2);    // 输出:金大轩

obj.speak.call(obj2, 1, 2);      // 参数逐个传递
obj.speak.apply(obj2, [1, 2]);   // 参数通过数组传递
bind

bind 方法不会立即执行函数,而是返回一个绑定了 this 的新函数:

javascript 复制代码
const fn2 = obj.speak.bind(obj2);  // 返回新函数,不执行
fn2(1, 2);  // 调用新函数时才执行

规则六:箭头函数中的 this

箭头函数没有自己的 this

箭头函数的 this 继承自外层作用域的 this,这是它与普通函数的本质区别。

javascript 复制代码
let obj = {
  name: "姆巴佩",
  say: function() {
    setTimeout(() => {
      console.log(this.name);  // 输出:姆巴佩(继承外层 this)
    }, 1000);
  }
};

obj.say();

经典面试题:setTimeout 中的 this 陷阱

这是一个非常经典的面试题,比较两种写法的区别:

写法一:普通函数 + bind

javascript 复制代码
var name = "梅西";
let obj = {
  name: "姆巴佩",
  say: function() {
    setTimeout((function() {
      console.log(this.name)  // 输出:姆巴佩
    }).bind(this), 1000);
  }
};

obj.say();

通过 bind(this) 手动绑定了 setTimeout 回调函数中的 this

写法二:箭头函数

javascript 复制代码
var name = "梅西";
let obj = {
  name: "姆巴佩",
  say: function() {
    setTimeout(() => {
      console.log(this.name)  // 输出:姆巴佩
    }, 1000);
  }
};

obj.say();

箭头函数继承了外层 say 方法中的 this,无需手动绑定。

如果不用箭头函数也不绑定 this

javascript 复制代码
var name = "梅西";
let obj = {
  name: "姆巴佩",
  say: function() {
    setTimeout(function() {
      console.log(this.name)  // 输出:梅西(this 指向 window)
    }, 1000);
  }
};

obj.say();

普通函数作为 setTimeout 回调时,this 默认指向 window

深度分析:var 与 let 对 this 的影响

var 声明的变量会挂载到 window 对象

javascript 复制代码
var name = "全局变量";
console.log(window.name);  // 输出:全局变量
console.log(this.name);    // 输出:全局变量(在全局作用域中)

这就是为什么普通函数调用时 this.name 能访问到用 var 声明的变量。

let/const 声明的变量不会污染 window

javascript 复制代码
let name = "全局变量";
console.log(window.name);  // 输出:undefined
console.log(this.name);    // 输出:undefined

letconst 声明的变量不会成为 window 对象的属性,这是 ES6 带来的重要改进。

this 指向速查表

调用方式 this 指向
普通函数调用 window(非严格模式)/ undefined(严格模式)
对象方法调用 调用该方法的对象
构造函数调用 新创建的实例对象
事件处理函数 触发事件的 DOM 元素
call/apply/bind 手动指定的对象
箭头函数 外层作用域的 this

总结

理解 this 的关键在于:关注函数的调用方式,而非定义位置

在实际开发中,推荐以下实践:

  1. 始终启用严格模式 "use strict"
  2. 使用箭头函数避免 this 绑定问题
  3. 使用 bind 预绑定 this(适合需要复用的函数)
  4. 优先使用 let/const 而非 var

掌握 this 的绑定规则,不仅能让您写出更优雅的代码,也是通过前端面试的必备技能。


相关推荐
Heo1 小时前
Vite进阶用法详解
前端·javascript·面试
洛卡卡了1 小时前
Claude Code rules 要怎么用,团队协作时如何统一代码规范呢?
面试·agent·claude
铁皮饭盒3 小时前
Next.js 风格路由内置?Bun FileSystemRouter 凭啥这么香
javascript
小林ixn4 小时前
别再背八股了!从 5 个真实场景彻底搞懂 JavaScript 的 this
javascript
东风破_4 小时前
JavaScript 面试常考的字符串算法:从反转字符串到回文判断
前端·javascript
巴勒个啦4 小时前
D3.js 入门实战:用力导向图可视化项目依赖关系
javascript
不好听6135 小时前
JavaScript 的 this 到底指向谁?
javascript·面试
触底反弹5 小时前
🔥 2026 年爆火的 Harness Engineering 到底是什么?从原理到实战一文讲透
javascript·人工智能·程序员