TypeScript学习笔记八:类

8 类(Classes)

  Ts扩展了js类,包括类型参数(type parameters)、实现子语句(implements clauses)、可访问性修饰符(accessibility modifiers)、成员变量声明(member variable declarations)和构造器参数特性声明(parameter property declarations in constructors)。

8.1 类声明(Class Declarations)

  类声明声明一个类类型(class type)和一个构造器函数(constructor function)。

  ClassDeclaration:  ( Modified )
    class   BindingIdentifieropt   TypeParametersopt   ClassHeritage   {   ClassBody   }

  类声明:

    class 绑定标识符  类型参数(可选)  类继承  {  类体  }

  类声明在容器声明空间(containing declaration space)中引入一个命名类型(类类型)和一个命名值(构造器函数)。类类型是由类体中声明的实例成员和继承于基类的实例成员组成。构造器函数是函数体中的构造器声明、静态成员声明和继承于基类的静态成员组成。构造器函数初始化并返回一个类类型的实例。

  类声明的标识符不能与预定义类型名相冲突。当类声明发生在一个导出默认声明(export default declaration)中时,标识符是可选的。

  一个类可选地具有类型参数。具有类型参数的类被称为泛型类(generic class)。泛型类的类型参数的作用域为整个类声明,可被类体和子类引用。

  下例中,我们在容器声明空间中同时引入了一个命名类型“Point”(类类型)和一个命名值“Point”(构造器函数)。

class Point{
  constructor(public x: number, public y: number) { } public length() { return Math.sqrt(this.x * this.x + this.y * this.y); } static origin = new Point(0, 0); }

  此命名类型“Point"等同于:

interface Point {
    x: number;
    y: number;
    length(): number;
}

  此命名值”Point"是一个构造器函数,它的类型相当于以下声明:

var Point: {
    new(x: number, y: number): Point;
    origin: Point;
};

  在引用一个类时,ts通过上下文(context)来区分引用的是类型还是构造器函数。例如,下面的赋值表达式:

var p: Point = new Point(10, 20);

  类型注释中的标识符“Point"被指代为类类型,然而"new"表达式中的标识符“Point”则被指代为构造器函数对象。

8.1.1 类继承规范(Class Heritage Specification)

  类继承包含可选的“extends”和”implements”子句。“extends”子句指定类的基类,“implements”指定类需要实现的接口集合。

  ClassHeritage:  ( Modified )
    ClassExtendsClauseopt   ImplementsClauseopt

  ClassExtendsClause:
    extends    ClassType

  ClassType:
    TypeReference

  ImplementsClause:
    implements   ClassOrInterfaceTypeList

  类继承:

    类扩展子句(可选)  实现子句(可选)

  类扩展子句:

    extends  类类型

  实现子句

    implements  类或接口类型列表

  包含”extends”子句的类被称为派生类(derived class),“extends“子句中指定的类是派生类的基类(base class)。当一个类继承规范省略了”extends“子句时,此类就没有基类。 

  类继承指定必须满足以下约束,否则会出现编译时报错。

  • 如果”extend“子句指定了类型引用,那么此类型必须为类类型。此外,当作为表达式计算时,此类型引用的类型名称部分需要引用类构造器函数。
  • 类声明不能直接或间接地指定源于同一声明的基类。换言之,一个类不能直接或间接地成为自身的基类,也不能成为类型参数。
  • 类的“this”类型必须能分配给基类引用和“implements"子句中的每一个类型引用。
  • 类声明中的构造器函数类型必须能分配给基类构造器函数类型,忽略构造签名。

  下例违反了上面的第一个约束

class A { a: number; }
namespace Foo {
    var A = 1;
    class B extends A { b: string; }
}

  当被作为表达式计算时,”extends"子句中的类型引用”A"没有引用"A”的类构造器函数(相反,它引用了本地变量“A")。

  当一个类重载(override)一个或多个基类成员,存在新的非兼容成员时,就会违反上面后两个约束。

  需要注意的是,由于ts有一个结构类型系统(structural type system),一个类无需显式地说明实现了一个接口,而是包含一个相应的实例成员集合就够了。“implements"子句提供了一种用来断言和验证此类包含相应的实例成员的机制,但此机制不能作用于类类型。

