脚本进阶

在阅读到此章节时,默认您已经对脚本系统较为熟悉,包括装饰器等。否则,请参阅:

实例化

通过脚本定义一个 Foo 类和 Bar 类,Foo 类需要使用 Bar 类定义的属性,此时可以在 Foo 类中将 Bar 类直接 new 出一个对象:

  1. class Foo {
  2. public bar: Bar = null;;
  3. constructor() {
  4. this.bar = new Bar();
  5. }
  6. }
  7. let bar = new Foo();

实例方法

实例方法请在原型对象中声明:

  1. class Foo {
  2. public text!: string;
  3. constructor() {
  4. this.text = "this is sprite"
  5. }
  6. // 声明一个名叫 "print" 的实例方法
  7. print() {
  8. console.log(this.text);
  9. }
  10. }
  11. let obj = new Foo();
  12. // 调用实例方法
  13. obj.print();

判断类型

需要做类型判断时,可以用 TypeScript 原生的 instanceof

  1. class Sub extends Base {
  2. }
  3. let sub = new Sub();
  4. console.log(sub instanceof Sub); //true
  5. console.log(sub instanceof Base); //true
  6. let base = new Base();
  7. console.log(base instanceof Sub); // false

静态变量和静态方法

静态变量或静态方法可以用 static 声明:

  1. class Foo {
  2. static count = 0;
  3. static getBounds() {
  4. }
  5. }

静态成员会被子类继承,继承时 Creator 会将父类的静态变量 浅拷贝 给子类,因此:

  1. class Object {
  2. static count = 11;
  3. static range: { w: 100, h: 100 }
  4. }
  5. class Foo extends Object {
  6. }
  7. console.log(Foo.count); // 结果是 11,因为 count 继承自 Object 类
  8. Foo.range.w = 200;
  9. console.log(Object.range.w); // 结果是 200,因为 Foo.range 和 Object.range 指向同一个对象

如果不需要考虑继承,私有的静态成员也可以直接定义在类的外面:

  1. // 局部方法
  2. doLoad(sprite) {
  3. // ...
  4. };
  5. // 局部变量
  6. let url = "foo.png";
  7. class Sprite {
  8. public url = '';
  9. load() {
  10. this.url = url;
  11. doLoad(this);
  12. };
  13. };

继承

父构造函数

注意:不论子类是否有定义构造函数,在子类实例化前,父类的构造函数都会被自动调用。

  1. class Node {
  2. name: string;
  3. constructor() {
  4. this.name = "node";
  5. }
  6. }
  7. class Sprite extends Node {
  8. constructor() {
  9. super();
  10. // 子构造函数被调用前,父构造函数已经被调用过,所以 this.name 已经被初始化过了
  11. console.log(this.name); // "node"
  12. // 重新设置 this.name
  13. this.name = "sprite";
  14. }
  15. }
  16. let obj = new Sprite();
  17. console.log(obj.name); // "sprite"

重写

所有成员方法都是虚方法,子类方法可以直接重写父类方法:

  1. class Shape {
  2. getName() {
  3. return "shape";
  4. }
  5. };
  6. class Rect extends Shape {
  7. getName () {
  8. return "rect";
  9. }
  10. };
  11. let obj = new Rect();
  12. console.log(obj.getName()); // "rect"

get/set 方法

如果在属性中定义了 get/set,那么在访问属性的时候,就能触发预定义的 get/set 方法。

get

在属性中定义 get 方法:

  1. @property
  2. get num() {
  3. return this._num;
  4. }
  5. @property
  6. private _num = 0;

get 方法可以返回任意类型的值。
定义了 get 方法的属性可以显示在 属性检查器 中,可以在代码中直接访问。

  1. class Sprite {
  2. @property
  3. get width() {
  4. return this._width;
  5. }
  6. @property
  7. private _width = 0;
  8. print(){
  9. console.log(this.width);
  10. }
  11. };

注意

  • 属性定义了 get 方法之后就不能被序列化,也就是 serializable 参数不可用。例如下面的写法,width 属性既不会在编辑器上显示,也不会序列化。

    1. get width() {
    2. return this._width;
    3. }
    4. @property({ type: CCInteger, tooltip: "The width of sprite" })
    5. private _width = 0;
  • 定义了 get 方法的属性如果需要在编辑器中显示,需要定义 property。例如下面的写法,width 属性如果去掉 @property 就不会在编辑器上呈现,_width 属性会序列化。

    1. @property
    2. get width() {
    3. return this._width;
    4. }
    5. @property({ type: CCInteger, tooltip: "The width of sprite" })
    6. private _width = 0;
  • 定义了 get 方法的属性本身是只读的,但返回的对象并不是只读的。开发者依然可以通过代码修改对象内部的属性,例如:

    1. get num() {
    2. return this._num;
    3. }
    4. @property
    5. private _num = 0;
    6. start() {
    7. console.log(this.num);
    8. }

set

在属性中定义 set 方法:

  1. set width(value) {
  2. this._width = value
  3. }
  4. private _width = 0;
  5. start() {
  6. this.width = 20;
  7. console.log(this.width);
  8. }

set 方法接收一个传入参数,这个参数可以是任意类型。set 方法一般和 get 方法一起使用:

  1. @property
  2. get width() {
  3. return this._width;
  4. }
  5. set width(value) {
  6. this._width = value;
  7. }
  8. @property
  9. private _width = 0;

注意:set 方法不定义属性。