当前位置: 首页 > 文档资料 > TJS2 参考手册 >

类(class)

优质
小牛编辑
137浏览
2023-12-01

 “类”是创建“对象”时使用的“模型”。类中的变量称为“成员变量”、类中的函数称为“成员函数”、或者叫做“方法”、类中的属性称为“成员属性”。

 类的声明就象下面这样。

classclassname
{
//定义成员变量,成员函数和成员属性

functionclassname()//构造函数
{
}

functionfinalize()//这个相当于析构函数
{
}
}

 指定一个标识符作为类名。

 在类的内部定义变量、函数和属性,作为类的成员。

 在类的定义中必须有一个和类同名的成员函数,这个成员函数被称为“构造函数”。使用new 运算符创建这个类的对象的时候,这个函数就会作为 new 运算符的参数而被调用。

 在类的定义中同样存在一个名为“finalize”的特殊的成员函数。这个函数会在对象销毁时被调用、可以省略。就算不写也没关系(如果想在对象销毁时做一些自己想做的事,就可以自己定义这个函数,把想要进行的操作写进去)。

例:
classtest
{
functiontest()
{
//构造函数
//在这里填写对象创建时需要处理的内容。
variable=0;
}

functionfinalize()
{
//finalize方法
//在这里填写对象销毁时需要处理的内容。
}

functionmethod1()
{
//方法
System.inform(variable);
}

varvariable;//成员变量

propertyprop//成员属性
{
getter(){returnvariable;}
}
}

对类使用 instanceof 运算符和 "Class" 操作数时,会得到“真”(true)的结果(以上面的那个类为例、test instanceof "Class" 是“真”)。

对象的创建

 已定义的类的对象使用 new 运算符来创建。
 new 运算符的后面像函数那样指定类名和传递给构造函数的参数。

例:
classTest
{
varvariable1=getValue();//成员变量的初始化

functionTest(arg1,arg2)//构造函数
{
//使用new运算符创建对象时指定的参数会被传到arg1和arg2这两个变量里
}
}

varnewobject=newTest(1,2);//把1和2作为参数传入,创建test类的对象

 对象在创建时的处理顺序如下。

  1. 首先创建一个空的对象
  2. 注册方法和属性
  3. 创建成员函数 ( 有必要初始化的变量会在这时初始化 )
  4. 执行构造函数

Note
 即使在构造函数不需要参数的时候也不能省略 new 运算符和类名后面的 ( ) 。像 JavaScript 那样写成 new Test 是不行的。必须写成 new Test( ) 。

 注意,在类的方法和属性里创建该类的对象或该类的超类的对象的时候,象下面这样写是错误的。

例:
classTest
{
functionTest()//构造函数
{
}

functionfunc()
{
returnnewTest();//错误
}
}

 其原因在于,在类的方法或属性里只写 Test 的话、因为相对于类名的 Test 构造函数的 Test 在范围上更接近,这个 Test 会被当作构造函数来处理。为了避免这种情况,应该像下面这样明确地使用 global. 来指定类名 ( 因为类已经注册到 global 里了 )。

例:
classTest
{
functionTest()//构造函数
{
}

functionfunc()
{
returnnewglobal.Test();//这样写就OK了
}
}

对象的销毁

 在 TJS2 语言中,销毁对象时分为对象的无效化和对象的销毁两个步骤。
 对象被无效化的时候会调用 finalize 方法,然后该对象会被置上无效的标记。以后对该对象的所有操作都会失败并产生异常。可以通过调用 isvalid 运算符来判断一个对象是否已经失效。

 使用 invalidate 运算符来对对象进行无效化操作。

例:
classTest
{
varvariable;

functionTest()
{
//构造函数
variable=newAnotherClass();
}

functionfinalize()
{
//finalize方法会在对象被无效化的时候会调用
invalidatevariable;
}
}

varobject=newTest();//对象的创建

(略)

invalidateobject;//对象的无效化

 即使不使用 invalidate 运算符来操作,对象也会在没用了的时候被删除。没有被无效化的对象在这时候会被无效化。
 TJS2中并没有对“什么时候对象会被销毁”进行明确规定,销毁和无效化“随时可能发生”。未无效化的对象在被销毁时会先被无效化,此时可能会自动调用 finalize 方法。为了避免这一点,建议在对象用完之后使用 invalidate 运算符对其进行无效化。

Note
 invalidate 运算符的行为类似于 C++ 中的 delete 运算符。
 TJS2 中的 delete 运算符和 C++ 中的 delete 运算符是不一样的,TJS2 中的 delete 是用来消除成员或全局变量的运算符。虽然 delete 运算符无法直接对对象进行无效化或删除,但是成员或全局变量的删除会引起其内部对象的无效化和删除。

对对象的操作

 已创建的对象的成员变量、方法和成员属性可以通过 . (成员选择运算符) 或者 [ ] (间接成员选择运算符) 来操作。

例:
varobj=newMyLayer(window,window.prmaryLayer)
obj.method1();//方法的调用或者写成obj['method1']()也可以
obj.num=3;//成员变量的赋值或者写成obj['num']=3也可以
obj.prop1++;//成员属性的自增操作或者写成obj['prop1']++也可以