8.1.2 类体(Class Body)

  类体保护了零个或多个构造器或成员声明。类体中不允许存在语句,语句只能位于构造器或成员当中。

  ClassElement:  ( Modified )
    ConstructorDeclaration
    PropertyMemberDeclaration
    IndexMemberDeclaration

  类体可选地包含一个构造器声明。具体参见8.3节。

  成员声明被用来声明实例和静态成员。属性成员声明参见8.4节,索引成员声明参见8.5节。

8.2 成员(Members)

  一个类的成员包括当前类体中声明的成员和从基类继承而来的成员。

8.2.1 实例和静态成员(Instance and Static Members) 

  成员可以是实例成员或静态成员。

  实例成员是类类型的成员和相关的”this“类型。在构造器、实例成员函数和实例成员访问器中,this的类型是类的”this“类型。

  用”static“修饰符声明的静态成员是具有构造器函数类型的成员。在静态成员函数和静态成员访问器中,this的类型是构造器函数类型。

  不能在静态成员声明中引用类类型参数。

8.2.2 可访问性(Accessibility)

  属性成员有公共(public),私有(private )或保护(protected)可访问性。默认为公共可访问性,我们可以通过“public“,“private“,“protected“修饰符来显式地在属性成员声明中指定所期望的可访问性。

  公共属性成员随处可访问而无任何限制。

  私有属性成员只能被当前类中的元素访问。例如一个声明在类“C”中的私有成员“M”只能被“C”类体中的元素访问。

  保护属性成员只能被当前类中的元素和派生于它的类访问,并且一个保护实例属性成员必须通过一个类的实例或类的子类来访问。例如,一个声明在类“C”中的保护成员“M”只能在“C”的类体中或派生于“C”的类访问。

  私有和保护可访问性只能执行在编译时并且仅仅是一种意图的表示。因为js没有提供在一个对象上创建私有和保护属性的机制,所以不能在运行时的动态代码中执行私有和保护修饰符。例如,可以通过把一个对象的静态类型改变为“Any”,动态访问成员的方式来代替私有和保护可访问性。

  下例演示了私有和保护可访问性:

class A {
    private x: number;
    protected y: number;
    static f(a: A, b: B) {
        a.x = 1;  // Ok
        b.x = 1;  // Ok
        a.y = 1;  // Ok
        b.y = 1;  // Ok
    }
}
class B extends A {
    static f(a: A, b: B) {
        a.x = 1;  // Error, x only accessible within A
        b.x = 1;  // Error, x only accessible within A
        a.y = 1;  // Error, y must be accessed through instance of B
        b.y = 1;  // Ok
    }
}

  在类”A“中,访问”x“是允许的,因为”x“就声明在类”A“中,访问”y“也是允许的,因为两个”y”都是被一个“A”实例或一个“A”的派生类型替换了。在类“B“中,不能访问”x“,第一个”y“访问是错误的,因为它是被一个”A“实例替代的,而”A”不是从“B”派生而来的。

8.2.3 继承和重写(Inheritance and Overriding)

  一个派生类继承了所有的基类的成员。继承意味着一个派生类隐式地包含基类所有的非重写成员。只有公共和保护属性成员才能被重写。

  当派生类的属性成员与基类属性成员同名且同类型(实例或静态)时,我们就说这个派生类中的属性成员重写了其基类的属性成员。重写的属性成员类型必须能够赋给被重写属性成员的类型,否者会报编译时错误。

  基类实例成员函数可以被派生类实例成员函数重写,但是不能被其他成员重写。

  基类实例成员变量和访问器可以被派生类实例成员和访问器重写,但是不能被其他成员重写。

  基类静态属性成员可以被派生类的任何具有兼容类型的静态属性成员重写。

  当派生类索引成员与基类索引成员具有相同的索引类型(字符串或数字)时,我们就说这个派生类中的这个索引成员重写了其基类的索引成员。重写的索引成员类型必须能够赋给被重写索引成员的类型,否则会报编译时错误。

8.2.4 类类型(Class Types)

  类声明声明了一个新命名类型:类类型。在类的构造器和实例成员函数中,this的类型就是类类型的“this”类型。类类型包含以下成员:

  • 每一个实例成员变量声明的属性。
  • 每一个实例成员函数声明的属性,此属性的类型为函数类型。
  • 每一个唯一命名实例成员访问器声明的属性。
  • 每一个通过public,private或protected修饰符声明的构造器参数的属性。
  • 每一个实例索引成员声明的索引签名。
  • 未被重写的所有的基类实例属性或索引成员。

  一个类的所有实例属性成员必须满足其索引成员的隐式约束。

class A {
    public x: number;
    public f() { }
    public g(a: any) { return undefined; }
    static s: string;
}
class B extends A {
    public y: number;
    public g(b: boolean) { return false; }
}

  类类型“A”等同于:

interface A {
    x: number;
    f: () => void;
    g: (a: any) => any;
}

  类类型“B"等同于:

interface B {
    x: number;
    y: number;
    f: () => void;
    g: (b: boolean) => boolean;
}

  需要注意的是,静态声明在类类型中是无效的,”B"中声明的“g"重写了”A"中的“g"。

8.2.5 构造器函数类型(Constructor Function Types)

  通过类声明引入的构造器函数的类型被称为构造器函数类型。它包含以下元素:

  • 如果类不包含构造器声明和基类,一个无参的构造签名,具有与类相同类型的参数和返回一个作为类型参数传递的类类型的实例。
  • 如果类包含一个无重写的构造器声明,一个具有构造器实现的参数列表的构造签名,具有与类相同类型的参数和返回一个作为类型参数传递的类类型的实例。
  • 如果类包含了一个重写的构造器声明,一组具有重写的参数列表的构造签名,具有与类相同类型的参数和返回一个作为类型参数传递的类类型的实例。
  • 每一个静态成员变量声明的属性。
  • 每一个静态成员函数声明的具有函数类型的属性。
  • 每一个唯一命名静态成员访问器声明的属性。
  • 一个名为“prototype”的属性,此属性的类型是一个具有”Any”类型的类类型的实例。
  • 所有的没被重写的基类构造器函数类型属性。

  每个类自动包含一个名为”prototype“的静态属性成员,其类型为具有“Any”类型的容器类,此容器里的每个类型参数都被替代为了“Any”类型。

class Pair<T1, T2> {
    constructor(public item1: T1, public item2: T2) { }
}
class TwoArrays<T> extends Pair<T[], T[]> { }

  引入了两个命名类型

interface Pair<T1, T2> {
    item1: T1;
    item2: T2;
}
interface TwoArrays<T> {
    item1: T[];
    item2: T[];
}

  和两个构造器函数

var Pair: {
    new <T1, T2>(item1: T1, item2: T2): Pair<T1, T2>;
}
var TwoArrays: {
    new <T>(item1: T[], item2: T[]): TwoArrays<T>;
} 

  需要注意的,构造器函数类型中的每一个构造签名与此类具有同样的类型参数并返回一个此类的实例,此类的类型形参被作为类型实参传递。同时,当一个派生类没有声明一个构造器时,在构造签名被从基构造器函数类型传递到派生构造器类型之前,来自于基类引用的类型实参就被替换了。

8.3 构造器声明(Constructor Declarations)

  构造器声明声明一个类的构造器函数。

  ConstructorDeclaration:
    AccessibilityModifieropt   constructor   (   ParameterListopt   )   {   FunctionBody   }
    AccessibilityModifieropt   constructor   (   ParameterListopt   )   ;

  指定了函数体的构造器声明被称为构造器实现(constructor implementations),否则被称为构造器重载(constructor overloads)。一个类可以指定多个构造器重载,当最多只能指定一个构造器实现。所有的构造器声明必须指定相同的修饰符集。构造器只支持公共的(public)。

  当一个类没有构造器声明时,将自动提供一个构造器。

  当一个类含有构造器重载时,这些重载决定了赋给构造器函数对象的类型的构造签名,构造器实现签名必须能够赋给此类型。否则,构造器实现自身决定构造签名。

  当一个类同时含有构造器重载和构造器实现时,重载必须位于实现之前,并且所有的声明必须联系而不中断。

  构造器的函数体中可以保护返回语句。如果返回语句指定了表达式,那么这些表达式的类型必须能够赋给类的”this“类型。

  泛型类的类型参数可被构造器声明访问。

8.3.1 构造器参数(Constructor Parameters)

  如同函数一样,构造器实现可以为可选参数指定默认值表达式。默认值表达式不能引用”this”,否则会包编译时错误。

  构造器实现的参数可以是公共、包含或私有的。我们可以通过在构造器参数中声明属性或赋值的方式来简化属性的声明和赋值。例如:

class Point {
    constructor(public x: number, public y: number) {
        // Constructor body
    }
}

  等同于:

class Point {
    public x: number;
    public y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
        // Constructor body
    }
}  

  参数属性声明可以声明一个可选参数(包含“?”符号或一个默认值)。

8.3.2 super调用

  我们用“super”关键字来调用基类的构造器。如下所示:

class ColoredPoint extends Point {
    constructor(x: number, y: number, public color: string) {
        super(x, y);
    }
}

  无“extends”子句的构造器可能不包含super调用,然而派生类的构造器函数体中必须至少包含一个super调用。super调用不能在构造器外或构造器中的本地函数中使用。

  以下两项条件都满足的情况下,构造器中的第一个表达式必须为“super”调用。

  • 容器类是一个派生类。
  • 构造器声明了参数属性或容器类声明了初始化的实例成员变量。

  在此情况的super调用中,当参数表达式引用“this”时,会报编译时错误。

8.3.3 自动构造器(Automatic Constructors)

  当一个类忽略了构造器声明时,系统将提供一个自动构造器。

  在无“extend”子句的类当中,自动构造器没有参数,只执行实例成员变量初始化。

  派生类中的自动构造器具有与基类相同的参数列表。

BaseClass.apply(this, arguments);

  然后初始化实例成员变量。

8.4 属性成员声明(Property Member Declarations)

  属性成员声明可以是成员变量(member variabel)声明,成员方法(member function)声明或成员访问器(member accessor)声明。

  PropertyMemberDeclaration:
    MemberVariableDeclaration
    MemberFunctionDeclaration
    MemberAccessorDeclaration

  无”static“修饰符的成员声明被称为实例成员(instance member)声明。实例属性成员声明声明类类型中的属性,它必须指定一个唯一名。

  有”static“修饰符的成员声明被称为静态成员(static member)声明。它声明构造方法类型中的属性,它必须指定一个唯一名。

  需要注意的是,实例和静态成员声明的声明空间是分开的,因此,实例和静态成员可以同名。

  除非重写,我们不能为一个派生类声明与基类成员相同名和类型的属性成员。

  每一个类都自动包含一个名为“prototype”的静态属性成员。不能显式地声明一个名为“prototype”的静态属性成员。

  下面是一个同时包含了实例成员声明和静态成员声明的例子:

class Point {
    constructor(public x: number, public y: number) { }
    public distance(p: Point) {
        var dx = this.x - p.x;
        var dy = this.y - p.y;
        return Math.sqrt(dx * dx + dy * dy);
    }
    static origin = new Point(0, 0);
    static distance(p1: Point, p2: Point) { return p1.distance(p2); }
}

  类类型“Point"有以下成员:

interface Point {
    x: number;
    y: number;
    distance(p: Point);
}

  构造器函数”Point“由一个与声明相应的类型:

var Point: {
    new(x: number, y: number): Point;
    origin: Point;
    distance(p1: Point, p2: Point): number;
}

8.4.1 成员变量声明(Member Valiable Declarations)

  成员变量声明声明一个实例成员变量或一个静态成员变量。

  一个成员变量声明的相关类型的决定方式与普通的变量声明类似(参见5.2节)。

  一个实例成员变量声明在类类型中引入一个成员并且可选地初始化类实例的属性。实例成员变量声明中的初始化只为每个新的类实例执行一次,相当于构造器中的this赋值。在实例成员变量的初始化表达式中,类的“this”类型就是this

  一个静态成员变量声明在构造器函数类型中引入一个属性并且可选地初始化构造器函数对象的属性。静态成员变量声明中的初始化只在包含代码或模块被导入时执行一次。

  实例成员变量的初始化表达式的作用域为类构造器体,但是不能引用参数或构造器的本地变量。这意味着实例成员变量的初始化表达式不能访问来自于外部作用域的同名构造器参数或本地变量的实体。

  实例成员变量初始化相当于构造器中的this赋值,例如:

