企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
** class的super,有两种指向,在静态方法和构造函数,指向父类,在普通函数中,指向父类的prototype ** ### 5.8 抽象类 * 抽象描述一种抽象的概念,无法被实例化,只能被继承 * 无法创建抽象类的实例 * 抽象方法不能在抽象类中实现,只能在抽象类的具体子类中实现,而且必须实现 ~~~ abstract class Animal3 { name:string; abstract speak(); } class Cat extends Animal3{ speak(){ console.log('喵喵喵'); } } let cat = new Cat(); cat.speak(); ~~~ | 访问控制修饰符 | private protected public | | --- | --- | | 只读属性 | readonly | | 静态属性 | static | | 抽象类、抽象方法 | abstract | ### 5.9 抽象类 vs 接口 * 不同类之间公有的属性或方法,可以抽象成一个接口(Interfaces) * 而抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现 * 抽象类本质是一个无法被实例化的类,其中能够实现方法和初始化属性,而接口仅能够用于描述,既不提供方法的实现,也不为属性进行初始化 * 一个类可以继承一个类或抽象类,但可以实现(implements)多个接口 * 抽象类也可以实现接口 ~~~ abstract class Animal5{ name:string; constructor(name:string){ this.name = name; } abstract speak(); } interface Flying{ fly() } class Duck extends Animal5 implements Flying{ speak(){ console.log('汪汪汪'); } fly(){ console.log('我会飞'); } } let duck = new Duck('zhufeng'); duck.speak(); duck.fly(); ~~~ ### 5.10 抽象方法 * 抽象类和方法不包含具体实现,必须在子类中实现 * 抽象方法只能出现在抽象类中 ~~~ abstract class Animal{ abstract speak():void; } class Dog extends Animal{ speak(){ console.log('小狗汪汪汪'); } } class Cat extends Animal{ speak(){ console.log('小猫喵喵喵'); } } let dog=new Dog(); let cat=new Cat(); dog.speak(); cat.speak(); ~~~ ### 5.11 重写(override) vs 重载(overload) * 重写是指子类重写继承自父类中的方法 * 重载是指为同一个函数提供多个类型定义 ~~~ class Cat6 extends Animal6{ speak(word:string):string{ return 'Cat:'+word; } } let cat6 = new Cat6(); console.log(cat6.speak('hello')); function double(val:number):number function double(val:string):string function double(val:any):any{ if(typeof val == 'number'){ return val *2; } return val + val; } let r = double(1); console.log(r); ~~~ ### 5.12 继承 vs 多态 * 继承(Inheritance)子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性 * 多态(Polymorphism)由继承而产生了相关的不同的类,对同一个方法可以有不同的响应 ~~~ class Animal7{ speak(word:string):string{ return 'Animal: '+word; } } class Cat7 extends Animal7{ speak(word:string):string{ return 'Cat:'+word; } } class Dog7 extends Animal7{ speak(word:string):string{ return 'Dog:'+word; } } let cat7 = new Cat7(); console.log(cat7.speak('hello')); let dog7 = new Dog7(); console.log(dog7.speak('hello')); ~~~ ## 6\. 接口 * 接口一方面可以在面向对象编程中表示为`行为的抽象`,另外可以用来描述`对象的形状` * 接口就是把一些类中共有的属性和方法抽象出来,可以用来约束实现此接口的类 * 一个类可以继承另一个类并实现多个接口 * 接口像插件一样是用来增强类的,而抽象类是具体类的抽象概念 * 一个类可以实现多个接口,一个接口也可以被多个类实现,但一个类的可以有多个子类,但只能有一个父类 ### 6.1 接口 * interface中可以用分号或者逗号分割每一项,也可以什么都不加 ~~~ //接口可以用来描述`对象的形状`,少属性或者多属性都会报错 interface Speakable{ speak():void; name?:string;//?表示可选属性 } let speakman:Speakable = { name:string;//多属性也会报错 speak(){}//少属性会报错 } ~~~ ~~~ //接口可以在面向对象编程中表示为行为的抽象 interface Speakable{ speak():void; } interface Eatable{ eat():void } class Person5 implements Speakable,Eatable{ speak(){ console.log('Person5说话'); } eat(){} } class TangDuck implements Speakable{ speak(){ console.log('TangDuck说话'); } eat(){} } ~~~ ~~~ //无法预先知道有哪些新的属性的时候,可以使用 `[propName:string]:any`,propName名字是任意的 interface Person { readonly id: number; name: string; [propName: string]: any; } let p1 = { id:1, name:'zhufeng', age:10 } ~~~ ### 6.2 接口的继承 * 一个接口可以继承自另外一个接口 ~~~ interface Speakable{ speak():void } interface SpeakChinese extends Speakable{ speakChinese():void } class Person5 implements SpeakChinese{ speak(){ console.log('Person5') } speakChinese(){ console.log('speakChinese') } } ~~~ ### 6.3 readonly * 用 readonly 定义只读属性可以避免由于多人协作或者项目较为复杂等因素造成对象的值被重写 ~~~ interface Person{ readonly id:number; name:string } let tom:Person = { id :1, name:'zhufeng' } tom.id = 1; ~~~ ### 6.4 函数类型接口 * 对方法传入的参数和返回值进行约束 ~~~ interface discount{ (price:number):number } let cost:discount = function(price:number):number{ return price * .8; } ~~~ ### 6.5 可索引接口 * 对数组和对象进行约束 * userInterface 表示:只要 index 的类型是 number,那么值的类型必须是 string * UserInterface2 表示:只要 index 的类型是 string,那么值的类型必须是 string ~~~ interface UserInterface { [index:number]:string } let arr:UserInterface = ['zfpx1','zfpx2']; console.log(arr); interface UserInterface2 { [index:string]:string } let obj:UserInterface2 = {name:'zhufeng'}; ~~~ ### 6.6 类接口 * 对类的约束 ~~~ interface Speakable{ name:string; speak(words:string):void } class Dog implements Speakable{ name:string; speak(words){ console.log(words); } } let dog=new Dog(); dog.speak('汪汪汪'); ~~~ ## 7\. 泛型 * 泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性 * 泛型`T`作用域只限于函数内部使用 ### 7.1 泛型函数 * 首先,我们来实现一个函数 createArray,它可以创建一个指定长度的数组,同时将每一项都填充一个默认值 ~~~ function createArray(length: number, value: any): Array<any> { let result: any = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; } let result = createArray(3,'x'); console.log(result); ~~~ ~~~ function createArray(length: number, value: any): Array<any> { let result: any = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; } let result = createArray(3,'x'); console.log(result); function createArray2<T>(length: number, value: T): Array<T> { let result: T[] = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; } let result2 = createArray2<string>(3,'x'); console.log(result); ~~~ ### 7.2 类数组 * 类数组(Array-like Object)不是数组类型,比如`arguments` ~~~ function sum(...parameters:number[]){ let args:IArguments = arguments; for(let i=0;i<args.length;i++){ console.log(args[i]); } } sum(1,2,3); let root = document.getElementById('root'); let children:HTMLCollection = root.children; children.length; let nodeList:NodeList = root.childNodes; nodeList.length; ~~~ ### 7.3 泛型类 ~~~ class MyArray<T>{ private list:T[]=[]; add(value:T) { this.list.push(value); } getMax():T { let result=this.list[0]; for (let i=0;i<this.list.length;i++){ if (this.list[i]>result) { result=this.list[i]; } } return result; } } let arr=new MyArray(); arr.add(1); arr.add(2); arr.add(3); let ret = arr.getMax(); console.log(ret); ~~~ ### 7.5 泛型接口 * 泛型接口可以用来约束函数 ~~~ interface Calculate{ <T>(a:T,b:T):T } let add:Calculate = function<T>(a:T,b:T){ return a; } add<number>(1,2); ~~~ ### 7.6 多个类型参数 * 泛型可以有多个 ~~~ function swap<A,B>(tuple:[A,B]):[B,A]{ return [tuple[1],tuple[0]]; } let swapped = swap<string,number>(['a',1]); console.log(swapped); console.log(swapped[0].toFixed(2)); console.log(swapped[1].length); ~~~ ### 7.7 默认泛型类型 ~~~ function createArray3<T=number>(length: number, value: T): Array<T> { let result: T[] = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; } let result2 = createArray3(3,'x'); console.log(result2); ~~~ ### 7.8 泛型约束 * 在函数中使用泛型的时候,由于预先并不知道泛型的类型,所以不能随意访问相应类型的属性或方法。 ~~~ function logger<T>(val:T){ console.log(val.length) } interface LengthWise{ length:number } //可以让泛型继承一个接口 function logger2<T extends LengthWise>(val:T){ console.log(val.length) } logger2(1); logger2('zhufeng'); ~~~ ### 7.9 泛型接口 * 定义接口的时候也可以指定泛型 ~~~ interface Cart<T>{ list:T[] } let cart:Cart<{name:string,price:number}> = { list:[{name:'zhufeng',price:10}] } console.log(cart.list[0].name,cart.list[0].price); ~~~ ### 7.10 泛型类型别名 * 泛型类型别名可以表达更复杂的类型 ~~~ type Cart<T> = {list:T[]} | T[]; let c1:Cart<string> = {list:['1']}; let c2:Cart<number> = [1]; ~~~ ### 7.11 泛型接口 vs 泛型类型别名 * 接口创建了一个新的名字,它可以在其他任意地方被调用。而类型别名并不创建新的名字,例如报错信息就不会使用别名 * 类型别名不能被 extends和 implements,这时我们应该尽量使用接口代替类型别名 * 当我们需要使用联合类型或者元组类型的时候,类型别名会更合适 ~~~ //为什么会有泛型,它的意义在哪里 import React from 'react'; namespace a { //定义函数 类 function createArray<T>(length: number, value: T): Array<T> { let result: Array<T> = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; } let result = createArray<number>(3, 3); console.log(result); let result2 = createArray<string>(3, 'x');//就相当 于一个参数 console.log(result2); //类数组 ArrayLike arguments function sum(...args2: any[]) { let args: IArguments = arguments; for (let i = 0; i < args.length; i++) { console.log(args[i]); } } sum(1, 2, '3', 4); //ReferenceError: document is not defined /* let root: HTMLElement | null = document.getElementById('root'); let children: HTMLCollection = root!.children; let childNodes: NodeListOf<ChildNode> = root!.childNodes; */ class MyArray<T>{ private list: T[] = []; add(val: T) { this.list.push(val); } getMax(): T { let result: T = this.list[0]; for (let i = 1; i < this.list.length; i++) { if (this.list[i] > result) { result = this.list[i]; } } return result; } } let arr = new MyArray<number>(); arr.add(1); arr.add(2); arr.add(3); let result3: number = arr.getMax(); console.log(result3); //接口泛型 /* interface Calculate<T> { (a: T, b: T): T t?: T } let add: Calculate<string> = function (a: string, b: string) { return a; } */ interface Calculate { <T>(a: T, b: T): T } let add: Calculate = function (a, b) { return a; } let result4 = add(2, 2); //console.log(result4); //多个类型参数 如何在不增加中间变量的情况下,交换二个变量的值 function swap<A, B>(tuple: [A, B]): [B, A] { return [tuple[1], tuple[0]]; } let result5 = swap<string, number>(['zhufeng', 10]); console.log(result5);//[10,'zhufeng'] //let a = 1, b = 2; //[b, a] = [a, b]; // 默认泛型类型 function createArray2<T = string>(length: number): T | null { let t: T | null = null; return t; } let result6 = createArray2<boolean>(3); //泛型的约束 //在函数中使用泛型的时候,由于预先并不知道具体的类型,所以不能访问相应类型的方法 interface LengthWise { length: number } function logger<T extends LengthWise>(val: T) { console.log(val.length); } logger('zhufeng'); interface Cart<T> { list: T[] } let cart: Cart<string> = { list: ['1', '2', '3'] } // 泛型类型别名 type Cart2<T> = { list: T[] } | T[]; let c1: Cart2<string> = { list: ['1'] }; let c2: Cart2<string> = ['1'] //interface 定义一个实实在在的接口,它是一个真正的类型 //type一般用来定义别名,并不是一真正的类型 } ~~~