PPK 谈 JavaScript 的 this 关键字:拷贝与指向机制解析

作者:小圆点时间:2026-10-25 00:03:14浏览:209

本文深入解析 JavaScript 中 this 关键字在事件处理中的行为差异。通过对比函数赋值(拷贝)与行内调用(指向)两种场景,阐明 this 指向 HTML 元素或全局 window 对象的原理。文章结合 PPK 的经典观点,详细说明了传统事件注册、行内注册及微软 attachEvent 模型下的 this 指向规则,帮助开发者避免常见错误。

首先介绍在

event handling

场景中的应用,随后探讨 this 的其他用法。


自身指向

我们先分析函数 doSomething() 内部的 this 究竟指向何处?

function doSomething() {
  this.style.color = '#cc0000';
}

JavaScript 中的 this 始终指向运行该函数的“自身”。这是一种指向函数对象的方式。若在页面中直接定义 doSomething() 函数,其“自身”即指页面,也就是 JavaScript 的全局对象 window。而 onclick 属性则属于 HTML 元素本身。

这种“所有权”关系是 JavaScript 面向对象(OO)特性的体现。更多细节可参考《将对象作为关联数组》一文。

------------ window --------------------------------------
|                     / \      |
|                      |      |
|                     this     |
|  ----------------            |      |
|  | HTML element | <-- this     ----------------- |
|  ----------------   |      | doSomething() | |
|        |     |      ----------------- |
|     --------------------             |
|     | onclick property |             |
|     --------------------             |
|                            |
----------------------------------------------------------

若 doSomething() 执行时未与任何特定对象关联,关键字 this 将指向 window 窗口。此时函数试图修改 window.style.color,由于 window 对象没有 style 属性,该操作会引发 JavaScript 错误。

拷贝机制

因此,正确使用 this 具有一定难度。在上述示例中,函数本应指向 HTML 元素“自身”。换言之,存在一个函数被拷贝至 onclick 属性。我们来看传统事件注册的情况。

element.onclick = doSomething;

由于函数被完整拷贝至 onclick 属性(此时它成为该方法),在事件处理执行时,this 指向 HTML 元素,从而成功修改颜色。

------------ window --------------------------------------
|                            |
|                            |
|                            |
|  ----------------                   |
|  | HTML element | <-- this     ----------------- |
|  ----------------   |      | doSomething() | |
|        |     |      ----------------- |
|     -----------------------     |      |
|     |copy of doSomething()| <-- copy function  |
|     -----------------------            |
|                            |
----------------------------------------------------------

这使得我们可以为多个事件处理程序分配同一个函数拷贝。每次执行时,this 均指向正确的 HTML 元素:

------------ window --------------------------------------
|                            |
|                            |
|                            |
|  ----------------                   |
|  | HTML element | <-- this     ----------------- |
|  ----------------   |      | doSomething() | |
|        |     |      ----------------- |
|     -----------------------     |      |
|     |copy of doSomething()| <-- copy function  |
|     -----------------------     |      |
|                      |      |
|  -----------------------         |      |
|  | another HTML element| <-- this    |      |
|  -----------------------   |      |      |
|        |        |      |      |
|     -----------------------     |      |
|     |copy of doSomething()| <-- copy function  |
|     -----------------------            |
|                            |
----------------------------------------------------------

每次函数被调用,this 指向当前正在处理事件的 HTML 元素(即 doSomething() 函数的一个拷贝实例)。

指向机制

如果使用行内事件注册方式呢?

<element onclick="doSomething()">

这里没有拷贝函数,而是直接引用它,区别何在?此时 onclick 属性不包含实际函数代码,仅包含一个函数调用语句。

doSomething();

上述代码的含义是:“去执行 doSomething() 函数”。在 doSomething() 内部,this 关键字再次指向全局 window 对象,导致函数返回错误结果。

------------ window --------------------------------------
|                     / \      |
|                      |      |
|                     this     |
|  ----------------            |      |
|  | HTML element | <-- this     ----------------- |
|  ----------------   |      | doSomething() | |
|        |     |      ----------------- |
|     -----------------------     / \      |
|     | go to doSomething() |     |      |
|     | and execute it   | ---- reference to   |
|     -----------------------    function    |
|                            |
----------------------------------------------------------

区别分析

若要通过 this 访问 HTML 元素以处理事件,必须确保该引用实际写入 onclick 属性中,且事件处理已正确注册。如果采用以下方式:

element.onclick = doSomething;
alert(element.onclick)

得到的结果是

function doSomething()
{
	this.style.color = '#cc0000';
}

可以看到,this 关键字位于 onclick 方法内部,它指向 HTML 元素。

但如果采用以下写法:

<element onclick="doSomething()">
alert(element.onclick)

得到的结果是

function onclick()
{
	doSomething()
}

这里仅引用了函数 doSomething()。this 关键字不在 onclick 方法作用域内,因此不指向 HTML 元素。

示例:拷贝

在以下示例中,this 被写入 onclick 方法:

element.onclick = function () {doSomething()}
element.attachEvent('onclick',doSomething)
<element onclick="doSomething()">

示例:指向

在以下示例中,this 指向 window:

element.onclick = function () {doSomething()}
element.attachEvent('onclick',doSomething)
<element onclick="doSomething()">

需注意上述 attachEvent 方法。这是微软事件注册模型的缺点,它创建了对函数的引用而非拷贝,因此有时无法确定当前处理事件的 HTML 元素。

结合使用

在使用行内事件注册时,也可以将 this 传递给函数。因此可以这样使用:

<element onclick="doSomething(this)">
 
function doSomething(obj) {
	// this is present in the event handler and is sent to the function
	// obj now refers to the HTML element, so we can do
	obj.style.color = '#cc0000';
}
作者最新文章