闭包(Closure)

 已创建的对象的方法和属性是携带着“属与哪个对象”的信息被注册到对象中的。
 因此,这些方法和属性从对象中取出,单独使用时,仍是对其所属对象的操作。这一机能被称为闭包(Closure)。那个被操作的对象称为上下文(context)

例:
varobj=newFooBarClass();//创建对象
obj.method();//用常规手段调用对象的方法
varobjmethod=obj.method;//把指向对象中方法的引用代入到objmethod变量
objmethod();//调用objmethod和调用obj.method()一样,都是对obj这个对象的操作

 incontextof 运算符提供了修改函数中“作为哪个对象的方法”这一信息,让某个函数作为任意对象中的方法来调用的功能。

例:
(objmethodincontextofobj2)();//对obj2进行操作
(objmethodincontextofthis)();//对this进行操作

继承

 使用关键字 extends 可以让一个类继承其他的类。继承操作中,源类的成员都会被目的类继承下来。
 被继承的类称之为超类(super class),继承出来的类称之为子类(sub class)
 以如下方式定义类。

例:
classClass1//超类
{
functionClass1()//Class1的构造函数
{
}

functionfinalize()//Class1finalize
{
}

functionmethod1()//method1
{
}
}

classClass2extendsClass1
{
functionClass2()//Class2构造函数
{
super.Class1();//调用Class1的构造函数
}

functionfinalize()//Class2finalize
{
super.finalize();
}
}

varobj=newClass2();//创建Class2的对象
obj.method1();//Class2调用了从Class1中继承的method1方法


 上在上面的例子中、Class2 继承了 Class1 。或者称为 Class2 由 Class1 派生 而来。
 Class2 的构造函数内调用了 Class1 的构造函数,Class2 的 finalize 内调用了 Class1 的 finalize 。子类的这些方法中没有调用超类的对应的方法的时候,其结果是不可预知的 ( 现在的版本中并没有对是否调用进行检测 ) ,所以请务必手动调用超类对应的函数。

 为了在子类中调用超类的成员,可以使用 super 运算符。这个关键字在子类中才能使用,代表所在类的超类。

 在进行了继承的情况下,用 new 运算符创建对象时的初始化顺序如下。

  1. 首先创建空的对象
  2. 注册方法和属性( 从超类的方法和属性开始,然后才是子类的 )
  3. 创建成员变量 ( 从超类的成员变量开始,然后才是子类的 )
  4. 调用子类的构造函数
  5. ( 从子类的构造函数里 ) 调用超类的构造函数

多重继承

 在 extends 后面填写多个超类的类名,就可以进行多重继承。

例:
classSubClassextendsClassA,ClassB
{
functionSubClass()//SubClass构造函数
{
ClassA();//调用ClassA的构造函数
ClassB();//调用ClassB的构造函数
}

functionfinalize()//Class2finalize
{
global.ClassA.finalize();
global.ClassB.finalize();
}
}



 在这里不能使用 super 关键字,必须明确指定超类的类名。调用超类的方法时用 global. 后面跟各个类名来对类进行操作,例如在子类中单独指定 ClassA 及其构造函数。因为类本身已经在 global 中注册了,所以使用 global. 可以对类进行操作。

 进行多重继承的情况下,用 new 运算符创建对象时的初始化顺序和非多重继承的情况相同,超类的方法和属性的注册顺序依照 extends 关键字后面的类名的顺序。如果被继承的类之间有同名的方法或属性的话,写在后面的类优先。被隐藏的方法和属性可以通过明确指定类名,用 global.ClassA.hiddenMethod() 这样的方式来调用。

override

 译者注:“オーバーライド”貌似应该翻译成 “override”,这在中文的C++相关资料中被称为“覆盖”。但是,在TJS2中没有虚函数和晚捆绑机制以及多态性,所以,以下将这一现象称为“隐藏”。感谢Bruce Eckel和他的《Thinking in C++》。

 子类中定义了和超类中同名的方法和属性时,超类的成员会被隐藏。这被称为“override”。
 上面那段说明中子类的 finalize 方法就把超类的 finalize 方法隐藏了。

例:
classClass1//超类
{
functionClass1()//Class1构造函数
{
}

functionfinalize()//Class1finalize
{
}

functionmethod1()//method1
{
(略)
}
}

classClass2extendsClass1
{
functionClass2()//Class2构造函数
{
super.Class1();//调用Class1的构造函数
}

functionfinalize()//Class2finalize
{
super.finalize();
}

functionmethod1()//隐藏Class1.method1
{
(略)
if(略)returnsuper.method1();
(略)
}
}

varobj=newClass2();//创建Class2对象
obj.method1();//调用Class2的method1方法


 在子类的方法或属性中可以用 super 关键字来调用超类的方法或属性。

 成员变量无法隐藏。成员变量是针对某一个对象进行注册的,子类可以和超类有相同名字的成员函数
但子类的成员变量会把超类的成员变量覆盖掉。