跳到主要内容
JavaScript 中 this 的解析:从 call、bind 到箭头函数 | 极客日志
JavaScript Node.js 大前端
JavaScript 中 this 的解析:从 call、bind 到箭头函数 JavaScript 中的 this 关键字是理解语言机制的关键。详细解析了 this 的默认绑定规则(全局上下文、对象方法),以及通过 call、apply 强制指定 this 并立即执行的方法。针对定时器或异步回调中 this 丢失的问题,介绍了 bind 永久绑定和闭包保存 this 的解决方案。重点阐述了箭头函数没有自己的 this,而是继承外层作用域的特性及其适用场景与陷阱。最后总结了 this 绑定的优先级顺序及最佳实践,帮助开发者避免常见误区,写出更健壮的代码。
日志猎手 发布于 2026/3/27 更新于 2026/9/12 97 浏览引言
在 JavaScript 编程的世界里,this 是一个既基础又令人困惑的概念。它看似简单,却常常在不经意间'背叛'我们的预期;它灵活多变,却又遵循着一套严格的规则。尤其当与 call、apply、bind 以及 ES6 引入的箭头函数 结合时,this 的行为变得更加微妙而强大。
本文将结合代码,对 this 的机制进行一次全面、深入且生动的剖析。我们将还原其技术含义,并通过大量示例揭示背后的原理。无论你是初学者还是资深开发者,相信都能从中获得新的洞见。
一、核心内容
我们可以将 this 问题拆解为几个核心命题:
this 可以被覆盖
bind 用于定时器中绑定 this,延迟执行
call / apply 可指定 this 指向,并立即运行
that = this 利用作用域链保存 this
箭头函数没有自己的 this,而是继承父级作用域的 this
接下来,我们将围绕这五点展开详细论述。
二、this 的默认行为:谁调用,就属于谁
在深入高级技巧前,必须先理解 this 的默认绑定规则 。
1. 全局上下文中的 this
在非严格模式下,全局作用域中的 this 指向全局对象(浏览器中是 window,Node.js 中是 global):
console .log (this === window );
在严格模式下,全局函数中的 this 为 undefined:
'use strict' ;
function f ( ) { console .log (this ); }
f ();
2. 对象方法中的 this
当函数作为对象的方法被调用时,this 指向该对象:
const obj = { name : 'Alice' , ( ) { . ( . ); } };
obj. ();
greet
console
log
this
name
greet
但注意:函数一旦脱离对象调用,this 就会丢失 :
const fn = obj.greet ;
fn ();
这就是为什么我们需要 call、bind 等工具来'拯救'迷失的 this。
三、call 与 apply:强制指定 this,立即执行!
原理与语法
func.call(thisArg, arg1, arg2, ...)
func.apply(thisArg, [arg1, arg2, ...])
call 和 apply 的核心作用就是在调用函数的同时 ,显式指定 this 的值,并立刻执行 该函数。
示例演示 function introduce (age ) {
console .log (`I'm ${this .name} , ${age} years old.` );
}
const person = { name : 'Bob' };
introduce.call (person, 25 );
introduce.apply (person, [30 ]);
即使 introduce 不是 person 的方法,我们也能让它'假装'是——这就是 this 的灵活性。
实际应用场景
通用工具函数适配不同上下文。
数组方法借用(如将类数组转为真数组):
const args = Array .prototype .slice .call (arguments );
四、bind:永久绑定 this,延迟执行
原理与语法 func.bind(thisArg, arg1, arg2, ...) 返回一个新函数 ,该函数的 this 被永久绑定到 thisArg,且可预设部分参数(柯里化)。
常用于定时器等异步回调中绑定 this
不立即执行,而是返回一个可稍后调用的函数
经典问题:setTimeout 中的 this 丢失 const timer = {
seconds : 0 ,
start ( ) {
setInterval (function ( ) {
this .seconds ++;
console .log (this .seconds );
}, 1000 );
}
};
timer.start ();
解决方案一:使用 bind const timer = {
seconds : 0 ,
start ( ) {
setInterval (function ( ) {
this .seconds ++;
console .log (this .seconds );
}.bind (this ), 1000 );
}
};
timer.start ();
这里,.bind(this) 创建了一个新函数,其 this 永久指向 timer 对象,无论何时被调用都不会改变。
解决方案二:that = this(闭包保存) const timer = {
seconds : 0 ,
start ( ) {
const that = this ;
setInterval (function ( ) {
that.seconds ++;
console .log (that.seconds );
}, 1000 );
}
};
虽然有效,但需要额外变量,且在深层嵌套中容易混乱。bind 更加声明式和安全。
五、箭头函数:没有 this 的'佛系'函数
核心特性 箭头函数(Arrow Function)没有自己的 this 。它不会创建新的 this 上下文,而是词法地继承外层作用域的 this 。
'放弃了自己的 this' → 箭头函数内部不存在 this 绑定。
'指向父级作用域的 this' → 它的 this 由定义时的外层作用域决定,与调用方式无关。
示例对比
普通函数(this 丢失) const obj = {
name : 'David' ,
delayedLog ( ) {
setTimeout (function ( ) {
console .log (this .name );
}, 100 );
}
};
obj.delayedLog ();
箭头函数(自动继承) const obj = {
name : 'Cherry' ,
delayedLog ( ) {
setTimeout (() => {
console .log (this .name );
}, 100 );
}
};
obj.delayedLog ();
为什么能成功?因为箭头函数的 this 继承自 delayedLog 方法,而 delayedLog 的 this 指向 obj。
箭头函数的不可变性 const fn = ( ) => console .log (this .name );
const obj = { name : 'Eve' };
fn.call (obj);
fn.bind (obj)();
call、apply、bind 对箭头函数的 this毫无影响 。
适用场景 vs 陷阱
回调函数(如 map, filter, setTimeout)
避免 that = this 的样板代码
对象方法(会继承外层 this,可能不是对象本身)
构造函数(箭头函数不能用 new 调用)
需要动态 this 的场景
六、综合案例:四种方式对比 假设我们有一个计数器对象,需要在 1 秒后打印当前值:
const counter = {
count : 0 ,
method1 ( ) {
const self = this ;
setTimeout (function ( ) {
console .log ('method1:' , self.count );
}, 1000 );
},
method2 ( ) {
setTimeout (function ( ) {
console .log ('method2:' , this .count );
}.bind (this ), 1000 );
},
method3 ( ) {
setTimeout (() => {
console .log ('method3:' , this .count );
}, 1000 );
},
method4 ( ) {
const fn = function ( ) {
console .log ('method4:' , this .count );
};
setTimeout (() => fn.call (this ), 1000 );
}
};
counter.method1 ();
counter.method2 ();
counter.method3 ();
counter.method4 ();
四种方式都能正确输出,但箭头函数最简洁 ,bind 最显式 ,that = this 最传统 。
七、this 绑定优先级总结 当多个规则同时存在时,JavaScript 按以下优先级确定 this:
new 绑定 (构造函数)→ 最高
显式绑定 (call / apply / bind)
隐式绑定 (对象方法调用,如 obj.fn())
默认绑定 (独立函数调用)
八、常见误区与最佳实践
误区 1:认为箭头函数总是更好 错!箭头函数在对象方法中会导致 this 指向外层(可能是全局):
const badObj = {
name : 'Bad' ,
getName : () => this .name
};
console .log (badObj.getName ());
const goodObj = {
name : 'Good' ,
getName ( ) {
return this .name ;
}
};
误区 2:bind 后还能被 call 覆盖 不能!bind 创建的函数其 this 是永久锁定 的:
function f ( ) { console .log (this .x ); }
const bound = f.bind ({x : 1 });
bound.call ({x : 2 });
最佳实践
在类方法或对象方法中,使用普通函数。
在回调、事件监听、定时器中,优先考虑箭头函数。
若需兼容旧环境或需要预设参数,使用 bind。
避免过度使用 that = this,除非在不支持箭头函数的环境中。
九、结语:掌握 this,就是掌握 JavaScript 的灵魂 现在,我们不仅读懂了它,更理解了其背后的技术哲学:
call/apply 是'命令式'的干预——我要你现在就用这个 this!
bind 是'防御式'的设计——无论何时调用,都必须用这个 this!
that = this 是'妥协式'的智慧——既然你靠不住,我就自己存一份!
箭头函数 是'声明式'的优雅——我不需要 this,我信任我的上下文!
JavaScript 的魅力,正在于这种灵活性与规则性的统一。当你能自如地在这些工具之间切换,你就不再是 this 的奴隶,而是它的主人。
愿你在未来的代码中,不再对 this 感到迷茫,而是微笑着说:
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online