当前位置: 首页 > news >正文

汕尾网站建设公司北京比较大的网站建设公司

汕尾网站建设公司,北京比较大的网站建设公司,快速学习网站建设,做网站购买虚拟主机送模板吗第五章 类和接口 类是组织和规划代码的方式#xff0c;是封装的基本单位。 typescript类大量借用了C#的相关理论#xff0c;支持可见性修饰符#xff0c;属性初始化语句#xff0c;多态#xff0c;装饰器和接口。 不过#xff0c;由于Typescript将类编译成常规的JavaScri…第五章 类和接口 类是组织和规划代码的方式是封装的基本单位。 typescript类大量借用了C#的相关理论支持可见性修饰符属性初始化语句多态装饰器和接口。 不过由于Typescript将类编译成常规的JavaScript类所以我们也能使用一些JavaScript管用法例如兼顾类型安全的混入mixin。 Typescript的某些类特性例如属性初始化语句和装饰器JavaScript类也支持因此能生成运行时代码。其他特性例如可见性修饰符接口和泛型是Typescript专属的特性只存在于编译时把应用编译成JavaScript后不生成任何代码。 本章学习Typescript类的用法。掌握类的使用方法和缘由。 5.1 类的继承 我们将制作一个国际象棋引擎。提供一个API供两个玩家交替走棋。 首先草拟类型 // 表示以此国际象棋游戏class Game { }​// 表示一个国际象棋棋子class Piece { }​// 一个棋子的一组坐标class Position { }​// 将棋子分类class King extends Piece {}class Queen extends Piece {}// 主教class Bishop extends Piece {}// 骑士class Knight extends Piece {}// 城堡class Rook extends Piece {}// 兵class Pawn extends Piece {​}每个棋子都有颜色和当前位置。 下面为Piece类添加颜色和位置 type Color Black | White;// 竖线type File A | B | C | D | E | F | G | H;// 横线type Rank 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;// 表示以此国际象棋游戏​class Game { }​// 一个棋子的一组坐标class Position {constructor(private file: File,private rank: Rank) { }}​// 表示一个国际象棋棋子class Piece {protected position: Positionconstructor(private readonly color: Color,file: File,rank: Rank) {this.position new Position(file, rank)}}由于颜色横线和竖线相对较少因此可以手动把可能得值使用类型字面量列举出来。这样做限定了类型的范围进一步提高了安全性。构造方法中的private访问修饰符自动把参数赋值给thisthis.file等并把可见性设为私有这意味着Piece实例中代码可以读取和写入但是Piece实例之外的代码不可以。不同的Piece实例访问各自的私有成员其他类的实例即便是Piece的子类也不可以访问私有成员把实例变量position的可见性声明为proteed。与private类似protected也罢属性赋值给this但是这样的属性对Piece的实例和Piece子类的实例都可见。声明position时没有为其赋值因此在Piece构造方法中要赋值。如果未在构造方法中赋值Typescript将提醒我们。也就是说我们声明的类型是T但事实上是T|undefined这是因为我们没有在属性初始化语句或构造方法中为其赋值。如此一来我们要更新属性的签名指明其值不一定是一个Position实例还有可能是undefined。new Piece接受三个参color,file和rank。我们为color指定了两个修饰符一个是private把他赋值给this并确保只能由Piece的实例访问一个是readonly指明在初始赋值后这个属性只能读取不能再赋其他值。 Typescript类中的属性和方法支持三个访问修饰符 public 任何地方都能访问protected 当前类和子类的实例能访问private 当前类的实例访问 访问修饰符的作用是不让类暴露过多实现细节而是只开放规范的API供外部使用。 我们定义了一个Piece类但是并不希望用户直接实例化Piece而是在此基础上扩展定义QueenBishop等类实例化这些子类。为此我们可以做出限制具体方式是使用abstract abstract class Piece {protected position: Positionconstructor(private readonly color: Color,file: File,rank: Rank) {this.position new Position(file, rank)}}现在实例化Piece将报错 abstract关键字表示我们不能直接初始化该类但是并不阻止我们在类中定义方法 abstract class Piece {protected position: Positionconstructor(private readonly color: Color,file: File,rank: Rank) {this.position new Position(file, rank)}moveTo(position:Position){this.position position}abstract canMoveTo(position:Position):boolean}​​// 将棋子分类class King extends Piece {canMoveTo(position: Position): boolean {return true}}现在Piece类包含以下信息 告诉子类子类必须实现一个名为canMoveTo的方法而且要兼容指定的签名。如果忘记实现将报错。注意实现抽象类的时候也要实现抽象方法Piece类为moveTo方法提供了默认实现如果子类愿意也可以覆盖默认实现。我们没有为moveTo方式访问修饰符因此默认为public所以其他代码可读也可写。 下面更新King类的定义 class Position {constructor(private file: File,private rank: Rank) { }distanceFrom(position:Position){return {rank:Math.abs(position.rank - this.rank),file:Math.abs(position.file.charCodeAt(0)-this.file.charCodeAt(0))}}}// 将棋子分类class King extends Piece {canMoveTo(position: Position): boolean {let distance this.position.distanceFrom(position)return distance.rank2 distance.file 2}}开始新游戏时我们想自动创建一个棋盘和一些棋子 ​class Game {private pieces Game.makePieces()private static makePieces() {return [new King(White, E, 1),new King(White, E, 8),new Queen(White, D, 1),new Queen(Black, D, 8)]}}游戏其他功能自行实现 总结一下 类使用class关键字声明。扩展类时使用extend关键字。类可以是具体的也可以是抽象的(abstract)。抽象类可以有抽象方法和抽象属性。方法的可见性可以是privateprotected和public默认。抽象类可以有抽象方法和抽象属性。类可以有实例属性可见性也可以是privateprotected或public(默认)。实例属性可以在构造方法的参数中声明也可通过属性初始化语句声明。声明实例属性时可以使用readonly把属性标记为只读 5.2 super 与JavaScript一样Typescript也支持super调用。如果子类覆盖父类中定义的方法在子类中可以通过super调用父类中的同名方法。super有两种调用方式 方法调用super.take.构造方法调用。使用特殊形式super()而且只能在构造方法中调用。如果子类有构造方法在子类的构造方法中必须调用super(),把父子关系联系起来如果你忘记了Typescript会提醒你 // 将棋子分类class King extends Piece {constructor(color: Color,file: File,rank: Rank){super(color,file,rank)}canMoveTo(position: Position): boolean {let distance this.position.distanceFrom(position)return distance.rank 2 distance.file 2}}5.3 以this为返回类型 this可以用作值此外还能用作类型。对类来说this类型还可以用于注解方法的返回类型。 例如:实现ES6中set数据结构的简化版 class Set{has(value:string):boolean{return true}add(value:string):Set{return this}}定义Set的子类型 class Set{has(value:string):boolean{return true}add(value:string):this{return this}}class MutableSet extends Set {delete(value:number):boolean{return true}// 不用覆盖了// add(value:number)}扩展其他类时要把返回this的每个方法的签名覆盖掉就显得比较麻烦。如果只是为了让类型检查器满意这样做就失去了继承基类的意义。 如此一来哦我们可以把MutableSet中覆盖的add方法省略因为在Set中this指向一个Set实例而在MutableSet中this指向一个MutableSet实例。 5.4 接口 类经常当做接口使用 与类型别名相似接口是一种命名类型的方式这样就不用再行内定义了。类型别名和接口算是同一种概念的两种句法就像函数表达式和函数声明之间的关系 !-- 寿司 --type Sushi {calories:numbersalty:booleantasty:boolean}// 重写为interface Sushi{calories:numbersalty:booleantasty:boolean}在使用Sushi类型别名的地方都能使用Sushi接口。两个声明都定义结构而且二者可以相互赋值其实二者完全一样 把类型组合在一起时更为有趣。 type Food {calories:numbertasty:boolean}type Sushi Food{salty:boolean}interface Cake extends Food{sweet:boolean}接口不一定扩展其他接口其实接口可以扩展任何结构对象类型类或其他接口。 类型和接口之间有什么区别呢有三个细微的差别。 类型别名更加通用右边可以是任何类型包括类型表达式类型外加或|等类型运算符而在接口中右边必须为结构。例如下面类型别名不能使用接口重写 type A number;type B A | string扩展接口时候Typescript将检查扩展的接口是否可赋值给被扩展的接口 interface A {good(x:number):stringbad(x:number):string}interface B extends A {good(x:string|number):stringbad(x:string):string// 报错}而使用别名没事 type A {good(x:number):stringbad(x:number):string}type B A {good(x:string|number):stringbad(x:string):string// 报错}let c:B {good(x:number){return s},bad(x:string|number){return }}建模对象类型的继承时Typescript对接口所做的可赋值检查时捕获错误的有力工具 同一作用域中的多个同名接口将自动合并同一作用域中的多个同名类型别名将导致编译错误。这个特性称为声明合并 5.4.1 声明合并 声明合并指的是Typescript自动把多个同名声明组合在一起。介绍枚举时讲过这个特性3.2.12节讨论命名空间声明10.3节还会说到 倘若生命了两个名为User的接口Typescript将自动把二者组合成一个接口 interface User {name:string}​interface User {age:number}​let a:User {name:111,age:12}而使用类型别名重写的话将报错 注意两个接口不能冲突如果在一个接口中某个属性的类型为T而在另一个接口中该属性的类型为U由于T和U不是同一种类型Typescript将报错 interface User{age:string}interface User{age:number// 报错}​如果接口中声明了泛型5.7节那么两个接口中要完全相同的方式声明泛型名称一样还不行这样才能合并接口。 interface UserAge extends number{age:Age}interface UserAge extends string{age:Age}// 报错Typescript很少会这么做但是在这里Typescript不仅检查了两个类型满不满足可赋值性还会确认二者是否完全一致。 5.4.2 实现 声明类时可以使用implement关键字指明该类满足某个接口。与其他显示类型注解一样这是为类添加类型层面约束的一种便利方式。这么做能尽量保证类在实现上的准确性防止错误出现在下游不知具体原因。这也是实现常用的设计模式例如适配器工厂和策略的一种常见方式。后后面分享 interface Animal{eat(food:string):voidsleep(hour:string):void}class Cat implements Animal {eat(food: string): void {console.info(Ate some,food,.MM);}sleep(hour: string): void {console.info(Slept for,hour,hours);}}new Cat().sleep(10)new Cat().eat(fish)​cat必须实现Animal声明的每个方法。如果需要在此基础上还可以实现其他方法和属性。 接口可以声明实例属性但是不能带有可见性修饰符privateprotectedpublic也不能使用static关键字。另外像对象类型一样第三章可以使用readonly把实例属性标记为只读 interface Animal {readonly name: stringeat(food: string): voidsleep(hour: string): void}一个类不限于只能实现一个接口而是想实现多少个都可以 interface A {}interface B {}class C implements A,B{​}5.4.3 实现接口还是扩展抽象类 实现接口其实于扩展抽象类差不多区别是接口更通用更轻量而抽象类的作用更具体功能更丰富。 接口是对结构建模的方式。在值层面可以表示对象数组函数类或类的实例。接口不生成JavaScript代码只存在于编译时。 抽象类只能对类建模而且生成运行时代码即JavaScript类.抽象类可以有构造方法可以提供默认实现还能为属性和方法设置访问修饰符。这些在接口中都做不到。 具体使用哪个取决于实现用途。如果多个类共用同一个实现使用抽象类。如果需要一种轻量的方式表示“这个类是T型”使用接口。 5.5 类是结构化类型 与Typescript中的其他类型一样Typescript根据结构比较类与类的名称无关。类与其他类型是否兼容要看结构如果常规的对象定义了同样的属性或方法也与类兼容。从C#Scala和其他多数名义类型编程语言转过来的程序员来说一定要记住这点。 这意味着如果一个函数接受Zebra实例而我们传入一个Poodle实例Typescript并不介意 class Zebra{// 小跑trot(){​}}// 贵宾犬class Poodle {trot(){​}}// 漫步function ambleAround(animal:Zebra){animal.trot()}let zebra new Zebralet poodle new Poodle​ambleAround(zebra)ambleAround(poodle)Typescript是彻底的结构化类型语言因此这段代码完全有效。 然后如果类中使用private或proteceted修饰的字段情况就不一样了。检查一个结构是否可赋值给一个类时如果类中油private或protected字段而且结构不是类或其子类的实例那么结构就不可赋值给类 class A {private x 1}class B extends A {}function f(a:A){}​f(new A)f(new B)f({x:1})// 错误​5.6 类既声明值也声明类型 在Typescript中多数时候表达的要么是值要么时类型 let a 1999function b(){​}// 类型type a numberinterface b {():void}let c:bfunction(){}​在Typescript中类型和值位于不同的命名空间中。根据场合Typescript知道你要使用的是类型还是值上面的a或b if(a13){}// 推导为值let x:a 3// 推导为类型a这种根据上下文进行解析的特性十分有用可以做一些很酷的事情例如实现伴生类型companion type 6.3.4节 类和枚举比较特殊他们既在类型命名空间中生成类型也在值命名空间中生成值。 class C {}let c:C // 类型 new C// 值​enum E {F,G}let e:E// 类型 E.F// 值使用类时我们需要一种方式表达“这个变量应是这个类的实例”枚举同样如此“这个变量应是这个枚举的一个成员”。由于类和枚举在类型层面生成类型所以我们可以轻易表达这种”是什么“关系。 此外我们还需要一种在运行时表示类的方式这样才能使用new实例化类在类上调用静态方法做元编程使用instanceof操作因此类还需要生成值。 在上述示例中C指C类的一个实例。那要怎么表示C类自身的类型呢使用typeof关键字Typescript提供的类型运算符作用类似于JavaScript中值层面的typeof不过操作的是类型。 下面声明一个StringDatabase类实现一个简单的数据库 type State {// 索引签名[key: string]: string}​class StringDatabase {state:State {}get(key:string):string|null{return key in this.state ? this.state[key] : null}set(key:string,value:string):void{this.state[key] value}static from(state:State):StringDatabase{let db new StringDatabasefor(let key in state){db.set(key,state[key])}return db}}let db StringDatabase.from({name:red,age:16})console.log(db.get(name));这个类声明生成的类型是什么呢是实例类型StringDatabase interface StringDatabase{state:Stateget(key:string):string|nullset(key:string,value:string):void}以及 构造方法类typeof StringDatabase interface StringDatabaseConstructor{new():StringDatabasefrom(state:State):StringDatabase}即,StringDatabaseConstructor只有一个方法.from,使用new运算符操作这个构造方法得到一个StringDatabase实例。这两个接口组合在一起对类的构造方法和实例进行建模。 new()那一行称为构造方法签名Typescript通过这种方式表示指定的类型可以使用new运算符实例化。鉴于Typescript采用的是结构化类型这是描述类的最佳方式即可以通过new运算符实例化的是类。 类声明不仅在值层面和类型层面生成相关内容而且在类型层面生成两部分内容一部分表示类的实例另一部分表示类的构造方法通过类型运算符typeof获取 5.7 多态 与函数和类型一样类和接口对泛型参数也有深层次支持包括默认类型和限制。泛型的作用域可以放在整个类或接口中也可放在特定的方法中 class MyMapK,V{1.constructor(initialKey:K,initialValue:V){2。}get(key:K):V{3.}set(key:K,value:V):void{​}mergeK1,V1(map:MyMapK1,V1):MyMapK|K1,V|V1{4.//}static ofK,V(k:K,v:V):MyMapK,V{5.​}}声明类时绑定作用域与为整个类的泛型。K和V在MyMap的每个实例方法和实例属性都可用。在构造方法中不能声明泛型应该在类声明中声明泛型在类内部任何地方都能使用作用域为整个类的泛型实例方法可以访问类一级的泛型而且自己也可以声明泛型。.merge方法使用了类一节的泛型K和V同时还自己声明了两个泛型K1和K2静态方法不能访问类的泛型这就像在值层面不能访问类的实例变量一样。of不能访问1.中声明的K和V不过该方法自己声明了泛型K和V 接口也可以绑定泛型 interface MyMapK,V{get(key:K):Vset(key:K,value:V):void}与函数一样我们可以显式为泛型绑定具体类型也可以让Typescript自动推导 let a new MyMapstring,number(k,1)// MyMapstring,numberlet b new MyMap(k,true)//MyMapstring,booleana.get(k)a.set(k,false)5.8 混入 JavaScript和Typescript都没有trait或mixin关键字不过自己实现起来也不难。这两个特性都用于模拟多重继承一个类扩展两个以上的类可做面向角色编程。 这是一种编程风格在这种风格中我们不表述“这是一个Shape”,而是描述事物的属性表述”这个东西可以度量“或者”这个东西有四条边“我们不再关心”是什么“关系转而描述”能做什么“和”有什么“关系。 下面我们自己手动实现混入。 混入这种模式把行为和属性混合到类中。按照惯例混入有以下特性 可以有状态即实例属性只能提供具体方法与抽象方法相反可以有构造方法调用的顺序与混入类的顺序一致 Typescript没有内置混入的概念不过我们可以自己手动轻易实现。下面我们设计一个调试Typescript类的库以此为例进行说明。作用是输出关于类的一些信息。 class User{//}User.debug() // 求值结果为User({id:3,name:Emma Gluzman})通过这个标准的.debug接口用户便可以调试任何类。下面开始实现。我们将通过一个混入实现这个接口将其命名为withEZDbug。混入其实就是一个函数只不过这个函数接受一个类构造方法而且返回一个类构造方法。这个混入的声明如下 type ClassConstructor new(...args:any[]){} // 1.function withEZDebugC extends ClassConstructor(Class:C){// 2.return class extends Class {//3.constructor(...args:any[]){// 4.super(...args) //5.}}}先声明类型ClassConstructor表示任意构造方法。由于Typescript完全才用结构化类型因此使用new运算符操作的就是构造方法。我们不知道这个构造方法接受什么类型所以指明他可以接受任意个任意类型的参数注意Typescript要求不叫严格构造方法类型的参数必须any[]不能是voidunknown[]等这样才能扩展。声明withEZDebug混入只接受一个类型参数C。C至少是类构造方法。使用extends子句表示这一要求。我们让Typescript推导withEZDebug的返回类型结果是C与该匿名类的交集由于混入是接受一个构造方法并返回一个构造方法的函数所以这里返回一个匿名类构造方法类构造方法至少要接受传入的类型接受的参数。但是注意由于我们事先不知道将传入什么类所以要尽量放宽要求允许传入任意个任意类型的参数跟ClassConstructor一样。最后因为这个匿名类扩展自其他类为了正确建立父子关系别忘了调用Class的构造方法super方法。 与常规的JavaScript一样如果构造方法中没有什么逻辑可以省略4.和5.在这个withEZDebug示例中我们不打算在构造方法中放任何逻辑因此可以省略那两行。 准备工作后下面开始实现调试功能。调用.debug时我们想输出类的构造方法名称和实例的值 type ClassConstructor new(...args:any[]){} // 1.function withEZDebugC extends ClassConstructor(Class:C){// 2.return class extends Class {//3.constructor(...args:any[]){// 4.super(...args) //5.}debug(){let Name Class.constructor.name;let value this.getDebugValue()return Name(JSON.stringify(value))}}}!!!???这里要调用.getDebugValue方法可以我们怎么样确保类实现了这个方法呢 答案是不接受常规的类而是使用泛型确保传给withEZDebug的类定义了.getDebugValue方法 type ClassConstructorT new(...args:any[]){} // 1.function withEZDebugC extends ClassConstructor{getDebugValue():object //1.2}(Class:C){// 2.return class extends Class {//3.constructor(...args:any[]){// 4.super(...args) //5.}debug(){let Name Class.constructor.name;let value this.getDebugValue()return Name(JSON.stringify(value))}}}1.为ClassConstructor添加了一个泛型参数。1.2为ClassConstructor绑定一个结构类型C,规定传给withEZDebug的构造方法至少定义了.getDebugValue方法 最终代码 type ClassConstructorT new(...args:any[]){}​function withEZDebugC extends ClassConstructor{getDebugValue():object}(Class:C){return class extends Class {getDebugValue: any;constructor(...args:any[]){super(...args)}debug(){let Name Class.constructor.name;// 当前作用域中查找没有就去父类中找let value this.getDebugValue()return Name(JSON.stringify(value))}}}​class HardToDebugUser{constructor(private id:number,private firstName:string,private lastName:string){}getDebugValue(){console.log(running);return{id:this.id,name:this.firstName this.lastName}}}​let User withEZDebug(HardToDebugUser)let user new User(3,red,润)console.log(debug中,user.debug());我们可以把任意多个混入混合到类中为类增添更丰富的行为而且这一切在类型上都是安全的。混入有助于封装行为是描述可重用行为的一种重要方式。 很多语言比如ScalaPHPKotlin和Rust实现了精简版混入称为性状trait。性状与混入类似但是没有构造方法也不支持实例属性。 因此性状更容易使用而且不会在多个性状访问性状与基类共用的状态时产生冲突。 5.9 装饰器 装饰器是Typescript的一个实验特性为类类方法属性和方法参数的元编程提供简介的句法。其实装饰器就是子啊装饰目标上调用函数的一种句法。 tsconfig.json添加experimentalDecorators: true开启装饰器这是一个实验特性目前Typescript5.x版支持装饰器第三阶段了本文暂时没涉及学好本节后可以快速过渡。 使用装饰器 serializeableclass APIPayload{getValue():Payload{//}}不使用装饰器 let APIPayload serializeable(class APIPayload{getValue():Payload{//.}})对不同种类的装饰器Typescript要求作用域中有那种装饰器指定名称的函数而且该函数还要具有相应的签名见下表5-1 表5-1:不同种类装饰器函数要具有的类型签名 装饰目标具有的类型签名类(Constructor:{new(...any[])any})any方法(classPrototype:{},methodName:string,descriptor:PropertyDescriptor)any静态方法(Constructor:{new(...any[])any})methodName:string,descriptor:PropertyDescriptor)any方法的参数(classPrototype:{},paramName:string,index:number)void静态方法的参数(Constructor:{new(...any[])any})paramName:string,index:number)any属性(classPrototype:{},propertyName:string)any静态属性(Constructor:{new(...any[])any},propertyName:string)any属性设值方法/读值方法(classPrototype:{},propertyName:string,descriptor:PropertyDescriptor)any静态属性设置方法/读值方法(Constructor{new(...any[])any},propertyName:string,descriptor:PropertyDescriptor)any Typescript没有内置任何装饰器如果你要使用只能自己实现或者从npm中安装。不同种类的装饰器包括类装饰器方法装饰器属性装饰器和函数参数装饰器都是常规函数只不过要满足相应的特定签名。例如前面使用的serializable装饰器可以像下面这样实现 type ClassConstructorT new(...args:any[])T//1.​function serializeableT extends ClassConstructor{getValue():Payload// 2.}(Constructor:T){// 3.return class extends Constructor{// 4.serialize(){return this.getValue()}}}​serializeableclass Payload{nameredrunserialize:anygetValue(): Payload {console.log(this);return this}}let p new Payload()p.serialize()在Typescript中类的构造方法使用new()表示结构化类型。如果类的构造方法可被扩展使用extends,Typescript要求参数的类型为可展开的any即new(…any[])serializable可以装饰任何实现.getValue方法而且返回一个Payload的类的实例类装饰器是一个接受单个参数即目标类的函数。如果装饰器函数返回一个类在运行时这个类将替换被装饰的类否则要返回原类为了装饰目标类我们返回一个扩展原类的类增加.serialize方法。 Typescript假定装饰器不改变装饰器目标的结构意即不增加或删除方法和属性。Typescript在编译时检查返回的类是否可以复制给传入的类。 在Typescript的装饰器称为稳定特性前不建议使用。 使用常规用法 let DecroatedAPIPayload serialized(APIPayload)let payload new DecoratedAPIPayloadpayload.serialize() // string更多装饰器信息官方文档 5.10 模拟final类 final关键字的作用某些语言使用这个关键字把类标记为不可拓展或者把方法标记为不可覆盖。 Typescript的类和方法不支持final关键字但是我们可以轻易模拟 可以使用私有的构造方法模拟final类 class MessageQueue{private constructor(private message:string[]){}}class BadQueue extends MessageQueue{}//报错new MessageQueue()// 报错除了禁止扩展类以外私有的构造方法还禁止直接实例化类。但是我们喜欢final类能够实例化禁止拓展就好那么怎样保留第一个限制而避免第二个限制呢 class MessageQueue{private constructor(private message:string[]){}static create(message:string[]){return new MessageQueue(message)}}// class BadQueue extends MessageQueue{}//报错// new MessageQueue()​MessageQueue.create([])// 创建一个类5.11 设计模式 下面动手实现一两个设计模式 5.11.1 工厂函数 工厂模式factory pattern是创建某种类型的对象的一种方式这种方式把创建哪种具体对象留给创建该对象的工厂决定。 type Shoe {purpose:string}class BalletFlat implements Shoe {purpose dancing}class Boot implements Shoe{purpose woodcutting}​这里使用type此外也可以使用interface 下面创建工厂 type Shoe {purpose:string}class BalletFlat implements Shoe {purpose dancing}class Boot implements Shoe{purpose woodcutting}​let Shoe {create(type:balletFlat|boot):Shoe{switch(type){case balletFlat:return new BalletFlatcase boot:return new Boot}}}这个实例使用伴生对象模式6.3.4节声明类型Shoe和同名值Shoe,以此表明值提供了操作类型的方法。若想使用这个工厂只需要调用.create: Shoe.ceate(boot)5.11.2 建造者模式 建造者模式builder pattern把对象的建造方式与具体的实现方式区分开。如果你用过jquery或者ES6的Map和Set等数据结构对这种API风格不陌生。 class RequestBuilder {private url:string|null nullprivate method:get|post|null nullprivate data:object|null nullsetURL(url:string):this{this.url urlreturn this}setMethod(method:get|post):this {this.method methodreturn this}setData(data:object):this{this.data datareturn this}send(){//}​}​new RequestBuilder().setURL(/user).setMethod(get).setData({firstName:Anna}).send()​按顺序调用 class RequestBuilder {protected data: object | null nullprotected method: get | post | null nullprotected url: string | null null​setMethod(method: get | post): RequestBuilderWithMethod {return new RequestBuilderWithMethod().setMethod(method).setData(this.data)}setData(data: object | null): this {this.data datareturn this}}​class RequestBuilderWithMethod extends RequestBuilder {setMethod(method: get | post | null): this {this.method methodreturn this}setURL(url: string): RequestBuilderWithMethodAndURL {return new RequestBuilderWithMethodAndURL().setMethod(this.method).setURL(url).setData(this.data)}}​class RequestBuilderWithMethodAndURL extends RequestBuilderWithMethod {setURL(url: string): this {this.url urlreturn this}send() {// ...}}​new RequestBuilder().setMethod(get).setData({}).setURL(foo.com).send()
http://www.dnsts.com.cn/news/15223.html

