类(class)

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

 类的声明就象下面这样。

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

    function classname() // 构造函数
    {
    }

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



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

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

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

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


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

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

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

        var variable; // 成员变量

        property prop // 成员属性
        {
            getter() { return variable; } 
        }
    }


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

对象的创建

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

例:
    class Test
    {
        var variable1 = getValue(); // 成员变量的初始化

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

    var newobject = new Test(1, 2); // 把 1 和 2 作为参数传入,创建 test 类的对象


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

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

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


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

例:
    class Test
    {
        function Test() // 构造函数
        {
        }

        function func()
        {
            return new Test(); // 错误
        }
    }


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


例:
    class Test
    {
        function Test() // 构造函数
        {
        }

        function func()
        {
            return new global.Test(); // 这样写就OK了
        }
    }

对象的销毁

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

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


例:
    class Test
    {
        var variable;

        function Test()
        {
            // 构造函数
            variable = new AnotherClass();
        }

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

    var object = new Test(); // 对象的创建

    (略)

    invalidate object; // 对象的无效化


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


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

对对象的操作

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

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

闭包(Closure)

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

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


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

例:
    (objmethod incontextof obj2)(); // 对 obj2 进行操作
    (objmethod incontextof this)(); // 对 this 进行操作

继承

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

例:
    class Class1 // 超类
    {
        function Class1() // Class1 的构造函数
        {
        }

        function finalize() // Class1 finalize
        {
        }

        function method1() // method1
        {
        }
    }


    class Class2 extends Class1
    {
        function Class2() // Class2 构造函数
        {
            super.Class1(); // 调用 Class1 的构造函数
        }

        function finalize() // Class2 finalize
        {
            super.finalize();
        }
    }

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

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

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

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

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

多重继承

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

例:
    class SubClass extends ClassA, ClassB
    {
        function SubClass() // SubClass 构造函数
        {
            ClassA(); // 调用 ClassA 的构造函数
            ClassB(); // 调用 ClassB 的构造函数
        }

        function finalize() // Class2 finalize
        {
            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 方法隐藏了。

例:
    class Class1 // 超类
    {
        function Class1() // Class1 构造函数
        {
        }

        function finalize() // Class1 finalize
        {
        }

        function method1() // method1
        {
            (略)
        }
    }


    class Class2 extends Class1
    {
        function Class2() // Class2 构造函数
        {
            super.Class1(); // 调用 Class1 的构造函数
        }

        function finalize() // Class2 finalize
        {
            super.finalize();
        }

        function method1() // 隐藏 Class1.method1 
        {
            (略)
            if(略) return super.method1();
            (略)
        }
    }

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

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

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