鸟食轩

 Microsoft .NET[C#] MVP 2003
随笔 - 429, 文章 - 235, 评论 - 5529, 引用 - 356
数据加载中……

JScript中的"this"关键字使用方式补充

    在"JavaScript中this关键字使用方法详解"一文中,我曾例举了在JavaScript和JScript中的8种this关键字的方式。这不又发现还有两种this关键字的使用方式当时没有说到,现补充说明一下。并且通过第一种this关键字使用的说明,能让我们更好的理解JavaScript作为Object-Based语言的本质。

    一种是和JavaScript类中的定义有关,我们知道当我们定义如下类的时候: 

function JSClass()
{
}

JSClass.prototype.m_Properties = 100;

JSClass.prototype.ToString = function()
{
    alert(
this.m_Properties);
}

    方法ToString中的this.m_Properties就是100,那么下面这种定义呢?

function JSClass()
{
}

JSClass.m_Properties 
= -100;

JSClass.ToString()
{
    alert(
this.m_Properties);
}

    这时ToString中的这个this.m_Properties是啥?是-100。一定吗?这个不一定了,这得看我们怎么调用这个ToString方法。

JSClass.ToString();
var fun = JSClass.ToString();
fun();

    这时的运行结果居然是:-100和undefined。真是郁闷哈,怎么才能运行fun得到-100呢?需要这样来为fun赋值:
 
var fun = function() { JSClass.ToString(); }
fun();

    呵呵,这样就是-100了。好像挺废话的,最终不还是调用的JSClass.ToString()吗?这个我们后面再说,看看把这两个JSClass合在一起是什么情况呢?

function JSClass()
{
}

JSClass.m_Properties 
= -100;
JSClass.prototype.m_Properties 
= 100;

JSClass.ToString 
= function()
{
    alert(
this.m_Properties);
}

JSClass.prototype.ToString 
= function()
{
    alert(
this.m_Properties);
}

    这两个ToString()方法,和里面的this关键字它们是什么关系呢?看下面的示例:

var jsclass = new JSClass();
jsclass.ToString();
JSClass.ToString();

    结果为:100和-100。这里的jsclass实际上是JavaScript的语言机制,通过new关键字的说明来创建的一个新的实例。而JSClass是什么呢?他们本就是对象实例,只是长得像个函数,也像一个类的说明。对于this的问题,第一个jsclass.ToString()方法里的this是指新创建实例,而JSClass.ToString()方法里的this是值得JSClass这个对象实例。

    理解了这两个this指代的东西的不同本质,也就能更好的明白,为什么JavaScript叫做Object-Based的语言,而更加清楚它和Object-Oriented语言的本质区别。

    回头再说一下补充第二中this的使用方法,就是在使用eval方法的时候,这个时候的this是什么呢?看看:

alert(this === eval("this"));

    的结果是什么?是true!这是因为eval内代码被执行的作用域就是当前页面本身的说。

posted on 2005-06-07 13:37 birdshome 阅读(3459) 评论(16)  编辑 收藏 网摘 所属分类: Jscript&Dhtml开发

评论

#1楼   回复  引用    

hehe,都快晕了
2005-06-08 01:09 | robaggio

#2楼   回复  引用    

JSClass.m_Properties = -100;

这种方式是定义静态属性,当然不能用this来调用啦。
2005-08-15 19:20 | Avlee

#3楼[楼主]   回复  引用  查看    

@Avlee
。。。你看完文章了吗?
2005-08-15 22:21 | birdshome      

#4楼   回复  引用  查看    

有种类叫Function,任何函数/构造器,都是它的实例
改变这个构造器的原型中的属性自然和改变这个构造器对象的属性是不一样的
2005-08-29 23:13 | ShiningRay      

#5楼   回复  引用    

一点心得,请birdhome兄斧正!
在javascript里面,自定义类的定义中的this和该类的方法定义中的this不一样,得用call函数或者apply函数转换。这对于用惯了c#刚刚接触javascript面向对象编程的朋友(本人就是其中之一)来说是个容易出错的地方。javascript函数定义中的this就是指函数本身,不能想当然的认为它指的是外层的类实例或者其他什么别的东西。prototype.js里面的bind方法可以用来转换这个this。而且利用javascript函数的一个似乎是比较奇怪的特性——函数定义的参数个数可以跟函数调用时传入的参数个数完全不同,可以很方便的实现转换过程中的多参数传递问题,也就是说只要写一个bind函数,就可以转换任意函数的上下文。

Function.prototype.Bind = function(obj){
var method = this;
return function(){
method.apply(obj,arguments);
}
}

在类定义中,把obj换成this,然后调用已定义方法的Bind方法,就可以转换方法定义中的this指针了。上面函数中的arguments是调用时实际传给function(){...}的参数数组,所以比如我要写一个公开的Send(arg1,arg2,arg3),它里面有this指针,怎么办呢?我先写一个Pri_Send(arg1,arg2,arg3),然后在类定义(注意是类定义中)中这么写:
this.Send = this.Pri_Send.Bind();
大功告成,现在可以尽情地在类的实例上使用Send方法了。而且对于那些参数个数不同或者根本没有参数的方法,Bind()方法同样适用,这都是arguments的功劳,呵呵。
此段代码在IE6.0,FireFox1.0.7,opera 8.51下测试通过。应该没有太大的平台问题。
2006-01-13 17:48 | hzy[未注册用户]

#6楼[楼主]   回复  引用  查看    

@hzy
"自定义类的定义中的this和该类的方法定义中的this不一样",这句话我没有明白,你是说的这种情况吗?
function JSClass()
{
    
this.Property = 'abc';
}


JSClass.prototype.Foo()
{
    alert(
this.Property);
}

这两个this是指示的同一个东西呀。
2006-01-13 22:18 | birdshome      

#7楼   回复  引用    

真的非常惭愧,今天早上我用了简单的几行代码就证明我上面的结论是错误的。
Bind()方法有用且正确,但在这里不需要,因为javascript并不像我昨天认为的那样奇怪,两个this的确指的是同一个类型实例。
经过仔细的审查(我自己的代码),发现了这个错误观点的根源。
虽然在birdhome兄所说的情况下,两个this指的是同一个东西,但在某些特定情况下,却并非如此。其根本原因还请birdhome兄多多指教。谢。
我的代码中就有两处这样的情况。
我使用正流行的ajax设计了一个简单的AsyncCommunicator类,用来提供更丰富的编程接口。其中两个功能就是“更细粒度的事件处理”和“超时处理”。
AsyncCommunicator类包含一个XmlHttpRequest属性,这个属性本身就是一个XMLHttpRequest类的实例。因此在AsyncCommunicator类的定义中,可以如下写法访问这个实例:
this.XmlHttpRequest
现在,我要给AsyncCommunicator类定义更多事件,方法是利用XMLHttpRequest的onreadystatechange属性。如何做呢?
我是这么写的:
AsyncCommunicator{
this.XmlHttpObj= GetNewXmlHttpRequest();
this.XmlHttpObj.onreadystatechange = this.EventStateChangeHandler;
this.EventStateChangeHandler = function(){
switch(this.XmlHttpObj.readyState){ case 3: this.OnTransferBack(this); break; case 4: if (!this.Pri_Aborted){
this.Pri_HasReceived = true; this.OnReceive(this);
switch(parseInt(((this.XmlHttpObj.status).toString()).charAt(0))){ case 1: this.OnReceive1xx(this); break;
case 2:
this.OnReceive2xx(this);
break;
case 3: this.OnReceive3xx(this);
break;
case 4:
this.OnReceive4xx(this);
break;
case 5: this.OnReceive5xx(this);
break;
default:
break;
} //switch } //(!this.Pri_Aborted) break; default: break;
} //switch
} //function
} //AsyncCommunitor

注意这句this.XmlHttpObj.onreadystatechange = this.EventStateChangeHandler,这句就是加Bind()方法正确,不加就会出错的源头。经过思考,我觉得这个this,更确切一点说,函数的上下文,已经变了,不再是AsyncCommunitor,究竟是什么我也没试出来,是否是XmlHttpObj?请birdhome兄指教。如果经过Bind方法转换,把this赋给Bind()方法作参数,就可与更正这个问题。如下:
this.XmlHttpObj.onreadystatechange = this.EventStateChangeHandler.Bind(this);
我被这个问题折磨了一个晚上,因此一旦解决就紧接着犯了以偏概全的“左”倾错误,呵呵,把所有内部定义的方法都看成这种情况,因此每个方法都加了Bind()方法,想来真是惭愧,正如birdhome兄所言,通常情况下无须如此,因此已经改正。

还有第二个情况,就是
this.AbortWhenTimeOut方法的调用。这个方法定义如下。:
this.Ori_AbortWhenTimeOut = function(){ if (!this.Pri_HasReceived) { this.Pri_Aborted = true; this.OnTimeOut(this); this.Abort(); } }
在xmlhttprequest对象的open方法之后和send方法之前加入window.setTimeout方法(放在send之后就不行,奇怪,请指教),参数中就包括这个this.AbortWhenTimeOut方法,现在想来,很可能是window.setTimeout方法改变了this.AbortWhenTimeOut方法的上下文,导致该方法定义中的this指针的含义发生变化,因此不能正常执行。同样,自从服用了Bind()之后,腰不酸了,背不痛了,腿也不抽筋了,走路也有劲了。呵呵。

关于上下文的这些奇妙变化(至少我觉得挺奇妙的,可能是受才智水平所限,惭愧),birdhome兄能否指教一二?
谢谢。
2006-01-14 15:49 | hzy[未注册用户]

#8楼   回复  引用    

PS,钦佩birdshome兄的专业水平。我隐约觉得javascript语言的诸多特性,是逻辑性很强的整体(其实事实也必然如此,用不着我去觉得,呵呵),例子举的再多也只是“身在此山中”。遗憾我目前的水平还远远远远没有达到可以跳出语言之外来论述语言的原理和本质。不知道birdshome兄是否有这个空闲和兴趣对javascript语言做一个总结性发言,不只是how 更重要的是why。
这只是个非常即兴的提议,更多的是某种期待。无论如何,我希望我自己也能做到,someday。
这差不多应该也属于技术区的非技术,呵呵,类似的话不会说第二次了。
以后只讨论技术。
2006-01-14 16:49 | hzy[未注册用户]

#9楼[楼主]   回复  引用  查看    

@hzy
    this.XmlHttpObj.onreadystatechange = this.EventStateChangeHandler后,EventStateChangeHandler方法里的this在事件触发后是指的window对象。第二个使用window.setTimeout也是同样的问题,使用setTimeout触发的方法,其内部的this也是指的window。
    至于:对javascript语言做一个总结性发言。我不是很明白你具体指的内容,毕竟JavaScript这门大家认为"简单"的小脚本,还是有挺多可圈点的内容,似乎很难简单的就能"总结性"道来:(
2006-01-15 02:11 | birdshome      

#10楼   回复  引用    

我给window定义(或者说覆盖?)了一个toString方法,让它返回"window",然后在两个方法开头调用一下,果然this指的是window。多谢birdshome兄!
现在我很想知道的是——为什么?
这两个问题背后的共性,或者说原理或者本质,究竟是什么?如何区分某个方法调用是否是这种情况?
请birdshome兄不吝赐教。
感谢!
2006-01-15 09:21 | hzy[未注册用户]

#11楼   回复  引用    

有本书叫《JavaScript: The Definitive Guide, 4th Edition》,说javascrpt是prototype oriented,不是object oriented,birdshome说是object based,概念真多啊
2007-03-03 13:36 | xty[未注册用户]

#12楼   回复  引用    

关于this的值我认为主要有三种方式:
1、调用某对象的成员函数时,该函数的this指向的是该对象
2、在全局或某函数中调用当前作用域范围中定义的函数,则使用当前的this,全局中的this即是全局对象。
function OuterFoo()
{
this.Name = 'Outer Name';

function InnerFoo()
{
var Name = 'Inner Name';
alert(Name + ', ' + this.Name);
}
return InnerFoo;
}
OuterFoo()();

InnerFoo中的this指向的即是OuterFoo中的this,而这里是OuterFoo()()如此调用,那么OuterFoo的this即是全局对象,在OuterFoo()()中跟一句alter(Name),显示Outer Name,证明这种看法是正确的
3、new Func()时,Func函数的this指向的是新生成的Object对象,也就是生成的自定义类,当然也包括系统类

以上这三点,可以完全解释你说的10种this关键字用法
2007-05-10 00:00 | 风舞影天[未注册用户]

#13楼[楼主]   回复  引用  查看    

@风舞影天
js中所有的this其实只有一种使用模式,就是对变量、方法的parent scope(也可以叫container)的指示。你说对吗?
2007-05-14 14:10 | birdshome      

#14楼   回复  引用    

@birdshome
我认为应该可以这样理解,首先function并不是某个“类”或对象所特有的东西,它们是独立存在的,这些类和对象实际上只是有对该function的引用,在我所提及的三种不同的调用方式中,最终引擎可能都会通过某种方式去设置调用function时的当前this所指向的是谁
1:在调用对象的函数之前,引擎设置其this
2,closure的形式其实也是设置了this
3,在new操作的native code中,不仅会new出Object,而且会调用函数并设置该函数的this为刚刚new出来的Object

这样一来应该就是叫做parent scope了
2007-06-20 14:17 | 风舞影天[未注册用户]

#15楼   回复  引用    

function CFoobar()
{
this.Name = "CFoobar";
this.GetInner = function()
{
return InnerFunction;
}
function InnerFunction()
{
alert( ".."+this.Name )
}
this.CallInner = InnerFunction;
}
var o = new CFoobar();
var f = o.GetInner();
f(); // 1
o.CallInner(); // 2
if( f == o.CallInner )
alert( "same function" );


1和2最后调用的都是同一个函数,但他们的this不同,1是全局对象,即window,2是自定义对象o
2007-07-05 15:39 | 风舞影天[未注册用户]

#16楼   回复  引用    

说了这么多,没有说清事情的本质。

1. Javascript里的this和OO里的this不一样,哪个对象调用this所在的函数,this就指向哪个对象。

2. 函数调用时没有明确指明caller对象的,为全局对象,即window

3. Javascripe里所谓的类(型)实际上都是type为Function的特殊对象,不信alert( typeof (Object) );
也即Javascript里的函数实际上是类型为Function的特殊对象。
2008-07-19 19:21 | bbcc[未注册用户]



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 169168




相关文章:

相关链接: