JavaScript中this关键字的指向规则与场景解析
本文详细解析JavaScript中this关键字在不同执行环境下的指向逻辑。涵盖全局上下文、对象方法调用、构造函数、自执行函数、DOM事件绑定及原型方法等场景,通过具体代码示例说明this动态绑定的机制与常见陷阱,帮助开发者准确掌握this的使用规则。
JavaScript this关键字概述
this关键字在C++、Java等语言中早已存在,初学者常觉晦涩,但一旦掌握其核心逻辑,便能大幅提升编码效率。本文将系统梳理JavaScript中this关键字的用法与原理。
this是前端面试的高频考点,许多开发者在实际编码中也会因this指向问题遭遇困惑。例如在使用Knockout等框架时,常需显式传递this而非直接依赖当前上下文,理解其机制至关重要。
以下内容基于个人实践总结,旨在作为技术笔记供后续查阅。如有疏漏或错误,欢迎指正与讨论。
this的动态指向与基本规则
与C#等语言中this固定指向当前实例不同,JavaScript的this具有动态性。
JavaScript中this的指向并非一成不变,而是随调用方式动态确定。call和apply方法正是利用这一特性来显式改变this指向,从而提升代码的灵活性与复用能力。
通常情况下,this指向定义该函数的对象所有者。
这一规则是理解JavaScript this机制的核心,务必重点掌握。
以下代码是常见的面试题示例:
<script type="text/javascript">
var number = 1;
var obj = {
number: 2,
showNumber: function(){
this.number = 3;
(function(){
console.log(this.number);
})();
console.log(this.number);
}
};
obj.showNumber();
</script>
由于showNumber方法属于obj对象,因此方法内部的this指向obj。执行this.number = 3后,obj的number属性值变为3,此时this指向obj对象的number属性。
同理,第二个console.log输出的也是obj对象的number属性值。
为何前文称“一般情况下”this指向函数拥有者?因为存在特例。在函数自执行(IIFE)场景中,this默认指向全局对象window。因此,第一个console.log输出的是window对象的number属性值(即1)。
因此,需补充以下特例说明:
在函数自执行(IIFE)中,this指向全局window对象。
DOM事件中的this指向
此外,DOM事件处理中的this指向也常令人困惑,主要涉及以下三种注册方式:
具体示例如下:
1. 通过HTML标签属性注册事件时,this指向window对象。
<input id="test" type="button" value="按钮" onClick="test()"/>
function test(){alert(this)}
2. 对于1,要让 this 指向 input,可以将 this 作为参数传递。
3. 使用addEventListener等标准DOM方法注册事件时,this指向绑定的DOM元素。
document.getElementById("test").addEventListener("click",test);
JavaScript中this的复杂场景解析
在经典面向对象语言(如C++、C#、Java)中,this关键字含义明确,始终指向当前实例。JavaScript虽也提供this关键字,但其行为比传统OO语言更为复杂多变。
接下来将深入探讨JavaScript中this在不同场景下的具体表现及其复杂性。
1. 在HTML元素事件属性中以inline方式使用this关键字:
// 可以在里面使用this
">division element
// 可以在里面使用this
">division element
常见做法是调用如javascript: EventHandler(this)的形式。此处可写入任何合法JavaScript语句,甚至定义内部类。其原理是脚本引擎为div实例生成一个匿名成员方法,并将onclick指向该方法。
2. 通过DOM方式在事件处理函数中使用this关键字:
division element
var div = document.getElementById('elmtDiv');
div.attachEvent('onclick', EventHandler);
function EventHandler()
{
// 在此使用this
}
// -->
division element
var div = document.getElementById('elmtDiv');
div.attachEvent('onclick', EventHandler);
function EventHandler()
{
// 在此使用this
}
// -->
此时EventHandler函数内的this指向IE浏览器的window对象。因为attachEvent调用后,脚本引擎对该函数的调用与div实例本身无直接关联。可通过this.event.srcElement获取div对象引用,且EventHandler的caller属性为null。
3. 通过DHTML方式在事件处理函数中使用this关键字:
division element
lt;mce:script language="javascript">
var div = document.getElementById('elmtDiv');
div.onclick = function()
{
// 在此使用this
};
/ -->
division element
var div = document.getElementById('elmtDiv');
div.onclick = function()
{
// 在此使用this
};
// -->
此时this指向div元素实例。通过直接赋值div.onclick = EventHandler,相当于为div实例添加成员方法。与HTML inline方式不同,DHTML方式下脚本引擎不会再生成匿名方法。
4. 在类定义中使用this关键字:
function JSClass()
{
var myName = 'jsclass';
this.m_Name = 'JSClass';
}
JSClass.prototype.ToString = function()
{
alert(myName + ', ' + this.m_Name);
};
var jc = new JSClass();
jc.ToString();
function JSClass()
{
var myName = 'jsclass';
this.m_Name = 'JSClass';
}
JSClass.prototype.ToString = function()
{
alert(myName + ', ' + this.m_Name);
};
var jc = new JSClass();
jc.ToString();
这是JavaScript模拟类定义中this的典型用法,与传统OO语言相似。但需注意,成员属性和方法必须使用this引用,否则如直接访问myName会提示未定义。
5. 为脚本引擎内部对象添加原型方法时的this关键字:
function.prototype.GetName = function()
{
var fnName = this.toString();
fnName = fnName.substr(0, fnName.indexOf('('));
fnName = fnName.replace(/^function/, '');
return fnName.replace(/(^\s+)|(\s+$)/g, '');
}
function foo(){}
alert(foo.GetName());
function.prototype.GetName = function()
{
var fnName = this.toString();
fnName = fnName.substr(0, fnName.indexOf('('));
fnName = fnName.replace(/^function/, '');
return fnName.replace(/(^\s+)|(\s+$)/g, '');
}
function foo(){}
alert(foo.GetName());
此时this指向被添加原型的类的实例,与第4点类似,无特殊之处。
6. 结合第2点与第4点,讨论一个易混淆的this使用场景:
view plaincopy to clipboardprint?
function JSClass()
{
this.m_Text = 'division element';
this.m_Element = document.createElement('DIV');
this.m_Element.innerHTML = this.m_Text;
this.m_Element.attachEvent('onclick', this.ToString);
}
JSClass.prototype.Render = function()
{
document.body.appendChild(this.m_Element);
}
JSClass.prototype.ToString = function()
{
alert(this.m_Text);
};
var jc = new JSClass();
jc.Render();
jc.ToString();
function JSClass()
{
this.m_Text = 'division element';
this.m_Element = document.createElement('DIV');
this.m_Element.innerHTML = this.m_Text;
this.m_Element.attachEvent('onclick', this.ToString);
}
JSClass.prototype.Render = function()
{
document.body.appendChild(this.m_Element);
}
JSClass.prototype.ToString = function()
{
alert(this.m_Text);
};
var jc = new JSClass();
jc.Render();
jc.ToString();
页面运行后首先显示“division element”,点击该文字后弹出“undefined”。
7. 在CSS expression表达式中使用this关键字:
height: expression(this.parentElement.height);">
division element
height: expression(this.parentElement.height);">
division element
此处的this行为与第1点相同,均指向div元素实例本身。
8. 在函数内部嵌套函数中使用this关键字:
view plaincopy to clipboardprint?
function OuterFoo()
{
this.Name = 'Outer Name';
function InnerFoo()
{
var Name = 'Inner Name';
alert(Name + ', ' + this.Name);
}
return InnerFoo;
}
OuterFoo()();
function OuterFoo()
{
this.Name = 'Outer Name';
function InnerFoo()
{
var Name = 'Inner Name';
alert(Name + ', ' + this.Name);
}
return InnerFoo;
}
OuterFoo()();
运行结果显示为“Inner Name, Outer Name”。按第2点逻辑,结果似应为“Inner Name, undefined”,但实际结果受JavaScript变量作用域影响。详细机制可参考“原来JScript中的关键字'var'还是有文章的”一文及其回复。
this用法的三类归纳与总结
综上所述,JavaScript中this的用法可归纳为以下三类(详细用法参原文):
1. 在HTML元素事件属性或CSS expression表达式中,以inline方式使用this关键字——对应原文第1、7点。
2. 在事件处理函数中使用this关键字——对应原文第2、3点。
其中又可分为两种具体方式:
(1) DOM方式:此时this指向窗口(window)对象。
(2) DHTML方式:此时this指向div元素实例。
3. 在类定义中使用this,并在其内部函数或成员函数(主要指prototype定义的方法)中使用——对应原文第4、5、8点。
需注意的是,函数本身也是对象,因此需区分变量定义与成员变量定义,具体见下方代码示例。
view plaincopy to clipboardprint?
var variableName; //变量定义
//作用域:函数定义范围内
//使用方法:直接使用variableName
this.varName; //成员变量定义
//作用域:函数对象定义范围内及其成员函数中
//使用方法:this.varName
var variableName; //变量定义
//作用域:函数定义范围内
//使用方法:直接使用variableName
this.varName; //成员变量定义
//作用域:函数对象定义范围内及其成员函数中
//使用方法:this.varName
基于上述三类this使用方法的归纳,对原文第6点提到的程序进行测试与改进,以验证后两种使用场景。
view plaincopy to clipboardprint?
function JSClass()
{
var varText = "func variable!"; //函数中的普通变量
this.m_Text = 'func member!'; //函数类的成员变量
this.m_Element = document.createElement('DIV'); //成员变量,创建一个div对象
this.m_Element.innerHTML = varText; //使用函数的普通变量
this.m_Element.attachEvent('onclick', this.ToString); //给这个对象的事件连上处理函数
this.newElement = document.createElement('DIV');
this.newElement.innerHTML = "new element";
this.newElement.m_Text = "new element text!"; //给创建的对象建个成员
this.newElement.onclick = function()
{
alert(this.m_Text); //指向div对象的成员
};
}
JSClass.prototype.Render = function()
{
document.body.appendChild(this.m_Element); //把div对象挂在窗口上
document.body.appendChild(this.newElement);
}
JSClass.prototype.ToString = function()
{
alert(this.m_Text); //指向窗口(window)对象
};
function initialize(){
var jc = new JSClass();
jc.Render();
jc.ToString(); //里面的this指向JSClass类的实例,里面有m_Text成员
}
// -->
initialize();
// -->
function JSClass()
{
var varText = "func variable!"; //函数中的普通变量
this.m_Text = 'func member!'; //函数类的成员变量
this.m_Element = document.createElement('DIV'); //成员变量,创建一个div对象
this.m_Element.innerHTML = varText; //使用函数的普通变量
this.m_Element.attachEvent('onclick', this.ToString); //给这个对象的事件连上处理函数
this.newElement = document.createElement('DIV');
this.newElement.innerHTML = "new element";
this.newElement.m_Text = "new element text!"; //给创建的对象建个成员
this.newElement.onclick = function()
{
alert(this.m_Text); //指向div对象的成员
};
}
JSClass.prototype.Render = function()
{
document.body.appendChild(this.m_Element); //把div对象挂在窗口上
document.body.appendChild(this.newElement);
}
JSClass.prototype.ToString = function()
{
alert(this.m_Text); //指向窗口(window)对象
};
function initialize(){
var jc = new JSClass();
jc.Render();
jc.ToString(); //里面的this指向JSClass类的实例,里面有m_Text成员
}
// -->
initialize();
// -->
上述代码的执行结果如下:
页面加载时,弹出对话框输出“func member!”。
页面上显示文本内容。
func variable!
new element
单击“func variable”时,弹出对话框显示undefined。
原因是此时toString函数内的this指针指向window对象。
单击“new element”时,弹出对话框显示“new element text!”。
原因是此时toString函数内的this指针指向div元素,且该元素已定义m_Text成员(this.newElement.m_Text = "new element text!")。
JavaScript怎么准确判断数据类型:typeof、instanceof与Object.prototype.toString
2026-11-05 17:09:21
JS中将数组和对象混合组装为JSON字符串
2026-11-05 16:41:42
JS运算符优先级与结合性的区别是什么?
2026-11-05 13:22:02
JS Map怎么只遍历Key或Value
2026-11-05 13:00:36
D3.js 与 ECharts 选型对比:灵活性 vs 开发效率
2026-11-05 09:45:01
