技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTJS

JavaScript 基础心法——this

所属馆藏
JavaScript
文件格式
Markdown
原始路径
JavaScript/30-JavaScript基础心法——this
本文目录10 个章节
  1. 一、先记住一句话
  2. 二、普通函数的四种常见绑定
  3. 三、严格模式、脚本和模块环境
  4. 四、箭头函数的词法 this
  5. 五、DOM 事件中的 this
  6. 六、class 方法不会自动绑定实例
  7. 七、常见丢失 this 的场景
  8. 八、检查 this 的最小实验
  9. 九、总结
  10. 参考资料与原文

JavaScript 基础心法——this

Category(分类): JavaScript Status: 已整理(2026)

原文只有标题和两个来源链接。本文保留来源,并补回 this 的判断规则、严格模式、箭头函数、class、浏览器事件和不同模块环境的现代说明。

一、先记住一句话

对普通函数来说,this 通常由调用方式决定,而不是由函数定义位置决定。阅读一段代码时,先看函数是如何被调用的,再判断 this

箭头函数是重要例外:箭头函数没有自己的 this,它从定义位置的词法环境捕获 this,因此不能通过 callapplybind 改变自己的 this

const object = {
  value: 10,
  normal() {
    return this.value
  },
  arrow: () => this?.value
}

console.log(object.normal()) // 10
console.log(object.arrow()) // 通常是 undefined:箭头函数不从调用点取 this

上面的箭头函数还受到所在脚本/模块环境影响,因此不要用它来定义需要动态接收者的方法。

二、普通函数的四种常见绑定

对普通、非箭头函数,可以先按下面的顺序排查:

  1. new 绑定:用 new 调用构造函数时,创建的新实例成为 this
  2. 显式绑定callapplybind 指定接收者;但 new 调用绑定函数时会优先创建新实例;
  3. 隐式绑定:以 object.method() 形式调用,objectthis
  4. 默认绑定:独立调用普通函数,严格模式下是 undefined,非严格模式下会替换为该运行时的全局对象。

这是学习时很有用的排查顺序,但 Proxy、Symbol.hasInstance、宿主回调和箭头函数等特殊机制仍需单独看规则。

2.1 默认绑定与严格模式

function sloppy() {
  return this
}

function strictFunction() {
  'use strict'
  return this
}

console.log(sloppy() === globalThis) // true:非严格普通函数的典型结果
console.log(strictFunction()) // undefined

不要把“独立调用时 thiswindow”当成普遍结论:

  • 浏览器经典脚本中的非严格普通函数通常会得到 window,它也是该环境的 globalThis
  • ES module 和 class 方法默认严格,独立调用时通常是 undefined
  • Node.js CommonJS 顶层的 this 与浏览器顶层不同;
  • Worker、Node.js ESM、iframe 和其他 Realm 也有各自的全局对象。

需要表示当前运行时全局对象时,优先使用标准的 globalThis,但不要用它伪造严格函数的 this 语义。

2.2 隐式绑定

const user = {
  name: 'Ada',
  getName() {
    return this.name
  }
}

console.log(user.getName()) // Ada

const getName = user.getName
console.log(getName()) // 非严格函数可能是 undefined 或读取全局对象属性

把方法取出来再调用,会丢失原来的接收者。可以使用 bind,或显式传递对象:

const boundGetName = user.getName.bind(user)
console.log(boundGetName()) // Ada

console.log(user.getName.call(user)) // Ada

括号本身通常不会改变接收者,但逗号运算符、赋值、解构和回调传递可能会造成丢失:

const object = {
  value: 1,
  getValue() {
    return this.value
  }
}

console.log((object.getValue)()) // 1
console.log((0, object.getValue)()) // 非严格函数下通常是 undefined

2.3 显式绑定:call、apply 和 bind

function describe(prefix, suffix) {
  return `${prefix}${this.name}${suffix}`
}

const person = { name: 'Ada' }

console.log(describe.call(person, 'Hello ', '!'))
console.log(describe.apply(person, ['Hello ', '!']))

const boundDescribe = describe.bind(person, 'Hello ')
console.log(boundDescribe('!'))
  • call(thisArg, arg1, arg2, ...) 立即调用;
  • apply(thisArg, argsArray) 立即调用,把参数作为 array-like 对象传入;
  • bind(thisArg, ...args) 返回一个新函数,不会立即调用,还可以预绑定部分参数。

callapplybind 对箭头函数的 this 不起重新绑定作用:

const arrow = () => this
const receiver = { value: 1 }

console.log(arrow.call(receiver) === arrow()) // true:箭头函数仍使用词法 this

2.4 new 绑定

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

const user = new User('Ada')
console.log(user.name) // Ada
console.log(user instanceof User) // true

new User() 会创建对象、把对象的原型关联到 User.prototype、以该对象作为 this 执行构造函数,并返回对象(构造函数显式返回对象时还有额外规则)。

绑定函数也可以被 new 调用:这时新实例优先于绑定的 this,但通过 bind 预绑定的参数仍会保留。

