JavaScript中this关键字工作原理与绑定规则详解
本文深入解析JavaScript中this关键字的绑定机制,涵盖全局代码、函数调用、对象方法、构造函数及内部函数五种场景下的this指向。重点讲解call、apply的显式绑定以及ES5中bind方法的原理与模拟实现,帮助开发者避免常见的this陷阱。
this 关键字的基本概念
本文聚焦于 this 关键字,不作过多延伸。在面向对象编程语境下,this 通常指向当前类的实例,且不可被直接赋值。这一特性隐含了 this 必须依附于类或对象存在,它是面向对象语言中的常见关键字。若采用纯函数式编程风格而非面向对象模式,代码中出现的 this 会大幅减少甚至完全消失。因此,使用 this 时应明确自己处于对象或类的开发模式中,否则它仅是函数调用时产生的副作用。
JavaScript 中的 this 常令开发者困惑,被视为该语言著名的陷阱之一。由于 this 采用晚绑定机制,其指向可能是全局对象、当前对象或其他上下文,这种不确定性甚至导致部分开发者选择完全避免使用 this。
若能透彻理解 this 的工作机制,便可轻松避开这些陷阱。接下来将分析不同调用场景下 this 的具体指向。
this 的五种绑定场景
1. 全局代码中的 this
alert(this)
//window
在全局作用域中,this 指向全局对象。在浏览器环境中,该全局对象即为 window。
2. 作为普通函数调用
function fooCoder(x) {
this.x = x;
}
fooCoder(2);
alert(x);
// 全局变量x值为2
此时 this 指向全局对象(浏览器中为 window)。若代码处于严格模式(strict mode),this 的值则为 undefined。
3. 作为对象的方法调用
var name = "clever coder";
var person = {
name : "foocoder",
hello : function(sth){
console.log(this.name + " says " + sth);
}
}
person.hello("hello world");
执行结果为输出 'foocoder says hello world'。在此场景下,this 指向调用该方法的对象,即当前的 person 对象。
4. 作为构造函数调用
new FooCoder();
当函数通过 new 关键字作为构造函数执行时,函数内部的 this 指向新创建的对象实例。
5. 内部函数中的 this
var name = "clever coder";
var person = {
name : "foocoder",
hello : function(sth){
var sayhello = function(sth) {
console.log(this.name + " says " + sth);
};
sayhello(sth);
}
}
person.hello("hello world");
//clever coder says hello world
在内部函数中,this 并未按预期绑定到外层函数的对象上,而是错误地指向了全局对象。这通常被视为 JavaScript 语言设计上的缺陷,因为开发者通常不希望内部函数的 this 指向全局。通用的解决方案是将外层的 this 保存到一个变量中,通常约定命名为 that 或 self:
var name = "clever coder";
var person = {
name : "foocoder",
hello : function(sth){
var that = this;
var sayhello = function(sth) {
console.log(that.name + " says " + sth);
};
sayhello(sth);
}
}
person.hello("hello world");
//foocoder says hello world
显式绑定:call 与 apply
6. 使用 call 和 apply 设置 this
person.hello.call(person, "world");
apply 和 call 功能相似,区别在于参数传递方式:apply 接受一个参数数组,而 call 接受分离的参数列表。两者的方法定义如下:
call( thisArg [,arg1,arg2,… ] );
// 参数列表,arg1,arg2,...
apply(thisArg [,argArray] );
// 参数数组,argArray
这两个方法的作用是将指定函数绑定到特定对象上执行,因此 this 会被显式设置为第一个参数。
this 指向规则总结
简要总结
回顾上述几种情况,可以发现只有第六种(显式绑定)最容易引起困惑。
我们可以将结论归纳为以下几点:
1. 当函数作为对象的方法被调用时,this 指向该对象。
2. 当函数作为普通函数调用时,this 指向全局对象(在严格模式下为 undefined)。
3. 在构造函数中,this 指向新创建的对象。
4. 嵌套函数中的 this 不会继承外层函数的 this 值;若需访问外层 this,需借助额外变量保存。
更简洁的总结是:只有当函数直接被某个对象调用时,该函数内的 this 才指向该对象。
obj.foocoder();
foocoder.call(obj, ...);
foocoder.apply(obj, …);
回调中的 this 与 bind 方法
进阶讨论
我们常遇到如下代码场景:
$("#some-ele").click = obj.handler;
若在 handler 中使用 this,this 会绑定到 obj 上吗?答案是否定的。函数赋值后是在回调中执行的,此时 this 会绑定到 $("#some-div") 元素上。这需要深入理解函数的执行环境。本文不展开讨论执行环境和作用域链,建议参考《JavaScript高级程序设计》相关章节。理解 JS 函数的执行环境有助于更好地掌握 this 的行为。
如何解决回调函数中 this 绑定的问题?ES5 引入了 bind 方法:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg
当绑定函数被调用时,传入的第一个参数将作为原函数运行时的 this 指向。若使用 new 操作符调用绑定函数,则该参数无效。
arg1, arg2, ...
当绑定函数被调用时,这些预置参数加上绑定函数运行时传入的参数,将按顺序作为原函数的参数。该方法创建一个新函数(绑定函数),该函数以创建时传入 bind 方法的第一个参数作为 this,后续参数及运行时参数按序传递给原函数。
显然,bind 方法能很好地解决上述问题。
$("#some-ele").click(person.hello.bind(person));
//相应元素被点击时,输出foocoder says hello world
该方法实现原理并不复杂,以下是 Prototype.js 中 bind 方法的源码示例:
Function.prototype.bind = function(){
var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
return function(){
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
希望阅读完本文后,this 不再成为开发中的难题。欲了解更多内容,请点击本站相关学习资源。
JavaScript异步发送HTTP请求时怎么优雅地处理错误
2026-11-05 17:10:44
5款主流JS在线运行工具对比:CodePen、JSFiddle与StackBlitz怎么选
2026-11-05 17:04:04
JavaScript入门书籍避坑:这些书不适合初学者
2026-11-05 16:31:15
JS使用Math.round实现保留两位小数的原理与代码
2026-11-05 15:35:55
JavaScript 原生 Date 对象怎么实现日期加减
2026-11-05 14:30:00
