小白的JS学习之路(六)—— “this”

3 阅读5分钟

小白的JS学习之路(六)—— “this”

学习笔记整理,从"为什么要有 this"到五条绑定规则,再到箭头函数,一次搞懂 JavaScript 中 this 的所有场景。

前言

上一篇文章我们聊了原型,这期来聊一个让无数新手头疼的概念——this

很多同学学 this 的时候,状态是这样的:

"这个函数里的 this 到底是谁?" "等等,这个也没调用啊,this 怎么变了?" "箭头函数又没有 this,那怎么知道指向谁?"

别急,我们从为什么要有 this 开始,一步一步理清楚。


一、为什么要有 this?

先思考一个问题:假如没有 this,代码会长什么样?

// 没有 this 的写法(繁琐)
function identify(context) {
  return context.name.toUpperCase()
}

function speek(context) {
  var greeting = 'hello, I am ' + identify(context)
  console.log(greeting)
}

var me = { name: 'tom' }
speek(me)

每个函数都要显式传入对象,才能访问那个对象的属性。

有了 this 呢?

// 有 this 的写法(简洁优雅)
function identify() {
  return this.name.toUpperCase()
}

function speek() {
  var greeting = 'hello, I am ' + identify.call(this)
  console.log(greeting)
}

var me = { name: 'tom' }
speek.call(me)  // 直接把 me 和 this 关联起来

this 解决的问题: 提供了一种更优雅的方式隐式传递对象的引用,让代码更简洁、更易于复用。


二、this 可以出现在哪?

2.1 全局 this === Window

在浏览器环境下,直接输出 this

console.log(this)  // Window {}

2.2 函数体内的 this

this 在函数体内出现时,具体指向取决于函数的调用方式,不是固定不变的。

注意:在 Node.js 环境下输出 this,得到的是空对象 {},这是因为 Node.js 复用了 V8 引擎但做了不同处理。


三、五条绑定规则

这是本章的核心。this 到底指向谁,取决于函数的调用方式。

规则一:默认绑定——指向 Window

当函数独立调用时(没有任何对象引导),this 默认指向 Window

var a = 1  // === window: { a: 1 }

function foo() {
  console.log(this.a)  // 独立调用 → this === Window
}

function bar() {
  var a = 2
  foo()  // 虽然 bar 被调用了,但 foo 是独立调用的
  console.log(this)    // Window
}

bar()  // bar 本身也是独立调用的
// 输出:
// 1         ← foo 里 this.a === Window.a === 1
// Window {}

什么是独立调用?

function foo() {}
foo()  // ✅ 独立调用

test = {
  var a = 2
  foo()
}
test.foo()  // ❌ 不是独立调用(被 test 引导)

规则二:隐式绑定——指向拥有它的对象

当一个函数被一个上下文对象所拥有并被该对象调用时,this 指向这个对象。

function foo() {
  console.log(this.a)
}

var obj = {
  a: 1,
  foo: foo  // 把 foo 函数赋值给 obj
}

obj.foo()  // 被 obj 调用 → this === obj
// 输出:1

规则三:隐式丢失——指向最近的对象

当一个函数被多层对象调用时,this 指向最近的那个对象

function foo() {
  console.log(this.a)
}

var obj = {
  a: 1,
  foo: foo  // 第一层引用
}

var oo = {
  a: 2,
  foo: obj.foo  // 第二层引用(把 obj.foo 再赋值给 oo.foo)
}

oo.foo.foo()  // foo 被 oo 调用 → this === oo
// 输出:2

oo.fooobj.foo,再调用 oo.foo.foo(),实际还是调 foo,但这次被 oo 引导,this 就指向 oo 了。

规则四:显式绑定——强行指定 this

有些时候函数是独立调用的,this 指向 Window,但我们想强行指定它指向某个对象,怎么办?

callapplybind

4.1 call——零散传参
function foo(x, y) {
  console.log(this.a, x + y)
}

var fu = { a: 3 }

foo.call(fu, 1, 2)  // 强行把 this 指向 fu
// 输出:3 3
4.2 apply——数组传参
foo.apply(fu, [2, 3])  // 参数用数组集中传入
// 输出:3 5
方法参数形式示例
call零散参数foo.call(obj, 1, 2)
apply数组参数foo.apply(obj, [1, 2])
bind预绑定+调用foo.bind(obj, 1, 2)()
4.3 bind——预绑定,延迟调用
var fu = { a: 3 }

var bound = foo.bind(fu, 1, 3)
bound()  // 输出:3 4

注意:bind 传参可以放在前面(bind 时),也可以放在后面(调用时):

foo.bind(fu, 1, 3)()    // 参数在 bind 里
foo.bind(fu)(1, 3)      // 参数在调用时
foo.bind(fu, 1)(3)      // 参数两边各放一点

规则五:new 绑定——指向实例对象

当用 new 调用函数时,this 会指向新创建的实例对象。

function Car(name) {
  this.name = name
}

var car1 = new Car('SU7')
var car2 = new Car('蔚来')

console.log(car1.name)  // "SU7"
console.log(car2.name)  // "蔚来"

这背后的原理是:new 内部用 .call(this) 把函数的 this 强行绑定到新对象上。


四、箭头函数没有 this

这是 ES6 引入的一个重要特性。

4.1 箭头函数不绑定 this

箭头函数没有自己的 this。写在箭头函数里的 this,实际上是它外层第一个非箭头函数的 this

function outer() {
  const inner = () => {
    console.log(this)  // 继承外层 outer 的 this
  }
  inner()
}

const obj = { name: 'tom' }
outer.call(obj)  // outer 的 this 被 call 指向 obj
// 输出:{ name: 'tom' }

4.2 箭头函数不能用 new 调用

因为箭头函数没有自己的 this,自然也没有构造能力:

const Foo = () => {}
// new Foo()  // ❌ 报错:Foo is not a constructor

五、综合对比:五条规则一句话总结

规则调用方式this 指向
默认绑定独立调用 foo()Window
隐式绑定被对象调用 obj.foo()obj
隐式丢失被多层对象调用 oo.foo.foo()最近的那个对象
显式绑定call / apply / bind手动指定的对象
new 绑定new 函数()新创建的实例

六、一个综合练习

用学到的知识来分析下面这段代码的输出:

var a = 1

function bar() {
  var a = 2

  function foo() {
    console.log(this.a)
  }

  foo()  // 独立调用 → this === Window → Window.a === 1
}

bar()
// 输出:1

分析:虽然 foobar 内部被调用,但 foo()独立调用(没有任何对象引导),所以 this 指向 Window,输出 1


七、总结

  1. 为什么要有 this —— 隐式传递对象引用,代码更简洁
  2. this 出现在哪 —— 全局、函数体内,指向取决于调用方式
  3. 五条绑定规则 ——
    • 默认绑定 → Window(独立调用)
    • 隐式绑定 → 对象(被对象调用)
    • 隐式丢失 → 最近对象(多层引用)
    • 显式绑定 → 指定对象(call/apply/bind)
    • new 绑定 → 实例对象(new 操作符)
  4. 箭头函数 ——
    • 没有自己的 this
    • 继承外层函数的 this
    • 不能用 new 调用

一句话总结 this:

this 指向谁,不由函数写在哪决定,而由函数怎么调用决定。


写在最后

this 是 JavaScript 中最"玄学"的概念之一,核心就是一句话:看调用方式,不看写法。记住五条规则,理解箭头函数的"继承"逻辑,遇到 this 题目就不会再慌了。

如果这篇文章对你有帮助,欢迎点赞收藏,我们下期见!👋


🔗 系列文章: