TypeScript 学习笔记6: Classes

原文链接:https://leanpub.com/essentialtypescript/read#leanpub-auto-classes
TypeScript 提供了三种方式创建 custom type:interface,enum,class。
class 是ECMAScript 2015 提供的 syntactic sugar,它不是TypeScript引入的新概念。

1. Prototypical Inheritance

JavaScript 本身已经提供了面向对象的语法:对象、构造函数、prototype、属性、方法、继承、重载等等。这一小节讲了ES5实现面向对象的方法。(略过)

2. Defining a class

class TodoService {
    todos: Todo[] = [];

    constructor(todos: Todo[]) {
        this.todos = todos;
    }   

    getAll() {
        return this.todos;
    }
}

说明:
1. constructor() 是构造函数;
2. todo: Todo[] = []; 定义了一个属性,并赋予初值;
3. getAll() 定义了一个成员函数,注意,函数名前面不用写 function 前缀。

对于上面代码中的第2行和第5行,TypeScript 提供了一种便捷的写法:

class TodoService {
    constructor(private todos: Todo[]) {        
    }
    getAll() {
        return this.todos;
    }
}

在constructor 的参数前面加一个访问限定符(private),会自动做两件事:
1. 创建 todos 属性;
2. 创建新的对象时,把构造函数的参数赋值给 todos 属性。

看看TypeScript生成的ES5代码:

var TodoService = (function () {
    function TodoService(todos) {
        this.todos = todos;
    }
    TodoService.prototype.getAll = function () {
        return this.todos;
    };
    return TodoService;
}());

3. 静态成员/方法

class TodoService {
    static lastId: number = 0;

    constructor(private todos: Todo[]) {        
    }

    static getNextId() {
        return TodoService.lastId += 1;
    }

    add(todo: Todo) {
        var newId = TodoService.getNextId();
        // ...
    }

    getAll() {
        return this.todos;
    }
}

说明:
1. static lastId: number = 0; 定义了一个静态成员 lastId,所有的TodoService对象共享这个成员;
2. static getNextId() 定义了一个静态成员函数;
3. 在 add() 方法中使用静态成员函数。

4. accessors

4.1 在 object 中使用get/set

var todo = { 
    name: "Pick up drycleaning",
    get state() {
        return this._state;
    },
    set state(newState) {
        if(newState == TodoState.Complete) {
            var canBeCompleted = this._state == TodoState.Active || this._state == TodoState.Deleted;
            if(!canBeCompleted) {
                throw 'Todo must be Active or Deleted in order to be marked as Completed.';
            }
        }
        this._state = newState;
    }
};

todo.state = TodoState.Active;
console.log(todo.state);

说明:
1. 这里定义了 state 属性;
2. get 和 set 不必成对出现,可以只定义get,也可以只定义set;
3. state保存在 _state 属性中,通常以下划线开头的属性名是私有的内部使用的;
4. 添加了accessor之后,state属性的访问方法没有变化,代码的最后两行给出了使用方法。

4.2 在class 中使用accessor

在对象中使用accessor没什么用处,写到class中才是正途。语法一样。

class SmartTodo { 
    name: string;
    _state: TodoState = TodoState.New;

    constructor(name: string) {
        this.name = name;
    }

    get state() {
        return this._state;
    }

    set state(newState) {
        if(newState == TodoState.Complete) {
            var canBeCompleted = this._state == TodoState.Active || this._state == TodoState.Deleted;
            if(!canBeCompleted) {
                throw 'Todo must be Active or Deleted in order to be marked as Completed.';
            }
        }
        this._state = newState;
    }
};

var todo = new SmartTodo("Pick up drycleaning");
todo.state = TodoState.New;
todo.state = TodoState.Complete; // BOOM! 抛出异常

5. 继承

class TodoStateChanger {
    constructor(private newState: TodoState) {
    }

    canChangeState(todo: Todo): boolean {
        return !!todo;
    }

    changeState(todo: Todo): Todo {
        if(this.canChangeState(todo)) {
            todo.state = this.newState;
        }
        return todo;
    }
}

class CompleteTodoStateChanger extends TodoStateChanger {
    constructor() {
        super(TodoState.Complete);
    }

    canChangeState(todo: Todo): boolean {
        return super.canChangeState(todo) && (todo.state == TodoState.Active || todo.state == TodoState.Deleted);
    }
}

这个例子好像是”state模式”哈?
说明:
1. 继承基类使用 extends 关键字;
2. 调用基类的方法,使用 super 关键字;

6. abstract class

按照 state模式,我们应该给每种state定义一个类,直接创建基类(TodoStateChanger)的对象是没有意义的。用abstract关键字,可以阻止创建一个类的实例:

abstract class TodoStateChanger {
    constructor(private newState: TodoState) {
    }

    abstract canChangeState(todo: Todo): boolean;

    changeState(todo: Todo): Todo {
        if(this.canChangeState(todo)) {
            todo.state = this.newState;
        }
        return todo;
    }
}

class CompleteTodoStateChanger extends TodoStateChanger {
    constructor() {
        super(TodoState.Complete);
    }

    canChangeState(todo: Todo): boolean {
        return !!todo && (todo.state == TodoState.Active || todo.state == TodoState.Deleted);
    }
}

说明:
1. class 前面的abstract,声明这个类是abstract的,不能实例化;
2. canChangeState() 前面的abstract,说明基类中声明了这个接口,但没有实现,每一个子类必须实现这个方法。

7. 访问限定符

放在属性/方法前面,控制它们的可访问性:
1. private, 只有自己的成员函数可以访问;
2. protected, 自己的派生类可以访问;
3. public,公开的,任何地方都可以访问。
4. 第二小节中的 constructor(private todos: Todo[]),用到了private 限定符,也可以用protected或public。
5. static 成员也可以加访问限定符。

private static lastId = 0;

private get state() {
}

private set state(newState) {
}

private getAll() {
}

8. 实现 interfaces

interface ITodoService {
    add(todo: Todo): Todo;
    delete(todoId: number): void;
    getAll(): Todo[];
    getById(todoId: number): Todo;
}

interface IIdGenerator {
    nextId: number;
}

class TodoService implements ITodoService, IIdGenerator {
    static _lastId: number = 0;

    constructor(private todos: Todo[]) {        
    }

    get nextId(): number {
        return TodoService._lastId += 1;
    }

    add(todo: Todo) {
        todo.id = this.nextId;
        this.todos.push(todo);
        return todo;
    }

    getAll(): Todo[] {
        return this.todos;
    }

    delete(todoId: number) {
        var toDelete = this.getById(todoId);
        var toDeleteIndex = this.todos.indexOf(toDelete);
        this.todos.splice(toDeleteIndex, 1);
    }

    getById(todoId: number): Todo {
        var filtered = this.todos.filter(x => x.id == todoId);
        if(filtered.length) {
            return filtered[0];
        }
        return null;
    }
}

说明:
1. 用 implements 连接类名和接口名;
2. 一个类可以实现多个接口,中间用逗号分隔。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值