相关文章:

  • 济南网站建设网站建设福建住房与城乡建设网站
  • 哪一个网站做专栏作家好点会展中心网站平台建设方案
  • 网站项目中的工作流程番禺区网站建设哪家好
  • 山东省作风建设网站图片生成网页链接在线
  • 外贸做企业什么网站建设html软件下载手机版
  • 浙江台州网站制作已有域名 搭建网站
  • 公司做网站费用和人员配备怎样做境外电商
  • 东莞网站建设优化企业南京比较大的外贸公司有哪些
  • 廊坊企业建站重庆工厂网站建设
  • 营销型网站建设的流程建设一个本地网站
  • 滕州手机网站建设案例南宁市建设工程质量监督站网站
  • 网站域名的管理密码如何索取wordpress改成英文
  • 如何建设公司的网站在阿里云域名可以做网站吗
  • 电子商务网站建设的目的意义重庆发布微博
  • 做网站图片链接到天猫阿里巴巴国际站买家版
  • 基于阿里云的网站开发自己做淘宝客网站吗
  • 网站建站公司哪家价钱合理中英文网站建设的差别
  • 毕业设计都是做网站吗二手电商怎么做
  • 二手优品哪个网站做国外网站有哪些推荐的
  • 东莞品牌营销型网站建设企业建站系统营销吧tt团队
  • 网站城市分站是怎么做的佛山外贸网站建设公司
  • 河北省建设机械会网站首页广告公司简历模板
  • 站优云网络公司建设银行网网站打不开
  • 网站建设上海网站建设网站显示百度众测是怎么做的
  • app和微网站的对比分析建筑工地招工网
  • 网站建设相关合同内容移动端网站模板
  • 网络建站免费网址祁阳网页设计
  • 做网站视频存储网科创想网站管理
  • 做一个网站需要多少钱大概费用永兴网站建设报价
  • 怎么打开网站百度网址ip地址