📍 this
JavaScript 中的 this 关键字用于指代当前执行上下文,但由于 JavaScript 语言的动态特性,this 的值也是动态的(运行时绑定),并且在不同的场景下可能会有不同的值。
🌎 全局上下文
在全局上下文中,this值为全局对象,全局对象取决于运行环境,如下。
| 运行环境 | 全局对象 |
|---|---|
| 浏览器(Browser) | window |
| Node.js | global |
tip:
不建议在全局上下文中使用
this,而应使用window或global,以避免混淆和潜在的错误。ES11引入了新的全局对象
globalThis,它提供了一种跨平台访问全局环境的标准方式。
📦 模块上下文
在模块上下文中,this的值便不再是全局对象。同时,不同的运行环境,模块上下文中的this值也是不同的,如下。
| 运行环境 | this值 |
|---|---|
| 浏览器(Browser) | undefined |
| Node.js | module.exports |
tip:
- 当
module.exports重新赋值时,this与module.exports便不相等了。- 不建议在模块上下文中使用
this,而应使用Node模块中提供的exports或module.exports。
🧩 函数上下文
在函数上下文中, this 值在定义函数时是无法确定的,而是根据运行时函数被调用的方式来确定,以下是几种常见情况。
| 调用方式 | 示例 | 函数中的this值 |
|---|---|---|
| 全局中直接调用 | method() | 全局对象 |
| 模块中直接调用 | method() | undefined/module.exports |
通过new关键字调用 | new method() | 新对象 |
| 通过对象的属性调用 | obj.method() | 所属对象 |
通过call方法调用 | method.call(context) | 第一个参数,即context |
通过apply方法调用 | method.apply(context) | 第一个参数,即context |
通过bind方法生成新函数,再调用新函数 | method.bind(context)() | bind方法的第一个参数,即context |
需要注意的是,调用全局对象中的函数时,尽管全局对象可以省略,但此时函数体中的this值依然为函数所属对象。例如下面的函数中的this值依然为window,运行环境为浏览器。

此外,箭头函数没有自身的this绑定,出现在箭头函数内部中的this值与其定义时最近的外层上下文中的this值保持一致。以下为箭头函数this值示例,运行环境为浏览器。

同时,由于箭头函数没有this指针,通过apply、call或bind方法调用一个函数时,第一个参数将会被忽略。
✍️ call、apply、bind
call
Function.prototype.call方法用于显式地调用一个函数(API调用),并在调用时可以指定函数的this值和参数列表。

call方法是Function原型上的方法,常用的语法是通过method.call(context)进行调用。在调用过程中,call函数体内部的this值会被设置为method。由此,我们可以直接在call函数体内部间接调用context.method(),并将参数传递进去。这样一来,method函数调用时其内部的this就指向了传入的context对象。

从上图可知,实现call方法的重点是将method作为context身上的属性,并通过context.method()调用即可,method的值便是call函数体中的this值,但此过程仍需注意以下几点。
- 若传递的
context值为null或undefined,则this值为全局对象。 - 若传递的
context值为基本数据类型,如string、number、boolean等,context将被包装。 - 当
method作为context对象的属性时,其属性名必须唯一,且需要在call方法结束后删除。
Function.prototype.call = function (context, ...args) {
context = context === null || context === undefined
? globalThis : Object(context)
const tempKey = Symbol('temp')
Object.defineProperty(context, tempKey, {
value: this,
enumerable: false
})
const result = context[tempKey](...args)
delete context[tempKey]
return result
}apply
Function.prototype.apply方法与call方法类似,也用于显式地调用一个函数(API调用),并在调用时可以指定函数的this值和参数列表。

与call方法不同的是,apply方法接收的参数列表不是展开的,而是数组。所以,我们只需要将数组展开,调用call方法即可,但此过程仍需注意以下几点。
- 当参数列表,即方法的第二个参数为
null或undefined时,原函数调用时无参数。 - 当参数列表,即方法的第二个参数不是数组类型,且不是
null或undefined,则抛出错误。 apply函数体内的this值为原函数。
Function.prototype.apply = function (context, args) {
this.call(context, ...(args || []))
}bind
Function.prototype.bind方法用于创建一个新的绑定函数,新函数的this值为bind方法的第一个参数。bind方法还可以接受额外的参数,这些参数会被提前绑定到新函数的参数列表。当调用新函数时,这些绑定的参数和传入的参数会一起传递给原函数。

与call、apply方法不同的是,bind函数的调用不会触发原函数的执行,而是返回新的绑定函数。我们可以在绑定函数中调用apply来触发原函数的执行,并在调用时合并参数列表,但此过程仍需注意以下几点。
- 绑定函数也可以被当作构造函数使用,此时
bind方法绑定的this值将被忽略,函数体中的this值将指向新实例,合并的参数列表依然传入。 bind函数体内的this值为原函数。
Function.prototype.bind = function (context, ...args) {
const func = this
const boundFunc = function (...argArray) {
if (this instanceof boundFunc) {
return new func(...args, ...argArray)
}
return func.apply(context, argArray.concat(args))
}
Object.setPrototypeOf(func.prototype, boundFunc.prototype)
return boundFunc
}
