锁定老帖子 主题:[翻译]
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2006-09-15
下面我来阐述如何在事件处理中来使用this,之后我会附加一些this相关的例子。 Owner 接下来文章中我们将要讨论的问题是:在函数doSomething()中this所指的是什么? 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会关心它。 element.onclick = doSomething; 这个函数被完整复制到onclick属性(现在成为了函数)。因此如果这个event handler被执行,this将指向HTML元素,并且该元素的颜色得以改变。 这种方法使得我们能够复制这个函数到多个event handler。每次this都将指向正确的HTML元素: 这样你就可以最大限度使用this。每当执行该函数,this所指向的HTML元素都正确响应事件,这些HTML元素拥有doSomething()的一个拷贝。 Referring 然而,如果你使用inline event registration(内联事件注册) <element onclick="doSomething()"> 你将不能拷贝该函数!反而这种差异是非常关键的。onclick属性并不包含实际的函数,仅仅是一个函数调用。 doSomething(); 因此,它将声明“转到doSomething()并且执行它”。当我们到达doSomething(),this关键字又重新指向了全局的window对象,函数返回错误信息。 The difference 如果你想使用this来指向HTML元素响应的事件,你必须确保this关键字被写在onclick属性里。只有在这种情况下它才指向event handler所注册的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.sytle.color = '#cc0000';"> 例子--引用 下述情况中,this指向window: element.onclick = function() {doSomething()} element.attachEvent('onclick', doSomething) <element onclick="doSomething()"> 注意attachEvent()的出现。Microsoft event registration model最主要的弊端是attachEvent()创建了一个指向函数的引用,而不是复制它。因此有时不可能知道哪个HTML正在处理该事件。 组合使用 当使用内联事件注册时,你可以将this发送到函数以至于可以正常使用: <element onclick="doSomething(this)"> function doSomething(obj) { //this出现在event handler中并被发送到函数 //obj指向HTML元素,因此可以这样: obj.style.color = '#cc0000'; } 原文题目:The this keyword 原文链接:http://www.quirksmode.org/js/this.html 译文链接:http://www.blogjava.net/flyingis/archive/2006/09/15/69888.html 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2006-09-15
翻译不准确的地方请大家指正
|
|
返回顶楼 | |
发表时间:2006-09-19
主题:"this" of JavaScript [翻译]
链接:http://www.iteye.com/topic/24457 我不赞成该文的观点,所以针对该文提出我自己的理解,如有不当之处,还请各位多多指教! 我觉得该文章对Copy和Referring的概念的理解会使大家陷入困境。 下面我针对该文的一些行文附上自己的理解,方便大家对比。 引用 代码:
function doSomething() { this.style.color = '#cc0000'; } Copying 代码 element.onclick = doSomething; ps: 我觉得理解javascript这种解释型语言,应该从语言“运行时”这个角度来思考问题,注意“时间轴”上所发生的事情。 引用应该理解成js运行时指向一段内存空间的引用对象吧。 上面应该也只是引用而已,因为javascript的对象的属性都是引用,运行时,上面的例子只是将element的onclick属性指向了内存中分配的doSomething函数对象而已。 当然如果如果有两个引用对象开始都指向了同一个匿名对象,如果其中一个改变,并不会该变另外一个,因为其中一个的改变,其实是分配了新的对象给他,并不是改变原来他所指向的对象。 也许你会问: 如果element2.onclick = doSomething; 解释是:element2的onclick属性也指向了内存中分配的新的doSomething函数对象。 那下面的referring该如何解释呢?这里讲到了inline event registration(内联事件注册) 这是javascript和dom、bom对象相互协调工作的一种机制,我们可以这样理解,dom、bom对象的事件属性,在js里面的处理是这样的: js针对这些属性会让他们指向内存中的匿名函数对象A,在匿名函数对象里面再调用我们指定的函数B。运行时,如果B函数的参数中含有this自然是把A函数中的this对象传递了过去。 我感觉: 作者该文章,从自己的使用经验上很好的总结了this在javascript的事件机制中的灵活作用,但是作者的这样的分析却违背了javascript语言的本质。 正所谓,万变不离其宗! 我们应该牢牢把握javascript语言的本质:基于对象的、弱类型语言、解释型语言。 那么是不是javascript中对象就不存在属性复制呢?答案是 不存在! 这里我要纠正大家一直都在说的一个看法,就是prototype中对象的继承是通过“拷贝”来实现的,这种理解是错误的,这样的理解将导致copy和referring两种分类的误区,代码如下: Object.extend = function(destination, source) { for (property in source) { destination[property] = source[property]; } return destination; } 在这里我们可以看到destination对象和source对象的同名属性只是指向了内存相同的对象而已,根本没有拷贝的情况发生,反个角度思考,如果是拷贝,那么我们需要在内存中分别为他们生成新的空间了,实际情况并非如此。这也是js节约内存的一种做法,也许你会问当source对象的属性改变以后,destination的属性根本没有变化呀,是的,那是因为source对象的属性改变只是他指向了内存中重新生成的一个对象,原来的所指向的内存中的那个对象并没有改变。当然这一说法针对的是属性为对象类型 下面我附上一个例子: function MakeArray(n) { this[0] = "anuary" this[1] = "February" this[2] = "March" this[3] = "April" this[4] = "May" this[5] = "June" this[6] = "July" this[7] = "August" this[8] = "September" this[9] = "October" this[10] = "November" this[11] = "December" this['length'] = n return this } theMonths = new Object(); theMonths.fun=MakeArray; //这里打印出来看看也是有目的的哦 for(var p in theMonths){ alert("property:" + p + "==>value:" + theMonths[p]); } theMonths.fun(12); var p2 = new Object(); for(var p in theMonths ){ p2[p]=theMonths[p]; } //先打印一遍 for(var p in theMonths){ alert("property:" + p + "==>value:" + theMonths[p]); } for(var p in p2){ alert("property:" + p + "==>value:" + p2[p]); } theMonths.length=100; //再印一遍 要理解为什么我会这样打印2遍哦 for(var p in theMonths){ alert("property:" + p + "==>value:" + theMonths[p]); } for(var p in p2){ alert("property:" + p + "==>value:" + p2[p]); } 引用 Referring 然而,如果你使用inline event registration(内联事件注册) 代码 <element onclick="doSomething()"> 因此,它将声明“转到doSomething()并且执行它”。 当我们到达doSomething(),this关键字又重新指向了全局的window对象,函数返回错误信息。 The difference 如果你想使用this来指向HTML元素响应的事件,你必须确保this关键字被写在onclick属性里。只有在这种情况下它才指向event handler所注册的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() } ps: 上面这段代码只是解释了inline event registration(内联事件注册) 在javascript里是怎么实现的,与this的本质并没有任何关联。内联事件注册 上面我说了,js只是在内存中让元素的属性指向了新分配的匿名函数对象A,如果我们自己没有在标签中写函数的话,那么默认是空函数 什么也不做,如果我们写了函数B,那么在匿名空函数A中调用我们的函数B,如果B里面有this,this当然不会想当然地指向元素而是指向window了,否则自然是将A函数中的this传递进B函数。 引用 例子--拷贝 下面的例子中,this被写入onclick函数里: 代码 element.onclick = doSomething element.addEventListener('click', doSomething, false) element.onclick = function() {this.style.color = '#cc0000';} <element onclick="this.sytle.color = '#cc0000';"> 例子--引用 下述情况中,this指向window: 代码 element.onclick = function() {doSomething()} element.attachEvent('onclick', doSomething) <element onclick="doSomething()"> 注意attachEvent()的出现。 Microsoft event registration model最主要的弊端是attachEvent()创建了一个指向函数的引用,而不是复制它。 因此有时不可能知道哪个HTML正在处理该事件。 组合使用 当使用内联事件注册时,你可以将this发送到函数以至于可以正常使用: 代码 <element onclick="doSomething(this)"> function doSomething(obj) { //this出现在event handler中并被发送到函数 //obj指向HTML元素,因此可以这样: obj.style.color = '#cc0000'; } ps: 引用 Microsoft event registration model最主要的弊端是attachEvent()创建了一个指向函数的引用,而不是复制它。
该语句可能会让我们误以为inline event registration(内联事件注册)这样的处理方式有弊端,其实不然,这样做给了我们充分的自由,在触发了元素的事件以后,我们可以选择该处理什么范围内的事情,是全局还是针对该元素 控制权在我们自己手中。 |
|
返回顶楼 | |
发表时间:2006-09-28
@jianfeng008cn
感谢精彩回复。 总结一下你的回答,归纳为以下几点: 1.当两个对象引用A、B指向同一个对象C,引用A发生改变时,会在内存中产生一个新的对象,B所指的内容不发生变化; 2.JavaScript对内存使用是通过引用来进行的,不存在拷贝。 其实我觉得作者将Copying引用进来,本意应该是想将问题描述的更通俗一些,不料产生了歧异。"Copying"(加上了引号)之所以产生,正如你所说,是在对象内容发生改变的时候,在内存中会生成一个新的对象,原来对象还在内存中,这个过程就是"Copying",当然站在面向对象的角度上来说,当A指向对象C的时候,中间只有引用,而没有Copying。 <element onclick="doSomething()"> // X方案 element.onclick = doSomething(); // Y方案 上面两种方案的差别在于,X方案是在onclick的匿名函数中去调用doSomething(),Y方案中是将onclick指向了doSomething(),因此在doSomething()中使用this,其含义就产生了差别。在X方案中,如果显式的将this作为doSomething的参数传入,this自然就指向了onclick所属的HTML元素,得到我们所需要的结果。 |
|
返回顶楼 | |
发表时间:2006-09-29
脚本语言看似简单,用起来实际还挺复杂。
只是把复杂性推迟了而没有隐藏。 |
|
返回顶楼 | |
发表时间:2006-09-29
这个文章把简单的东东讲复杂了,在function内部的this,是指向调用这个function时的当前对象
写这样一个简单的script看看就明白了: <script> function test() { alert(this); } test.call(); test.call("String Object"); </script> 文章里面说的各种情况无非是Function.call(thisArg[, arg1[, arg2[, ...]]]),所传递的thisArg不一样而已。 参考文档: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Function:call http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Operators:Special_Operators:this_Operator |
|
返回顶楼 | |
发表时间:2006-09-30
<element onclick="doSomething()"> // X方案 element.onclick = doSomething(); // Y方案 你的Y方案和你说的意思不一样哦,你这样写是把doSomething函数的执行结果赋给了element.onclick 我想正确格式应该为: element.onclick = doSomething; // Y方案 |
|
返回顶楼 | |
发表时间:2006-09-30
Readonly 写道 这个文章把简单的东东讲复杂了,在function内部的this,是指向调用这个function时的当前对象
写这样一个简单的script看看就明白了: <script> function test() { alert(this); } test.call(); test.call("String Object"); </script> 文章里面说的各种情况无非是Function.call(thisArg[, arg1[, arg2[, ...]]]),所传递的thisArg不一样而已。 参考文档: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Function:call http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Operators:Special_Operators:this_Operator function showName(name){ alert("name:"+name); } var obj = new Object(); obj.fun=showName; obj.fun("jianfeng008cn"); delete obj.fun; function test() { alert(this); } test.call(); test.call("String Object"); 这两段代码的执行机制是一样的 ,属于语言支持的部分,这和上面讲的 事件机制 虽有几分类似 但在js的执行过程中还是有很大的不同的 ,我觉得这样去理解是可以,但不是正道。 |
|
返回顶楼 | |
发表时间:2006-10-01
jianfeng008cn 写道 <element onclick="doSomething()"> // X方案 element.onclick = doSomething(); // Y方案 你的Y方案和你说的意思不一样哦,你这样写是把doSomething函数的执行结果赋给了element.onclick 我想正确格式应该为: element.onclick = doSomething; // Y方案 纠正的极是 |
|
返回顶楼 | |
发表时间:2007-03-06
引用 function test() {
alert(this); } test.call(); test.call("String Object"); 这种理解方案很好~不过这篇帖子我建议不需要太过于仔细的去理解了。因为是真的把简单的问题弄复杂了。 其实很简单。在function(){}中引入this是一个动态的变量。谁去调就指向谁。这点和java语言是不相同的。这也正是javascript的魅力所在。不过一般象上面我引用的示例代码中在call中第一个函数传递串的还很少见。一般都是传对象,取代test类内部的this。不过如果确实是 引用 test.call("String Object"); 那么引用 alert( this.constructor ); 你可以发现是String,这不奇怪,但引用 alert( typeof this ); 你会发现是Object,即js解析器会new出一个内存空间来。相当于引用 test.call( new String( "XXXX" ) )
|
|
返回顶楼 | |