转载自:http://www.jb51.net/article/80192.htm 作者:wsion
JS中的this关键字让很多新老JS开发人员都感到困惑。这篇文章将对this关键字进行完整地阐述。读完本文以后,您的困惑将全部消除。您将学会如何在各种不同的情形正确运用this。
我们和在英语、法语这样的自然语言中使用名词一样地使用this。比如,“John飞快地跑着,因为他想追上火车”。请注意这句话中的代指John的代名词“他”。我们原本也可以这样表达,“John飞快地跑着,因为John想追上火车”。按照正常的语言习惯,我们并不按第二种方式表达。如果我们真按第二种方式说话,我们的家人和基友一定会把我们当成怪胎。说不定不止家人,甚至连我们的酒肉朋友和同事都会远离我们。类似地,在JS中,我们把this关键字当成一种快捷方式,或者说是引用(referent)。this关键字指向的是当前上下文(context,下文中将会对此作专门的解释)的主体(subject),或者当前正在被执行的代码块的主体。
考虑以下代码:
如果我们使用person.firstName和person.lastName,某些情况下代码会变得模棱两可。例如,全局变量中有一个跟person同名的变量名。这种情况下,如果我们想要读取person.firstName的值,系统将有可能从全局变量的person变量中读取firstName属性(property)。这样一来,我们调试代码的时候很难发现错误。所以this并不只起到美化代码的作用,同时也是为了保证程序的准确性。这种做法实际上和前面讲到的“他”的用法一样,使得我们的代码更加清晰。“他”所引用的正是句首的“John”。
正如代名词“他”被用来代指句中的先行词(先行词就是代名词所指示的名词),this关键字以同样的方式来引用当前方法(function,也可以称为函数)所绑定(bound)的对象。this不只引用对象,同时包含了对象的值。跟先行词一样,this也可以被理解成在上下文中用来引用当前对象(又叫作“先行词对象”)的快捷方式(或者来适度减少歧义的替代品)。我们迟些会专门讲解“上下文”。
this关键字基本理论
首先我们得知道,对象(Object)有属性集(properties),所有的方法(function)也有属性集。运行到某个方法的时候就有了一个this属性—一个存储了调用该方法(准确地说是使用了this关键字的方法)的对象的值的变量。
this关键字始终指向一个对象并持有这个对象的值,尽管它可以出现在全局范围(global scope)方法(函数)以外的地方,但它通常出现在方法体中。值得注意的是,如果我们使用严格模式(strict mode),并在全局方法(global functions)或者没有绑定到任何对象的匿名方法中使用this关键字时,this将会指向undefined。
this被用在方法体中,比如方法A,它将指向调用方法A的对象的值。并不是任何情况我们都能找到调用方法A的对象名,这时候就用this来访问调用方法A的对象所拥有的方法和属性。this确实只是一个用来引用先行词—调用方法的对象—的快捷方式。
我们来仔细体会下面这段使用this的代码。
再考虑下面这段使用了this的jQuery示例。
我想详细地说一下上面这个jQuery示例:$(this)的使用,这是this的jQuery版本,它用于匿名方法中,这个匿名方法在button的单击事件里执行。这里之所以说$(this)被绑定到button对象,是因为jQuery库把$(this)绑定到调用click方法的对象上。因此,尽管$(this)被定义在一个自身无法访问“自身”变量的匿名方法里,$(this)仍会指向button对象。
请注意,button是一个HTML页面的DOM元素,它同时是一个对象:在上面的例子中,因为我们把它包装在了jQuery的$()方法里,所以它是一个jQuery对象。
this关键字的核心
下面这条规则可以帮助你彻底搞懂this关键字:如果一个方法内部使用了this关键字,仅当对象调用该方法时this关键字才会被赋值。我们估且把使用了this关键字的方法称为“this方法”。
尽管看上去this引用了它在代码中所存在于的对向,事实上在方法被调用之前它并没有被赋值,而赋给它的值又严格地依赖于实际调用“this方法”的对象。this通常会被赋予调用对象的值,下面有一些特殊情况。
全局范围里的this
在全局域中,代码在浏览器里执行,所有变量和方法都属于window对象。因而当我们在全局域中使用this关键字的时候,它会被指向(并拥有)全局变量window对象。如前所述,严格模式除外。window对象是JS一个程序或一张网页的主容器。
因而:
对付this有绝招
当方法内使用了this关键字时,这几种情况最容易引起误解:方法被借用;把方法赋值给某个变量;方法被用作回调函数(callback),被作为参数传递;this所在的方法是个闭包(该方法是一个内部方法)。针对这几种情况,我们将逐一攻破。在此之前,我们先简单介绍一下“上下文”(context)。
JS当中的上下文跟这句话中的主语(subject)类似:“John是赢家,他还了钱”。这句话的主语是John。我们也可以说这句话的上下文是John,因为我们在这句话中关注的是John,即使这里有一个“他”字来代指John这个先行词。正如我们可以使用分号来切换句子的主语一样,通过使用不同的对象来对方法进行调用,当前的上下文对象同样可以被切换。
类似地,以下JS代码:
现在我们开始正式讨论应付this关键字的绝招,例子里包含了this所引发的错误以及解决方案。
1.当this被用作回调函数传入其它方法
当我们把一个使用了this关键字的方法当成参数作为回函数的时候,麻烦就来了。例如:
上面的代码中,我们把user.clickHandler当成回调函数传入$(“button”)对象的click事件,user.clickHandler中的this将不再指向user对象转。谁调用了这个包含this的方法this就会指向谁。真正调用user.clickHandler的对象是button对象—user.clickHandler会在button对象的单击方法里执行。
注意,尽管我们使用user.clickHandler来调用clickHander方法(我们也只能这么做,因为clickHandler是定义在user对象上的),clickHandler方法本身会被现在被this所指向的上下文对象所调用。所以this现在指向的是$(“button”)对象。
当上下文改变时—当我们在其它对象而非原对象上执行某个方法的时候,显然this关键字不再指向定义了this关键字的原对象。
解决方案:
由于我们真的很想让this.data指向user对象的data属性,我们可以使用Bind/ Apply/ Call等方法来强制改变this所指向的对象。本系列的其它篇目将专门对Bind/ Apply/ Call进行讲解,文中介绍了如何在不同的情况强制改变this的值的方法。与其在本文大篇幅讨论,我强烈建议大家直接去读另外的篇目(译者注:晚些时候放出这里所说的“其它篇目”)。
为了解决前面代码中的问题,我们可以使用bind方法。
针对下面这行代码:
在内部方法中,或者说闭包中使用this,是另一个很容易被误解的例子。我们必须注意的是,内部方法不能直接通过使用this关键字来访问外部方法的this变量,因为this变量 只能被特定的方法本身使用。例如:
因为匿名方法中的this不能访问外部方法的this,所以在非严格模式下,this指向了全局的window对象
解决方案:
在进入forEach方法之前,额外使用一个变量来引用this。
正如下面的代码,很多JS开发人员喜欢使用变量that来设置this的值。但我个人不太喜欢用that这个名字,我喜欢使用让人一眼就能看懂this到底指向谁的那种名字,所以上面的代码中我使用了theUserObj = this。
3.方法被赋值给某个变量
this关键字有时候很调皮,如果我们把一个使用了this关键字的方法赋值给一个变量,我们来看会有什么有趣的事发生:
}
4.借用方法带来的问题
JS开发中,借用方法(borrowing methods)很常见。
我们来看下面的代码:
avg方法的this关键字指向的是gameController对象,如果使用appController调用该方法,this将会指向appController(但事实上这并不是我们期望的结果,因为我们只想借用方法的实现逻辑而非具体的数据来源)。
解决方案:
为了保证gameController只借用appController的avg方法的逻辑,我们使用apply方法:
gameController只借用了appController的avg方法,这时this将指向gameController,因为我们把gameController作为apply方法的第一个参数进行传递。apply方法的第一个参数将会显式设置this的取值。
结语
希望您在文中有所收获。现在你可以使用文中介绍的绝招(bind方法,apply方法,call方法,以及把this赋值给 一个变量)来对付跟this相关的任何问题。
正如已经了解到的,this在上下文改变、被作为回调函数使用、被不同的对象调用、或者方法被借用的情况下,this将一直指向调用当前方法的对象。
转载自:http://www.jb51.net/article/16863.htm
this是面向对象语言中的一个重要概念,在JAVA,C#等大型语言中,this固定指向运行时的当前对象。但是在javascript中,由于 javascript的动态性(解释执行,当然也有简单的预编译过程),this的指向在运行时才确定。这个特性在给我们带来迷惑的同时也带来了编程上的 自由和灵活,结合apply(call)方法,可以使JS变得异常强大。
2.变化的this
在JavaScript中,this通常指向的是我们正在执行的函数本身,或者是指向该函数所属的对象(运行时)。当我们在页面中定义了函数 doSomething()的时候,它的owner是页面,或者是JavaScript中的window对象(或 global对象)。对于一个onclick属性,它为它所属的HTML元素所拥有,this应该指向该HTML元素。
2.1在几种常见场景中this的变化
函数示例
function doSomething ()
{
alert(this.navigator); //appCodeName
this.value = "I am from the Object constructor";
this.style.backgroundColor = "# 000000";
}
1. (A)作为普通函数直接调用时,this指向window对象.
2. (B)作为控件事件触发时
1) inline event registration 内联事件注册 .将事件直接写在HTML代码中(<element
οnclick=”doSomething()”>), 此时this指向 window对象 。
2) Traditional event registration 传统事件注册 (DHTML方式).
形如 element.onclick = doSomething; 此时this指向 element对象
3) <element οnclick=”doSomething(this)”>作为参数传递可以指向element
3. (C)作为对象使用时this指向当前对象。形如:new doSomething();
4. (D)使用apply 或者call方法时,this指向所传递的对象。
形如:var obj={}; doSomething.apply(obj,new Array(”nothing”); //thisàobj
下面我来阐述如何在事件处理中来使用this,之后我会附加一些this相关的例子。
Owner
接下来文章中我们将要讨论的问题是:在函数doSomething()中this所指的是什么?
Javascript代码
function doSomething() {
this.style.color = '#cc0000';
}
function doSomething() {
this.style.color = '#cc0000';
}
在JavaScript中,this通常指向的是我们正在执行的函数本身(译者注:用owner代表this所指向的内容),或者是,指向该函数所属的对象。当我们在页面中定义了函数doSomething()的时候,它的owner是页面,或者是JavaScript中的window对象(或 global对象)。对于一个onclick属性,它为它所属的HTML元素所拥有,this应该指向该HTML元素。
这种“所有权”就是JavaScript中面向对象的一种方式。在Objects as associative arrays中可以查看一些更多的信息。
如果我们在没有任何更多准备情况下执行doSomething(),this关键字会指向window,并且该函数试图改变window的 style.color。因为window并没有style对象,这个函数将非常不幸的运行失败,并产生JavaScript错误。
Copying
因此如果我们想充分利用this,我们不得不注意使用this的函数应该被正确的HTML元素所拥有。换句话说,我们应该复制这个函数到我们的onclick属性。Traditional event registration会关心它。
Javascript代码
element.onclick = doSomething;
element.onclick = doSomething;
这个函数被完整复制到onclick属性(现在成为了函数)。因此如果这个event handler被执行,this将指向HTML元素,并且该元素的颜色得以改变。
这种方法使得我们能够复制这个函数到多个event handler。每次this都将指向正确的HTML元素:
这样你就可以最大限度使用this。每当执行该函数,this所指向的HTML元素都正确响应事件,这些HTML元素拥有doSomething()的一个拷贝。
Referring
然而,如果你使用inline event registration(内联事件注册)
Javascript代码
<element οnclick="doSomething()">
<element οnclick="doSomething()">
你将不能拷贝该函数!反而这种差异是非常关键的。onclick属性并不包含实际的函数,仅仅是一个函数调用。
Javascript代码
doSomething();
doSomething();
因此,它将声明“转到doSomething()并且执行它”。当我们到达doSomething(),this关键字又重新指向了全局的window对象,函数返回错误信息。
The difference
如果你想使用this来指向HTML元素响应的事件,你必须确保this关键字被写在onclick属性里。只有在这种情况下它才指向event handler所注册的HTML元素。
Javascript代码
element.onclick = doSomething;
alert(element.onclick)
element.onclick = doSomething;
alert(element.onclick)
你将得到
Javascript代码
function doSomething() {
this.style.color = '#cc0000';
}
function doSomething() {
this.style.color = '#cc0000';
}
正如你所见,this关键字被展现在onclick函数中,因此它指向HTML元素。
但是如果执行
Javascript代码
<element οnclick="doSomething()">
alert(element.onclick)
<element οnclick="doSomething()">
alert(element.onclick)
你将得到
Javascript代码
function onclick() {
doSomething()
}
function onclick() {
doSomething()
}
这仅仅是到doSomething()函数的一个引用。this关键字并没有出现在onclick函数中,因此它不指向HTML元素。
例子--拷贝
下面的例子中,this被写入onclick函数里:
Javascript代码
element.onclick = doSomething
element.addEventListener('click', doSomething, false)
element.onclick = function() {this.style.color = '#cc0000';}
<element οnclick="this.sytle.color = '#cc0000';">
element.onclick = doSomething
element.addEventListener('click', doSomething, false)
element.onclick = function() {this.style.color = '#cc0000';}
<element οnclick="this.sytle.color = '#cc0000';">
例子--引用
下述情况中,this指向window:
Javascript代码
element.onclick = function() {doSomething()}
element.attachEvent('onclick', doSomething)
<element οnclick="doSomething()">
element.onclick = function() {doSomething()}
element.attachEvent('onclick', doSomething)
<element οnclick="doSomething()">
注意attachEvent()的出现。Microsoft event registration model最主要的弊端是attachEvent()创建了一个指向函数的引用,而不是复制它。因此有时不可能知道哪个HTML正在处理该事件。
组合使用
当使用内联事件注册时,你可以将this发送到函数以至于可以正常使用:
Javascript代码
<element οnclick="doSomething(this)">
function doSomething(obj) {
//this出现在event handler中并被发送到函数
//obj指向HTML元素,因此可以这样:
obj.style.color = '#cc0000';
}