class Employee {
    public name: string;
    public address: string;
    public retired = false;
    public manager: Employee = null;
    public reports: Employee[] = [];
}

  等同于:

class Employee {
    public name: string;
    public address: string;
    public retired: boolean;
    public manager: Employee;
    public reports: Employee[];
    constructor() {
        this.retired = false;
        this.manager = null;
        this.reports = [];
    }
}

8.4.2 成员函数声明(Member Function Declarations)

  成员函数声明声明一个实例成员函数或一个静态成员函数。

  MemberFunctionDeclaration:
    AccessibilityModifieropt   staticopt   PropertyName   CallSignature   {   FunctionBody   }
    AccessibilityModifieropt   staticopt   PropertyName   CallSignature   ;

  一个成员函数声明的处理方式与普通函数声明类似(参见第6节),除非成员函数中的this为已知类型。

  同一成员函数的所有声明必须指定同样的可访问性或种类(实例或静态)。

  一个实例成员函数声明在类类型中声明一个属性,并且将一个函数对象赋给类的原型对象上的属性。在实例成员函数声明体中,this就是类的“this”类型。

  一个静态成员函数声明在构造器函数类型中声明一个属性,并且将一个函数对象赋给构造器函数对象上的属性。在静态成员函数声明体中,this的类型是构造器函数类型。

  成员函数可以通过super属性访问来访问基类成员并重写基类成员,例如:

class Point {
    constructor(public x: number, public y: number) { }
    public toString() {
        return "x=" + this.x + " y=" + this.y;
    }
}
class ColoredPoint extends Point {
    constructor(x: number, y: number, public color: string) {
        super(x, y);
    }
    public toString() {
        return super.toString() + " color=" + this.color;
    }
}

  在一个静态成员函数中,this代表静态成员函数被引用的构造器函数对象,因此一个“new this()”调用实际上引用了一个派生的类构造器。

class A {
    a = 1;
    static create() {
        return new this();
    }
}
class B extends A {
    b = 2;
}
var x = A.create();  // new A()
var y = B.create();  // new B()

  需要注意的是,ts并不需要也不验证派生构造器函数是否是基类构造器函数的子类型。换言之,把“B”声明改为如下声明:

class B extends A {
    constructor(public b: number) {
        super();
    }
}

  “B"声明是正确的,尽管来自”create“函数的构造器调用并没指定参数(为”b“赋予了值”undefined“)。

8.4.3 成员访问器声明(Member Accessor Declarations)

  成员访问器声明声明一个实例成员访问器或一个静态成员访问器。

  MemberAccessorDeclaration:
    AccessibilityModifieropt   staticopt   GetAccessor
    AccessibilityModifieropt   staticopt   SetAccessor

  “get"和”set"访问器的处理方式与对象的成员处理方式类似(参见第4.5节),除了成员访问器声明中不能用上下文类型外。

  相同成员名的访问器必须指定相同的可访问性。

  一个实例成员访问器声明在类类型中声明一个属性,并且定义一个类的原型对象的属性。在实例成员访问器声明中,this就是类的”this“类型。

  一个静态成员访问器声明在构造器函数类型中声明一个属性,并且定义一个构造器函数对象的原型对象的属性。在静态成员访问器声明中,this的类型是构造器函数类型。

  ”get“和”set“访问器在js代码中被生成了调用”Object.defineProperty“的形式,参见8.7.1节。

8.4.4 动态属性声明(Dynamic Property Declarations)

  如果属性成员声明的属性名是一个计算属性名(computed property name),那么此声明被称为动态属性声明。其应用规则如下:

  • 它不向类类型或构造器函数类型中引入属性。
  • 动态属性赋值的属性名必须是“Any”、“String"、”Number"或Symbol"原始类型。
  • 如果属性名表达式是“Any”或“Number”原始类型,那么动态属性声明相关的名称被认为是一个数字属性名。

