Understanding prototypical inheritance
function TodoService(){
this.todos =[];
}
TodoService.prototype.getAll = function(){
return this.todos;
}
var service = new TodoService();
service.getAll();
this 关键字指的是对象的实例。所以第六行的 this.todos 会在运行时引用第二行的 this.todos
JavaScript 为对象分配原型的最常见方式是使用构造函数,它实际上只是一个使用 new 关键字调用的函数。当您使用 new 关键字初始化对象时,JavaScript 会做三件事。首先,它创建一个新对象。其次,它将新对象的原型设置为构造函数的原型。第三,它执行您使用 new 关键字调用的函数,在该方法中将新对象称为 this 。
Defining a class
// function TodoService() {
// this.todos = [];
// }
// TodoService.prototype.getAll = function () {
// return this.todos;
// };
// var service = new TodoService();
// service.getAll();
//=====>
// class TodoService {
// todos: Todo[] = [];
// constructor(todos: Todo[]) {
// this.todos = todos;
// }
// getAll() {}
// }
//=====>
class TodoService {
constructor(private todos: Todo[]) {}
getAll() {
return this.todos;
}
}
interface Todo {
name: string;
state: TodoState;
}
enum TodoState {
New = 1,
Active,
Completed,
Deleted,
}
Applying static properties
定义静态属性
- In other languages such as C#, Java, or even C, C++, you’d refer to these kinds of variables as static members.
- However, global variables are now generally considered bad practice and to be avoided at all costs.
- 所以在ES5里只能这样写:
function TodoService() {}
TodoService.lastId = 0;
TodoService.getNextId = function () {
return (TodoService.lastId += 1);
};
TodoService.prototype.add = function (todo) {
var newId = TodoService.getNextId();
};
- in ES6:
class TodoService {
static lastId: number = 0;//静态属性
constructor(private todos: Todo[]) {}
add(todo: Todo) {
var newId = TodoService.getNextId();
}
getAll() {
return this.todos;
}
//静态方法
static getNextId() {
return (TodoService.lastId += 1);
}
}
interface Todo {
name: string;
state: TodoState;
}
enum TodoState {
New = 1,
Active,
Completed,
Deleted,
}
Making properties smarter with accessors
to define and substantiate证实 simply object
this code is actually perfectly legitimate合法
it must be preceded by the set keyword
let passcode = 123456;
class Employee {
private _fullName: string; //私有变量 an internal variable
get changeName() {
return this._fullName;
}
set changeName(newName) {
if (passcode && passcode == 123456) {
this._fullName = newName;
} else {
throw "error";
}
}
}
Inheriting behavior from a base class
derive from源自于 a class
extends BaseClass
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.Completed); // 在派生类的构造函数中必须调用 super()
}
canChangeState(todo: Todo): boolean {
return (
super.canChangeState(todo) &&
(todo.state == TodoState.Active || todo.state == TodoState.Deleted)
);
}
}
Implementing an abstract class
enforce my intent 执行我的意图
nothing stopping some rogue javascripit code from creating a new instance
没有什么能阻止一些流氓 javascripit 代码创建一个新实例
is designed to guard against, there is ultimately nothing i can do about that
旨在防范,最终我无能为力
All I can do is focus on the protection that it gives me at development time and in that regard, abstract base classes are an excellent tool.
我所能做的就是专注于它在开发时给我的保护,在这方面,抽象基类是一个很好的工具。
abstract class TodoStateChanger {
constructor(private newState: TodoState) {}
// canChangeState(todo: Todo): boolean {
// return !!todo;
// }
// =>改成抽象类
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.Completed); // 在派生类的构造函数中必须调用 super()
}
canChangeState(todo: Todo): boolean {
return (
//super.canChangeState(todo)
//=>
!!todo &&
(todo.state == TodoState.Active || todo.state == TodoState.Deleted)
);
}
}
Controlling visibility with access modifiers
使用访问修饰符控制可见性
abstract class TodoStateChanger {
// 抽象类中把它从private改成protected,在派生类中可以被访问到
constructor(protected newState: TodoState) {}
}
class CompleteTodoStateChanger extends TodoStateChanger {
constructor() {
super(TodoState.Completed);
var a = this.newState // 抽象类中把它从private改成protected,在派生类中可以被访问到
}
}
class SmartTodo {
// name: string;
// constructor(name: string) {
// this.name = name;
// }
// => 不用专门声明一个name,直接在构造函数里加任何修饰符public/protected/private作为声明
constructor(public name: string) {
this.name = name;
}
}
私有变量加下划线
private static _lastId: number = 0;
Implementing interfaces
class TodoService implements IToDoService {
private static _lastId: number = 0;
get nextId() {
return (TodoService._lastId += 1);
}
constructor(private todos: Todo[]) {}
add(todo: Todo) {
todo.id = this.nextId;
this.todos.push(todo);
return todo;
}
delete(todoId: number): void {
var toDelete = this.getById(todoId);
var deletedIndex = this.todos.indexOf(toDelete);
this.todos.splice(deletedIndex, 1);
}
getAll(): Todo[] {
// 不直接return Todo数组出去
// 现在互联网传输的数据一般都是json格式
// 将数据传出去之前,先用JSON.stringify把数据转成json字符串格式,方便传输
// 否则得到的是[object object]
// 接收到数据后,用JSON.parse把数据转成对象格式以便本地使用
var clone = JSON.stringify(this.todos);
return JSON.parse(clone);
}
getById(todoId: number): Todo {
//JS's built-in array method filter JS的内置的数组过滤方法
var filtered = this.todos.filter((i) => i.id == todoId);
if (filtered.length) {
return filtered[0];
}
return null;
}
}
interface IToDoService {
add(todo: Todo): Todo;
delete(todoId: number): void;
getAll(): Todo[];
getById(todoId: number): Todo;
}
interface Todo {
id: number;
name: string;
state: TodoState;
}
enum TodoState {
New = 1,
Active,
Completed,
Deleted,
}
no, quite the contrary 不,恰恰相反
icing on the cake 锦上添花