ts类中定义静态属性 / 静态方法 /抽象类 / 多态
我们先来回顾一下ES5中定义静态属性和静态类
function People6(){
}
People.doHomework = function(){
console.log("临枫正在做作业");
}
People.whatAreYouDoing = '做作业'
People.doHomework(); //输出 临枫正在做作业
console.log(People.whatAreYouDoing);//输出 :做作业
在ts中定义静态方法
class People7{
public name:string;
static name2:string = "小枫枫";
constructor(name:string){
this.name = name;
}
work(){
console.log(this.name + '在上班')
}
write(){
console.log(this.name + '在写作业')
}
// 静态方法的关键字 static
static reading(){
console.log("小枫枫正在读《typeScript从精通到陌生》")
}
static buybuybuy(){
console.log(this.name+"正在买买买")
}
static buybuybuy2(){
console.log(this.name2+"618正在买买买")
}
}
// 静态方法 不需要new实例 直接调用
People7.reading();//打印:小枫枫正在读《typeScript从精通到陌生》
People7.buybuybuy(); //打印 People7正在买买买 ---> 静态方法里面无法调用类里面的属性
// 如果需要使用 需要定义静态属性 属性前面加关键字 static
People7.buybuybuy2(); //小枫枫618正在买买买
ts中的继承多态
多态:父类定义一个方法不去实现,让继承他的子类去实现 ,每个子类有不同的表现
class People8{
name:string;
constructor(name:string){
this.name = name;
}
duWhat(){
// 做什么? 不知道啊 具体谁在干吗 继承他的子类去实现 子类重写父类方法 每个子类的表现不一样
console.log("在干嘛")
}
}
class linfeng3 extends People8{
constructor(name:string){
super(name);
}
duWhat(){
console.log(this.name + "正在学习")
}
}
let lf8 = new linfeng3("临枫");
// lf8.duWhat();//输出:临枫正在学习
class chaoran extends People8{
constructor(name:string){
super(name);
}
duWhat(){
console.log(this.name + "在种地")
}
}
let cr = new chaoran("大眼");
// cr.duWhat();//输出:大眼在种地
ts中的抽象类
// ts中的抽象类,它是提供其他类继承的基类,不能直接被实例化 用来定义一个标准
// 用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现
// abstract 抽象方法只能放在抽象类中
abstract class People9{
name:string;
constructor(name:string){
this.name = name;
}
abstract write():string
}
// 抽象方法只能出现在抽象类中
// 抽象类中必须包含至少一个抽象方法,不然没有意义
// 抽象类和抽象方法用来定义标准,基类要求他的子类必须包含某种方法
class Linfeng5 extends People9{
constructor(name:string){
super(name)
}
// 父类定义了 write 方法 子类必须包含这个方法 否则报错:非抽象类“Linfeng5”不会实现继承自“People9”类的抽象成员“write”。
write():string{
return this.name + '在写论文';
}
eat():string{
return this.name + '在吃饭';
}
}
let lf10 = new Linfeng5("临枫哇");
// console.log(lf10.write());//打印:临枫哇在写论文
// ts中的接口 定义标准,定义了行为和动作的规范 对批量方法进行约束
// ts中的接口 定义标准,定义了行为和动作的规范 对批量方法进行约束
// 定义一个接口 关键字 interface
interface Tset1{
name:string;
age:number
}
// 传入方法的参数必须要满足接口规定的参数
function fn15(people:Tset1){
console.log(people.name + '今年' + people.age + '岁了');
}
/// 这样写会报错 类型“{ name: string; age: number; sex: number; }”的参数不能赋给类型“Tset1”的参数。对象文字可以只指定已知属性,并且“sex”不在类型“Tset1”中。
// fn15({
// name:'临枫',
// age:18,
// sex:1
// })
// 所以我们建议这样写
// 传入参数必须包含 接口定义的 sting 类型的 namge 和 number 类型的 age
let _obj = {
name:'临枫',
age:18,
sex:1
}
// fn15(_obj); //输出:临枫今年18岁了
// ts的接口 对批量方法进行约束
function fn16(people:Tset1){
console.log(people.name + '性别' + sex?'男':'女' + '今年' + people.age + '岁了');
// 还没开始调用 这里就报错了 报错 : 找不到名称“sex”
// 也就是说必须按照接口约束的参数传入 多一个少一个 都不行 参数可以多传 但是不能去使用
}
案例:通过接口 封装一个jquery的 ajax
// 案例: 封装一个请求数据的 ajax的接口
interface Config{
type:string,
url:string,
data?:string
dataType:string
}
class ${
constructor(){
}
static ajax(config:Config){
let xhr = new XMLHttpRequest();
xhr.open(config.type,config.url,true);
xhr.send(config.data);
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200){
console.log("OK");
if(config.dataType == 'json'){
console.log(JSON.parse(xhr.responseText))
}else{
console.log(xhr.responseText)
}
}
}
}
}
$.ajax({
type:'get',
url:'https://unpkg.com/live2d-widget-model-shizuku@1.0.5/assets/shizuku.model.json',
dataType:'json',
data:'啦啦啦'
})
// 输出:
// expressions: Array(4)
// 0: {name: "f01", file: "exp/f01.exp.json"}
// 1: {name: "f02", file: "exp/f02.exp.json"}
// 2: {name: "f03", file: "exp/f03.exp.json"}
// 3: {name: "f04", file: "exp/f04.exp.json"}
// length: 4
// __proto__: Array(0)
// hit_areas: (2) [{…}, {…}]
ts约束函数的接口
interface constraintFun{
(name:string,age:number,sex?:boolean):object
}
let fn18:constraintFun = function(name:string,age:number):object{
return {
name:name,
age:age
}
}
console.log(fn18("临枫啊",18);)
// 输出:{name: "临枫啊", age: 18}
console.log(fn18("临枫啊",18);)
// 输出:{name: "临枫啊", age: 18}
ts 类类型接口 对类的约束 跟抽象类很像 (常用)
interface People10{
name:string;
study(book:string):string
}
// 这不是继承这是实现
class lf19 implements People10{
name:string;
constructor(name:string){
this.name = name;
}
study(book:string){
return this.name + '在学' + book;
}
}
let fn19 = new lf19("临枫");
console.log(fn19.study("ts从精通到陌生"));
// 打印输出 :临枫在学ts从精通到陌生
ts中 接口的扩展 接口的继承 (接口完结篇)
interface People19 {
study(): void;
}
interface Ultraman extends People19 {
play(): void;
}
class Programma{
name:string;
constructor(name:string){
this.name = name
}
coding(code:string){
console.log(this.name + code)
}
}
class Dijia extends Programma implements Ultraman {
constructor(name: string) {
super(name)
}
// 继承父类 并使用了 Ultraman 继承People19的接口 所以必须包含 play 和study方法
play(){
}
study(){
}
}
ts泛型的定义
软件工程中,我们不仅要创建一致的定义好的API,同时也要考虑可重用性,组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能
在像c#和java中,可以使用泛型来创建可重用的组件,一个组件可支持多种类型的数据,这样用户就可以以自己的数据类型来使用组件
通俗理解:泛型就是解决 类 接口 方法的重用性、以及对不特定数据类型的支持
可以支持不特定的数据类型
// 传入number类型 必须返回 number 传入 string 必须饭string
// 泛型 可以支持不特定的数据类型
// 传入参数和返回参数一致
// T表示泛型 具体什么类型是调用这个方法时候决定的
function fn24<T>(val:T):T{
return val
}
fn24<string>('123')
fn24<number>(123)
function fn25<T>(val:T):any{
return val
}
fn25<string>('这是一个泛型')
console.log(fn25<number>(123))
/泛型类
class MinNum<T>{
public list: T[] = [];
add(num: T) {
this.list.push(num)
}
min() {
let minNum = this.list[0];
for (let i = 0; i < this.list.length; i++) {
if (minNum > this.list[i]) {
minNum = this.list[i]
}
}
return minNum
}
}
let lf22 = new MinNum<number>();
lf22.add(116);
lf22.add(2);
lf22.add(5);
lf22.add(78);
lf22.add(879);
// console.log(lf22.min()) //输出 : 2
let lf23 = new MinNum<string>();
lf23.add('你好啊');
lf23.add('我叫赛丽亚');
lf23.add('今天也是');
lf23.add('不出史诗的一天');
// console.log(lf23.min()) //输出 : 不出史诗的一天
定义泛型接口
interface Fun02{
<T>(val:T):T
}
let runing01:Fun02 = function<T>(a:T):T{
return a
}
runing01<string>("啦啦啦");
runing01<number>(123);
// 第二种方法
interface Fun03<T>{
(val:T):T
}
function runing02<T>(a:T):T{
return a
}
let myRuning02:Fun03<string> = runing02;
myRuning02('20');
把类作为参数 结合泛型类 来约束数据传入的类型
class User{
name:string | undefined;
token:string | undefined;
userId:number | undefined;
}
class SqlDb{
add(info:User):boolean{
console.log(info);
return false
}
}
let u = new User();
u.name = '临枫';
u.token = 'LinF0120',
u.userId = 154;
let db = new SqlDb();
db.add(u);
// 打印: User {name: "临枫", token: "LinF0120", userId: 154}
class People25{
name:string | undefined;
sex:boolean | undefined;
age:number | undefined;
}
class Sql02<T>{
add(user:T):boolean{
console.log(user)
return true
}
}
let lf25 = new People25();
lf25.name = '临枫';
lf25.sex = true;
lf25.age = 18;
// let sql = new Sql02();
// sql.add('56454');//传错误类型 但是不报错
let sql = new Sql02<People25>(); //这样就不报错了
sql.add(lf25);
模块 把一些公共的功能单独抽离成一个文件作为一个模块
// 模块导出
export function lf001(){
console.log('lf001')
}
// 或者
function lf0002(){
console.log('lf002')
}
export { lf0002 }
模块引入
// 引入模块
import { lf001, lf0002 as lalala } from "模块文件路径";
lf001();
lalala();
命名空间 用于组织代码 避免命名冲突
namespace LF{
interface User{
name:string;
age?:number;
study():void;
}
export class People30 implements User{
name:string;
age:number;
constructor(name:string,age:number){
this.name = name;
this.age = age;
}
study():void{
console.log(this.name + '正在学习')
}
}
}
let lfy = new LF.People30("临枫",18);
lfy.study(); //临枫正在学习
namespace DY{
interface User{
name:string;
age?:number;
study():void;
}
export class People30 implements User{
name:string;
age:number;
constructor(name:string,age:number){
this.name = name;
this.age = age;
}
study():void{
console.log(this.name + '正在学习')
}
}
}
let Dy = new DY.People30("大眼",18);
Dy.study(); //大眼正在学习
ts的装饰器
// 装饰器是一种特殊类型的声明,他能够被附加到类声明,方法,属性或者参数上,可以修改类的行为。
// 通俗的将装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。
// 常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
// 装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)
// 装饰器是过去几年中js最大的成就之一,已经是ES7的标准特性之一
// 普通装饰器:
interface Folk {
name: string;
age?: number;
}
function APPConfig(params: any) {
console.log(params);//params就是当前类
params.prototype.motto = '今天学了习,明天更牛逼';
params.prototype.say = function():void{
console.log(this.name + this.motto)
};
}
namespace LF {
@APPConfig
export class LinFengYa implements Folk {
name: string;
age?: number;
constructor(name: string) {
this.name = name
}
play():string{
return this.name +"再也不打游戏了"
}
}
}
let xff:any = new LF.LinFengYa("小枫枫");
console.log(xff.play()); //输出 : 小枫枫再也不打游戏了
console.log(xff.motto);//这里会报错 指定xff为any类型就好了 输出:今天学了习,明天更牛逼
xff.say();//小枫枫今天学了习,明天更牛逼
// 装饰器工厂(可传参)
function LfConfig(params:any){
return function(data:any){
console.log(data);
//打印 ƒ People31(name) {
// this.name = name;
// }
console.log(params);//打印www.linfengya.cn
data.prototype.baseUrl = params;
}
}
interface Folk0{
name:string;
age?:number;
}
@LfConfig("www.linfengya.cn")
class People31 implements Folk0{
name:string;
age?:number;
constructor(name:string){
this.name = name
}
}
let xffy:any = new People31("临枫呀");
console.log(xffy.baseUrl);//输出 www.linfengya.cn
使用类装饰器 重载构造函数 一个参数 即构造函数
function zsq(data: any) {
console.log(data);
return class extends data{
apiUrl:any = '我被修改了额';
getUrl(){
return this.apiUrl;
}
}
}
@zsq
class HttpClient {
public apiUrl: string | undefined
constructor() {
this.apiUrl = 'www.linfengya.cn'
}
getUrl() {
console.log(this.apiUrl)
}
}
let linfeng:any = new HttpClient();
console.log(linfeng.getUrl()); //打印 我被修改了额
// 属性装饰器 表达式会在运势是当做函数被调用 传入一下两个参数:
// 1.对于静态成员来说是类的构造函数 对于实例的成员是类的原型对象
// 2.成员的名字
function lfProto(params:any) {
return function(target:any,attr:any){
console.log(target); //输出 :{getUrl: ƒ, constructor: ƒ}
console.log(attr); // 输出: url
console.log(params);//输出: www.linfengya.cn
target[attr] = params
}
}
class People32{
@lfProto("www.linfengya.cn")
static url:string = '嘟嘟嘟嘟'
constructor(){
}
static getUrl(){
console.log(this.url);
}
}
People32.getUrl();//输出 www.linfengya.cn
// 方法装饰器 三个参数
function LfMethod(params:any){
return function(target:any,methodName:any,desc:any){
console.log(target); //当前原型对象 {getUrl: ƒ, constructor: ƒ}
console.log(methodName); //当前类中的方法
console.log(desc);//描述{writable: true, enumerable: true, configurable: true, value: ƒ}
target.testUrl = '滴滴滴滴滴';
target.study = function(){
console.log("学习");
}
console.log(desc.value);
// 打印的是当前方法:
// ƒ () {
// console.log(this.baseUrl);
// }
// 修改装饰器的方法 把装饰器方法传入参数改为string类型
// 1.保存当前方法
let oMethod = desc.value;
desc.value = function(...args:any[]){
args = args.map((v)=>{
return v.toString();
})
console.log(args);//输出 (2) ["sds", "4444"]
oMethod.apply(this,args)
}
}
}
class People33 {
public baseUrl:string|undefined;
constructor() {
}
@LfMethod('www.linfengya.cn')
getUrl(...args:any[]){
console.log('我是getUrl',args);
}
}
let lf33:any = new People33();
lf33.getUrl('sds',4444); //打印 我是getUrl (2) ["sds", "4444"]
装饰器的执行顺序
// 装饰器执行顺序
// 属性 > 方法 > 方法参数 > 类
function logClass1(params:any){
return function(target:any){
console.log("类装饰器1");
}
}
function logClass2(params:any){
return function(target:any){
console.log("类装饰器2");
}
}
function logAttr(params?:any){
return function(target:any,attrName:any){
console.log("属性装饰器");
}
}
function logMeth(params?:any){
return function(target:any,attrName:any,desc:any){
console.log("属性装饰器");
}
}
function logParams(params?:any){
return function(target:any,attrName:any,desc:any){
console.log("方法参数装饰器1");
}
}
function logParams2(params?:any){
return function(target:any,attrName:any,desc:any){
console.log("方法参数装饰器2");
}
}
@logClass1("/*****")
@logClass2("------")
class HttpClient01{
@logAttr()
public url:any | undefined;
constructor(){
}
@logMeth()
getData(@logParams("xxxx") uuid:any,@logParams2("xxxx") dd:any){
}
}
var llllffff = new HttpClient01();
// 先后输出:
// 属性装饰器
// index.js:981 方法参数装饰器2
// index.js:976 方法参数装饰器1
// index.js:971 属性装饰器
// index.js:961 类装饰器2
// index.js:956 类装饰器1
评论