8.5 索引成员声明(Index Member Declarations) 

  索引成员声明在类类型中引入一个索引签名。

  IndexMemberDeclaration:
    IndexSignature   ;

  索引成员声明无具体内容,不能指定可访问性修饰符。

  一个类声明可具至少具有一个字符串索引成员声明和一个数字索引成员声明。所有的实例属性成员必须满足索引成员隐含的约束。

  不能为类的静态部分声明索引成员。

  在类中包含字符串索引签名意义不大,因为它约束了所有的实例属性。然而,数字索引签名则作用明显,当一个类被作为类数组方式应用时,它控制着元素的类型。

8.6 装饰器(Decorators)

  一个可用的装饰器应该是:

  •   可赋予任何一种装饰器类型:类装饰器(ClassDecorator)、属性装饰器(PropertyDecorator )、函数/方法装饰器(MethodDecorator )或参数装饰器(ParameterDecorator)。
  •   有一个可被赋予给装饰值的返回值(在类装饰器和函数装饰器情况下)。
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
declare type ParameterDecorator = (target: Function, propertyKey: string | symbol, parameterIndex: number) => void;

  类装饰器

@F("color")
@G
class Foo {
}

  被编译为:

var Foo = (function () {
    function Foo() {
    }
    Foo = __decorate([F("color"), G], Foo);
    return Foo;
})();

   函数/方法装饰器

class Foo {
  @F(color)
  @G
  bar() { }
}

  被编译为:

var Foo = (function () {
    function Foo() {
    }
    Foo.prototype.bar = function () {
    };
    Object.defineProperty(Foo.prototype, "bar", __decorate([F(color), G], Foo.prototype, "bar", Object.getOwnPropertyDescriptor(Foo.prototype, "bar")));
    return Foo;
})();

  静态函数/方法装饰器

class Foo {
    @F("color")
    @G
    static sMethod() {}
}

  被编译为:

var Foo = (function () {
    function Foo() {
    }
    Foo.sMethod = function () {
    };
    Object.defineProperty(Foo, "sMethod", __decorate([F("color"), G], Foo, "sMethod", Object.getOwnPropertyDescriptor(Foo, "sMethod")));
    return Foo;
})();

  属性装饰器

class Foo {
    @F("color")
    @G
    prop: number;
}

  被编译为:

var Foo = (function () {
    function Foo() {
    }
    __decorate([F("color"), G], Foo.prototype, "prop");
    return Foo;
})();

  函数或访问器参数装饰器

class Foo {
    method(@G a, @F("color") b) {}
}

  被编译为:

var Foo = (function () {
    function Foo() {
    }
    Foo.prototype.method = function (a, b) {
    };
    __decorate([G], Foo.prototype, "method", 0);
    __decorate([F("color")], Foo.prototype, "method", 1);
    return Foo;
})();

  上面代码中“__decorate“函数定义如下:

var __decorate = this.__decorate || function (decorators, target, key, value) {
    var kind = typeof (arguments.length == 2 ? value = target : value);
    for (var i = decorators.length - 1; i >= 0; --i) {
        var decorator = decorators[i];
        switch (kind) {
            case "function": value = decorator(value) || value; break;
            case "number": decorator(target, key, value); break;
            case "undefined": decorator(target, key); break;
            case "object": value = decorator(target, key, value) || value; break;
        }
    }
    return value;
};

8.7 代码生成

  当输出目标是ECMAScript 2015或更高时,类型参数、实现子句、可访问性修饰符和成员变量声明在生成的js中被移除掉了。当输出目标是ECMAScript 3或5时,则是更全面的重写。

8.7.1 无扩展子句的类

  无“extends"子句的类生成如下格式的js代码:

var <ClassName> = (function () {
    function <ClassName>(<ConstructorParameters>) {
        <DefaultValueAssignments>
        <ParameterPropertyAssignments>
        <MemberVariableAssignments>
        <ConstructorStatements>
    }
    <MemberFunctionStatements>
    <StaticVariableAssignments>
    return <ClassName>;
})();

  ”ClassName“是类名。“ConstructorParameters“是一个以逗号分开的构造器参数名列表。”DefaultValueAssignments“是一序列与常规函数声明相对应的默认特性值赋值。”ParameterPropertyAssignments“是一序列与构造器中的参数声明相对应的赋值,其格式如下:

this.<ParameterName> = <ParameterName>;//ParameterName:参数特性名

  ”MemberVariableAssignments“是一序列与实例成员变量声明相对应的赋值,其格式如下:

this.<MemberName> = <InitializerExpression>;//MemberName:成员名,InitializerExpression:初始化表达式生成的代码

  ”ConstructorStatements“是构造器中语句的代码生成。

  ”MemberFunctionStatements "是一序列与成员函数声明或成员访问器声明相对应的语句。

  一个实例成员函数声明生成如下格式的语句:

<ClassName>.prototype.<MemberName> = function (<FunctionParameters>) {
    <DefaultValueAssignments>
    <FunctionStatements>
}

  而静态成员函数声明则生成如下格式的语句:

<ClassName>.<MemberName> = function (<FunctionParameters>) {
    <DefaultValueAssignments>
    <FunctionStatements>
}

  一个"get“和”set"实例成员访问器声明生成如下格式的语句:

Object.defineProperty(<ClassName>.prototype, "<MemberName>", {
    get: function () {
        <GetAccessorStatements>
    },
    set: function (<ParameterName>) {
        <SetAccessorStatements>
    },
    enumerable: true,
    configurable: true
};

  而"get“和”set"静态成员访问器声明则生成如下格式的语句:

Object.defineProperty(<ClassName>, "<MemberName>", {
    get: function () {
        <GetAccessorStatements>
    },
    set: function (<ParameterName>) {
        <SetAccessorStatements>
    },
    enumerable: true,
    configurable: true
};  

  “StaticVariableAssignments”是一序列与成员变量声明相对应的语句。其格式如下:

<ClassName>.<MemberName> = <InitializerExpression>;//MemberName:静态变量名,InitializerExpression:初始化表达式生成的代码

8.7.2 有扩展子句的类

  有“extends"子句的类生成如下格式的js代码:

var <ClassName> = (function (_super) {
    __extends(<ClassName>, _super);
    function <ClassName>(<ConstructorParameters>) {
        <DefaultValueAssignments>
        <SuperCallStatement>
        <ParameterPropertyAssignments>
        <MemberVariableAssignments>
        <ConstructorStatements>
    }
    <MemberFunctionStatements>
    <StaticVariableAssignments>
    return <ClassName>;
})(<BaseClassName>);

  ”__extends“函数在js源文件起始生成,它把基构造器函数对象的所有特性都复制给了派生的构造器函数对象,然后为派生构造器函数对象创建相应的”prototype"特性。

var __extends = this.__extends || function(d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function f() { this.constructor = d; }
    f.prototype = b.prototype;
    d.prototype = new f();
}

  “BaseClassName”是“extends"子句中指定的类名。
  如果类没有显式地指定构造器,”SuperCallStatement“格式如下:

_super.apply(this, arguments);

  否则,如果构造器函数以”super"调用开始,那么其格式如下:

_super.call(this, <SuperCallArguments>)  //SuperCallArguments:"super"调用中指定的参数列表。

  一个构造器中的“super"属性访问、实例成员函数或实例成员访问器生成如下格式的js代码:

_super.prototype.<PropertyName>  //PropertyName:被引用的基类属性名

  当函数调用中存在”super“属性访问,则生成如下格式的js代码:

_super.prototype.<PropertyName>.call(this, <Arguments>)  //Arguments:函数调用中指定的参数列表生成的代码

  一个静态成员函数或静态成员访问器中的”super"属性访问生成如下格式的js代码:

_super.<PropertyName>  //PropertyName:被引用的基类属性名

  当函数调用中存在”super“属性访问,则生成如下格式的js代码:

_super.<PropertyName>.call(this, <Arguments>)  //Arguments:函数调用中指定的参数列表生成的代码
posted @ 2016-01-11 19:16  leeberg  阅读(479)  评论(0)    收藏  举报