Jeekundo.com

以前端技术为主

Archive for January, 2010

this关键字

with 6 comments

javascript最重要的关键字之一是this。但是如果你不了解它的工作原理使用起来就会很困难。

下面我将介绍在事件处理中如何运用它。以后我会增加this的其他用途。

所有者

这篇文章将要讨论的问题是:在函数doSomething()this到底指向谁?

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

在javascript中,this始终指向我们正在执行的这个函数的“所有者”,或者更确切地说,函数是哪个对象的方法this就指向哪个对象。当我们在页面中定义函数doSomething()的时候,他的所有者就是这个页面,或者说javascript的window对象(或全局对象)。而一个onclick属性的所有者是它所在的HTML元素。

这种“所属关系”是javascript的面向对象特性造成的,欲了解更多信息请阅读这篇对象亦是关联数组

------------ window --------------------------------------
|                                          / \           |
|                                           |            |
|                                          this          |
|   ----------------                        |            |
|   |   HTML 元素   | <-- this         -----------------  |
|   ----------------      |           | doSomething() |  |
|               |         |           -----------------  |
|          --------------------                          |
|          |   onclick 属性    |                          |
|          --------------------                          |
|                                                        |
----------------------------------------------------------

如果我们直接执行doSomething(),那么this关键字指向window,并且函数会去改变window对象的style.color,由于window对象没有style属性,这个函数会执行失败,并产生js错误。

拷贝

如果我们要合理地运用this就必须确保使用它的函数为正确的HTML元素所拥有,或换一种说法,我们必须把函数拷贝到onclick属性上,传统的事件绑定模型很好地处理了这个问题。

element.onclick = doSomething;

函数被完整地拷贝到了onclick属性上(现在成了一个方法),所以当事件处理函数被执行的时候this指向HTML元素并且它的color会被改变。

------------ window --------------------------------------
|                                                        |
|                                                        |
|                                                        |
|   ----------------                                     |
|   |    HTML元素   | <-- this         -----------------  |
|   ----------------      |           | doSomething() |  |
|               |         |           -----------------  |
|          -----------------------          |            |
|          |doSomething()的一份拷贝|  <--  拷贝函数         |
|          -----------------------                       |
|                                                        |
----------------------------------------------------------

这里有个小技巧是我们可以把函数拷贝到多个事件处理器上,每次this都能指向正确地HTML元素:

------------ window --------------------------------------
|                                                        |
|                                                        |
|                                                        |
|   ----------------                                     |
|   |   HTML元素    | <-- this         -----------------  |
|   ----------------      |           | doSomething() |  |
|               |         |           -----------------  |
|          -----------------------          |            |
|          |doSomething()的一份拷贝|  <--  拷贝函数         |
|          -----------------------          |            |
|                                           |            |
|   -----------------------                 |            |
|   |    另一个HTML元素     | <-- this        |            |
|   -----------------------     |           |            |
|               |               |           |            |
|          -----------------------          |            |
|          |doSomething()的一份拷贝|  <--  拷贝函数         |
|          -----------------------                       |
|                                                        |
----------------------------------------------------------

这样才是比较充分地使用this,每次函数被执行的时候this都指向触发了这个事件的HTML元素,这个HTML元素“拥有”了doSomething()的一份拷贝。

调用

然而如果你使用行内事件绑定方式

<element onclick="doSomething()">

你没有拷贝函数!而是调用了这个函数。其中的区别是很关键的。这里的onclick属性并没有包含任何实际的函数,而只是一个触发了这个函数:

doSomething();

他只是说了句“找到doSomething()并执行它”,当我们找到doSomething()的时候this关键字再一次指向了全局的window对象,并且函数会返回错误提示。

------------ window --------------------------------------
|                                          / \           |
|                                           |            |
|                                          this          |
|   ----------------                        |            |
|   |    HTML元素   | <-- this         -----------------  |
|   ----------------      |           | doSomething() |  |
|               |         |           -----------------  |
|          -----------------------         / \           |
|          | 找到doSomething()    |          |            |
|          | 并执行它             | ----   对函数的调用     |
|          -----------------------                       |
|                                                        |
----------------------------------------------------------

区别

如果你想通过this来获得触发了这个事件的HTML元素,就必须确保this关键字确实被写入到了onclick属性里面,只有这种情况下它才会指向该函数所绑定到的HTML元素。所以如果你测试下面这段

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 = doSomething
element.addEventListener('click',doSomething,false)
element.onclick = function () {this.style.color = '#cc0000';}
<element onclick="this.style.color = '#cc0000';">

例子——调用

下面这些情况this指向window:

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

注意这里出现了attachEvent()微软事件绑定模型的一个严重的缺点是attachEvent()只建立了对函数的调用但没有拷贝。所以有些时候我们无法知道是哪个HTML元素触发了这个事件。

二者结合

当使用行内事件绑定方式时也可以把this传给函数,使你仍然可以在函数内部使用它:

<element onclick="doSomething(this)">

function doSomething(obj) {
// this出现在了事件处理器内并且传给了函数
// obj现在指向HTML元素,所以你可以做以下操作
obj.style.color = '#cc0000';
}

本文翻译自PPK的The this keyword

Written by admin

January 10th, 2010 at 1:09 am