三、严格模式、脚本和模块环境

this 的结果还取决于代码运行环境:

环境顶层 this 的常见结果注意
浏览器经典脚本通常是 globalThis/window仅指脚本顶层,不代表所有函数调用
浏览器 ES moduleundefined模块默认严格模式
Node.js CommonJS通常是 module.exports文件被 CommonJS 包装函数包裹
Node.js ES moduleundefinedESM 顶层没有 CommonJS 的 this
class 方法由调用方式决定class 方法默认严格模式

不要用一个浏览器控制台的结果去推导 Nuxt SSR、Node.js 模块或 Web Worker 的 this。如果代码需要跨宿主运行,应减少对顶层 this 的依赖,使用显式参数、模块导入或 globalThis

四、箭头函数的词法 this

箭头函数从外层词法环境捕获 this,没有自己的 argumentssupernew.target 绑定,也不能作为构造函数使用:

const counter = {
  value: 0,
  incrementLater() {
    setTimeout(() => {
      this.value += 1
      console.log(this.value)
    }, 0)
  }
}

counter.incrementLater()

如果把定时器回调写成普通函数,它的 this 就不再自动指向 counter,除非显式绑定:

const counter = {
  value: 0,
  incrementLater() {
    setTimeout(function () {
      this.value += 1
    }.bind(this), 0)
  }
}

类字段箭头函数可以捕获实例,但会为每个实例创建一个函数,是否使用应结合内存和传递回调的需要决定:

class ButtonController {
  count = 0

  handleClick = () => {
    this.count += 1
  }
}

五、DOM 事件中的 this

使用普通函数作为 DOM 事件监听器时,浏览器通常把 this 设置为 event.currentTarget;箭头函数不会获得这个动态绑定:

const button = document.querySelector('#save')

function handleClick(event) {
  console.log(this === event.currentTarget) // true
}

button.addEventListener('click', handleClick)
button.removeEventListener('click', handleClick)

箭头函数中应使用事件对象:

button.addEventListener('click', event => {
  console.log(event.currentTarget)
})

removeEventListener 需要同一个函数引用和匹配的监听选项,因此不要在添加和移除时分别创建两个匿名函数。组件框架中还应在组件卸载时清理监听器。

六、class 方法不会自动绑定实例

class Person {
  constructor(name) {
    this.name = name
  }

  greet() {
    return `Hello, ${this.name}`
  }
}

const person = new Person('Ada')
console.log(person.greet()) // Hello, Ada

const greet = person.greet
// greet():严格模式下 this 是 undefined,会访问失败

const boundGreet = person.greet.bind(person)
console.log(boundGreet()) // Hello, Ada

把 class 方法传给事件监听器、定时器或第三方库时,必须根据 API 约定处理接收者。可选方式包括:

  • 在构造函数中 this.greet = this.greet.bind(this)
  • 使用类字段箭头函数;
  • 在回调中显式调用 person.greet()
  • 更推荐在纯逻辑中把依赖作为参数传递。

七、常见丢失 this 的场景

7.1 解构方法

const api = {
  baseUrl: 'https://example.com',
  getUrl(path) {
    return `${this.baseUrl}${path}`
  }
}

const { getUrl } = api
// getUrl('/users'):丢失 api 这个接收者

const getApiUrl = api.getUrl.bind(api)
console.log(getApiUrl('/users')) // https://example.com/users

7.2 传递给数组方法或第三方 API

数组的 mapforEach 等方法不会自动把外层对象作为回调的 this

const formatter = {
  prefix: '#',
  format(value) {
    return `${this.prefix}${value}`
  }
}

const values = [1, 2]
console.log(values.map(formatter.format, formatter)) // ['#1', '#2']

这里是数组方法显式提供了 thisArg;现代代码也可以用箭头函数显式捕获:

console.log(values.map(value => formatter.format(value)))

八、检查 this 的最小实验

function showThis(label) {
  'use strict'
  console.log(label, this)
}

const object = { value: 1 }
showThis('default') // undefined
showThis.call(object, 'call') // { value: 1 }
showThis.apply(object, ['apply']) // { value: 1 }

const bound = showThis.bind(object)
bound('bind') // { value: 1 }

严格模式示例可以清楚展示原始 thisArg。非严格函数对 null/undefined 和原始值的替换或装箱行为是另一条规则,不应写成“call(null) 永远指向 window”。

九、总结

  • 普通函数的 this 主要由调用形式决定;
  • 箭头函数的 this 由定义位置词法捕获,不能重新绑定;
  • new 调用、显式绑定、隐式绑定、默认绑定有明确的排查顺序;
  • call/apply 立即调用,bind 返回新函数;
  • 严格模式、ES module、class、Node.js 和浏览器经典脚本的默认行为不同;
  • 方法解构、回调传递和事件监听器是最常见的 this 丢失场景;
  • 业务代码可以优先使用显式参数和稳定的闭包,减少对隐式 this 的依赖。

参考资料与原文

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS