ts学习(初)文章格式乱了

发布于 2021-10-18  126 次阅读


TS简介 Ts微软开发,包含ES6、包含ES5 编译 tsc xx.ts 每一次都得编译,可以自动编译 开发工具中配置typescirpt自动编译 vscode: 创建tsconfig.json文件 tsc --init 生成配置文件 tsconfig.json配置文件中,修改outDir配置项,取消注释然后修改为.js vscode中,点击上方栏位run task,选择ts监听 完成 TS类型 与es5中的区别 // es5:类型变化不报错 var flag = true; flag = 234; // ts:必须指定类型 typescript var flag:boolean=true; flag = 131;//报错 TS类型: 1.boolean 2.number 3.string 4.array数组: 方式1:var arr:number[] = [1,2,3]//制定arr里面全是数字 方式2:var arr:Array= [1,2,3] 5.元组类型(tuple) 方式1:属于数组的一种,即数组中每一个元素指定类型 方式2:var arr:[number, string]=[123,“this is ts”]; 6.枚举类型(enum) // 常用来标识状态码 enum Flag{ success=1, error=2 } let f:Flag=Flag.error; console.log(f);// 2 // 如果 没有标识符没有赋值,那么打印的就是下标 enum Color{blue,red,orange}; var c:Color=Color.red; console.log(c); //1,下标 enum Color{blue,red=3,orange}; var c:Color=Color.red; console.log(c); //3 // 常用来标识状态码 enum Err{ 'undefined'=-1, 'null'=-2, } var c:Err=Err.null console.log(c) // -2 7.任意类型any 类似ES5不指定变量类型的var var num:any=123; num = true;// 不报错 8.null类型和undefined其他数据类型的子类型 变量定义之后没有赋值,报undefined // 一个元素可能是number类型,可能是null或者undefined var num:number | undefined | null; 9.void,和java一样 没有返回值类型 // 如果方法没有返回值 function run():void{ console.log('asdf') } // 如果方法有返回值: function run():number{ return 1; } 10.never类型,代表从来不会出现的值,是其他类型(包括null‘和undefined)的子类型,代表从不会出现的值 自己理解为上述类型之外的数据类型 // 如下,接收Err类型的数据 var a:never; a = undefined;//报错 a = (()=>{ new Throw Err("报错") })() 函数的定义 ES5中: // 函数声明法 function run(){ return ... } //匿名函数 var run2 = function(){ return .. } TS中: //函数声明法 function run():number{ return 123; } // 匿名函数 var fun2=function():number{ return 123; } ts中定义方法传参 function getInfo(name:string, age:number):string{ return name + " " + age; } var getInfo= function(name:string, age:number):string{ return name+age; } 方法可选参数 // es5里方法实参和形参可以不一样,但是ts必须一致,如果不一样就需要配置可选参数 参数后边加?可以设置参数可选传 可选参数必须配置到参数的最后边 function getInfo(name:string, age?number):string{ return … } 默认参数 // 默认参数,直接在形参赋值 function getInfo(name:string, age:number=20):string{ return… } 剩余参数 function sum(a:number, b:number, c:number, d:number):number{ return a+b+c+d; } // 三点运算符:接收不固定参数的(剩余参数)的值 function sum(…rest:number[]):number{ var sum= 0 ; for(var i=0; i<rest.length;i++){ sum+=rest[i]; } return sum; } 函数重载 // 类似java,同名但是不同参数的多个函数方法 // ts为了兼容es5,以及es6,和java有区别 // es5中,出现同名方法时候,下面的方法会替换上面的方法 ts中的重载: function getInfo(name:string):string; function getInfo(age:number):string; function getInfo(str:any):any{ if(typeof str ==="string"){ return "我叫:"+ str; }else{ return "我的年龄是:" + str; } } 箭头函数 箭头函数里面的this指向上下文 类 1、ES5中定义类: function Person(){ this.name='张三'; this.age = 20; } var p = new Person(); alert(p.name); 2、构造函数和原型链里面定义 // 声明的构造方法 function Person(){ this.name = "张三"; this.age=20; this.run = function()){ alert(this.name+"在运动"); } } // 原型链的属性和方法 Person.prototype.sex="男"; Person.prototype.work=function(){ alert(xx) } var p = new Person(); p.work(); 3、静态方法 4、es5中的继承-对象冒充继承 // 要实现Web类 继承 Person类 原型链+对象冒充的组合继承模式 function Person(){ this.name = "张三"; this.age=20; this.run = function()){ alert(this.name+"在运动"); } } // 原型链的属性和方法 Person.prototype.sex="男"; Person.prototype.work=function(){ alert(xx) } // 要实现Web类 继承 Person类 function Web(){ Person.call(this); //对象冒充继承 } var w = new Web(); w.run();//执行父类Person的run,对象冒充可以继承构造函数里面的属性和方法 w.work();// 对象冒充可以继承构造函数的属性和方法 但是没办法继承原型链的属性和方法(prototype) 关于call: function add(c, d) { return this.a + this.b + c + d; } const obj = { a: 1, b: 2 }; console.error(add.call(obj, 3, 4)); // 10 大统上的说法就是,call改变了this的指向。然后,介绍this xxx什么一大堆名词,反正不管你懂不懂,成功绕晕你就已经ok了,但是实际发生的过程,可以看成下面的样子。 const o = { a: 1, b: 2, add: function(c, d) { return this.a + this.b + c + d } }; 给o对象添加一个add属性,这个时候 this 就指向了 o, o.add(5,7)得到的结果和add.call(o, 5, 6)相同。 5、原型链继承方法 function Person(){ this.name = "张三"; this.age=20; this.run = function()){ alert(this.name+"在运动"); } } // 原型链的属性和方法 Person.prototype.sex="男"; Person.prototype.work=function(){ alert(xx) } // web原型链方式继承 person function web(){ } web.prototype= new person();// 原型链继承 web.work();// 可以工作,可以继承原型链属性方法 6、原型链实现继承的问题??无法给父类传参 function Person(){ this.name = "张三"; this.age=20; this.run = function()){ alert(this.name+"在运动"); } } // 原型链的属性和方法 Person.prototype.sex="男"; Person.prototype.work=function(){ alert(xx) } var p = new person('李四', 20); p.run(); // 没问题 // 继承,无法给父类传参 function Web(name,age){ } Web.prototype= new Person(); var w = new Web('sss', 20); w.run();// 父类会alert出来“undefiend在运动” // 实例化子类时候没法给父类传参 7、原型链+构造函数的组合继承模式 function Person(){ this.name = "张三"; this.age=20; this.run = function()){ alert(this.name+"在运动"); } } // 原型链的属性和方法 Person.prototype.sex="男"; Person.prototype.work=function(){ alert(xx) } var p = new person('李四', 20); p.run(); // 没问题 // 继承,无法给父类传参 function Web(name,age){ Person.call(this,name,age); // 对象冒充继承 可以继承构造函数里面的属性和方法 实例化子类可以给父类传参 } Web.prototype = new Person();// 实例化 var w = new Web('sss', 20); w.run();// 父类会alert出来“undefiend在运动” // 实例化子类时候没法给父类传参 8、原型链+对象冒充的另一种写法 function Person(){ this.name = "张三"; this.age=20; this.run = function()){ alert(this.name+"在运动"); } } // 原型链的属性和方法 Person.prototype.sex="男"; Person.prototype.work=function(){ alert(xx) } var p = new person('李四', 20); p.run(); // 没问题 // 继承,无法给父类传参 function Web(name,age){ Person.call(this,name,age); // 对象冒充继承 可以继承构造函数里面的属性和方法 实例化子类可以给父类传参 } Web.prototype = Person.prototype; // 和方法7中不同的是这里!!! var w = new Web('sss', 20); w.run();// 父类会alert出来“undefiend在运动” // 实例化子类时候没法给父类传参 类的定义 1、ts中定义类,类似java: class Person(){ name:string; //属性 前面省略了private construtor(n:string){ this.name = n; } run():void{ log(this.name); } } 2、继承 class Web extends Person{ constructor(name:string){ super(name); } } var w = new Web("李四"); alert(w.run()); 3、类里面的修饰符 ts提供了三种修饰符: public(默认的): 公有 在类里面、子类类外边都可以访问 protected:在类里面、子类里面可以访问、在类外部无法访问 private:在类里面可以访问、子类、类外部没法访问 静态属性 静态方法 function Person(){ this.run1=function(){// 实例方法 } } Person.run2=function(){} // 静态方法 调用实例方法:(实例化之后才能调用的) var p = new Person(); p.run1(); 调用静态方法: Person.run2(); 为什么会有静态方法和实例方法之分? JQuery的实例方法css()方法和静态方法$.get()方法大概原码为: // 生命一个节点/元素对象 function Base(element){ this.element = 获取dome节点的方法; this.css = function(str, value){ this.element.style[str] = value; } } // $方法去实例化这个BAse对象、实例方法 function $(element){ return new Base(element); // 实例化一个方法 } // 静态方法get $.get(){ 。。。 } // 那么css调用时候就可以这样写 实例方法: $("#box").css("color", "red"); 静态方法: $.get('url', function(){ ) 另一种方式声明静态方法,利用static关键字声明: class Person{ public name:string; static sex = "男"; constructor(name:string){ this.name = name; } static print(){// 静态方法 里面没办法直接调用类里面的属性, alert("静态方法:"+Person.sex);// 如果调用this.name就会报错!!! } } // $.get(){// jq里面的get就是静态方法 } 多态 父类定义一个方法不去实现,让继承他的子类去实现,每一个子类都有不同的表现 抽象方法 // 用来定义一个标准 // ts中的抽象类,它是提供其他类继承的基类,不能直接被实例化 // 用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现 // abstract 抽象方法只能放在抽象类中 // 抽象类和抽象方法用来定义标准,基类要求他的子类必须包含某种方法 // 抽象方法只能出现在抽象类中 // 抽象类中必须包含至少一个抽象方法,不然没有意义 abstract class Animal{ // 省略构造方法 abstract eat():any; } // 抽象类无法直接实例化 var a = new Animal();// 这句话是错的 class Dog extends Animal{ // 省略构造方法 eat(){ console.log(this.name + '吃'); } } var d = new Dog("sss") d.eat();// sss吃 接口 // 也是定义标准,定义了行为和动作的规范 1、属性接口 // 定义了这个方法的参数是jsonObjec,而且必须有 function printLabel(labelInfo:{label:string}):void{ console.log(labelInfo.label); } printLabel("ssss"); // 错误 printLabel({name:"asdf"}); // 错误 printLabel({label:"sss"}); // 正确,打印sss 2、接口,对批量方法进行约束 // 对批量方法传入参数进行约束 // 传入对象的约束 // 声明类型 interface FullName{ firstName:string; // 注意是;结束 secondName:string; } // 方法名中引用FullName类型 function printName(name:FullName){ log(name.firstName +" "+ name.secondName); } // 调用方式一(下方调用方式是会报错的,interface定义的属性object只能包含firstName和secondName) printName({ age:20, firstName: "张", secondName: "三" }) // 调用方式二,下方引入其他的object即可忽略多余参数 var obj = { age:20, firstName: "张", secondName: "三" }; printName(obj);// 这个不报错 3、接口、可选属性,加?问号表示可传可不传 interface FullName{ firstName:string; secondName?:string;// secondName可传可不传 } 4、模拟封装一个ajax interface Config{ type:string; url:string; data?:string; dataType:string; } 5、函数类型接口、对方法传入的参数、以及返回值进行约束、批量约束 // 例子:定义加密的函数类型接口 interface encrypt{ // 定义了函数参数为string、value,返回string (key:string,value:string):string; } var md5:encrypt=function(key:string, value:string):string{ // 实现encrypt类型的函数 return key+value;//模拟下 } 6、可索引接口:对数组、对象的约束 ts定义数组方法: var arr:number[]=[123,234]; var arr1:Array = ['123', '222']; // 对数组的约束,数组类型接口 interface UserArray{ // 表示数组中index必须是number,value必须是string [index:numer]:string; } var arr:UserArray=['123', '22312']; // 对对象的约束,对象类型接口 interface UserObj{ [index:string]:string; } var obj:UserObj={name:"2342"}; // 对类的约束,类类型接口,和抽象类有点相似 interface Animal{ // 规定实现类必须要有name属性和eat方法 name:string; eat(str:string):void; } class Dog implements Animal{ name:string;// 若没此属性,ts会编译报错 constructor(name:string){ this.name = name; } eat(){ log("eat") } } // 接口的扩展:接口可以继承接口 interface Animal{ eat():void; } interface Person extends Animal{ work():void; } class Web implements Person{ public name:string; constructor(name:string){ this.name = name; } // eat必须定义 eat(){ log(this.name+"吃") } // work也必须定义 work(){ log(this.name+"工作") } } interface Animal{ eat():void; } interface Person extends Animal{ work():void; } class Programmer{ 构造方法省略 coding(code:string){ log(this.name+ " "+code) } } class Web extends Programmer implements Person{ public name:string; constructor(name:string){ this.name = name; } // eat必须定义 eat(){ log(this.name+"吃") } // work也必须定义 work(){ log(this.name+"工作") } } 泛型 和any有什么区别? any放弃了类型检查 如果想做到传入什么类型就返回什么类型,例如传入number就返回number,这时候就可以使用泛型 function getData(value:any):any{ return ""//什么类型都可以 } 泛型: 软件工程中,我们不仅要创建一致的定义好的API,同时也要考虑可重用性,组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能 在像c#和java中,可以使用泛型来创建可重用的组件,一个组件可支持多种类型的数据,这样用户就可以以自己的数据类型来使用组件 通俗理解:泛型就是解决 类 接口 方法的重用性、以及对不特定数据类型的支持 可以支持不特定的数据类型 function getData(value:T):T{ return value;//传入什么返回什么 } // 这样调用 getData(123123); getData("12131"); // 也可以写成: function getData(value:T):any{ return value;//传入什么返回什么 } // 泛型类,比如有个最小堆算法,需要同时支持返回数字和字符串两种类型,通过类的泛型来实现,示例: // 定义泛型类 class MinClass{ public list:number[]=[]; add(num:number){ this.list.push(num); } min():number{ var minNum = this.list[0]; for(var i = 0;i<this.list.length;i++){ if(minNum>this.list[i]){ minNum=this.list[i]; } } return minNum; } } // 调用 var m = new MinClass(); m.add(3); m.add(2); log(m.min());// 2 但是上边的只能传入数字类型,是否可以用泛型解决?可以: class MinClass{ public list:T[]=[]; add(num:T):void{ this.list.push(num); } min():T{ var minNum = this.list[0]; for(var i = 0;i<this.list.length;i++){ if(minNum>this.list[i]){ minNum=this.list[i]; } } return minNum; } } // 调用,实例化时候要先声明参数类型(); m1.add(2); m1.add(4); log(m.min());// 2 函数类型接口 指定特殊类型的方法: interface ConfigFn{ (value1:string, value2:string):string; } var setData:ConfigFn=function(value1:string, value2:string):string{ return value1 + value2; } setData("name", "张三); 泛型接口写法1: interface Config{ (value:T):T; } var getData:ConfigFn=function(value:T):T{ return value; } getData("张三"); 泛型接口写法2: interface Config{ (value:T):T; } function getData(value:T):T{ return value; } var myGetData:ConfigFn=getData; myGetData("张三"); // 把类作为参数来约束数据传入的类型 class User{ username:string | undefined; password:string | undefined; } class MySqlDb{ add(user:User):boolean{ console.log(user); retrun true; } } // 调用 var u = new User(); u.username="张三"; u.password="123456"; var Db = new MySqlDb(); Db.add(u);// console.log(u) // 上述方法可以改为泛型类 // 操作数据库的泛型类,这样可以规范插入数据库数据的类规范 class MySqlDb{ add(info:T):boolean{ log(info); return true; } } // 想给User表增加数据 // 1、定义一个User类 和数据库进行映射 class User{ username:string | undefined; password:string | undefined; } var u = new User(); u.username= '张三'; u.password="2312"; var Db = new MySqlDb();// 这一步很关键,要定义User类型 Db.add(u); // 2、文章类,数据库映射 class Article{ title:string | undefined; desc:string | undefined; status:number | undefined; constructor(params:{ title:string | undefined; desc:string | undefined; status?number | undefined;// status可选参数 }){ this.title=params.title; this.desc=params.desc; this.status=params.status; } } // 调用 var a = new Article({ title:"分类", desc:"111", status:1 }) //类当前参数的泛型类 var Db = MySqlDB

();// 指定类型 Db.add(a);// log a 实战:要实现TS封装统一操作Mysql Mongodb Mssql的底层库 // 先定义一个接口,用于提供各类型数据库规范 interface DBI{ add(info:T):boolean; update(info:T, id:number):boolean; delete(id:number):boolean; get(id:number):any[]; } // 定义一个操作mysql的类,注意 要实现泛型接口 这个类也应该一定是个泛型类 class MysqlDb implements DBI{ add(info:T): boolean{ log(info); } update... delete... get... } // 调用 操作数据表,定义一个User类和数据库进行映射,并进行MySql数据的插入操作 class User{ username:string | undefined; password:string | undefined; } var u = new User(); u.username = "张三"; u.password="213"; var oMysql = new MysqlDb();// 声明User类型参数 oMysql.add(u);// 插入 模块 概念: 把一些公共的功能单独抽离成一个文件作为一个模块 模块里面的变量 函数 类等默认都是私有的,如果我们要在外部访问模块内的数据(函数、变量、类) 我们就需要通过export暴露模块里面的数据 然后其他地方通过import引入模块就可以使用模块内的数据 模块暴露export: // 方式一 export function a(){ ... } // 方式二 function a(){ ... } export { a } 模块导入import: import { a, a as alias } from "xxx"; a(); alias(); 模块默认导出default,一个模块只能用一次 暴露: export default a(){ } 引入(不用花括号): import a from "aaa"; a(); DB库用模块化封装// 省略了,代码比较简单,可以参考这里 ts命名空间 内部模块,主要用于组织代码,避免命名冲突, 个人理解:模块之中再分模块 定义模块、并导出不同命名空间: export namespace A{ interface Animal{ name: string; eat(): void; } export Class Dog implements Animal{ name: string; constructor(name:string){ this.name = name; } eat:void(){ log(this.name +"在空间A中吃狗粮") } } } export namespace B{ interface Animal{ name: string; eat(): void; } export Class Dog implements Animal{ name: string; constructor(name:string){ this.name = name; } eat:void(){ log(this.name +"在空间A中吃狗粮") } } } 调用: import {A, B} from "xxx"; var d = new A.Dog("小黑"); d.eat();// 小黑在空间A中吃狗粮 var dog = new B.Dog("小花"); dog.eat(); // 小花在空间B中吃狗粮 装饰器 定义: 装饰器是一种特殊类型的声明,他能够被附加到类声明,方法,属性或者参数上,可以修改类的行为。 通俗的将装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。 常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器 装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参) 装饰器是过去几年中js最大的成就之一,已经是ES7的标准特性之一 类装饰器:普通装饰器 function logClass(params:any){ console.log(params); // params就是当前类 params.prototype.apiUrl = "动态扩展的属性"; params.prototype.run=function(){ console.log("我是一个run方法"); } } @logClass // 类装饰器,普通装饰器,无法传参,默认吧class传入 class HttpClient{ constructor(){ } getData(){ } } 类装饰器:装饰器工厂 作用: 修改构造函数 扩展类属性和方法 定义: function logClass(params:string){// params是下方传过来的参数 return function(target:any){// target相当于是默认传过来的 log(target); log(params); target.prototype.apiUrl = params; } } @logClass("https://baidu.com")// 可以传参 class HttpClient{ constructor(){ } getData(){ } } var http:any = new HttpClient(); console.log(http.apiUrl);// https://baidu.com 可以修改构造函数的写法 function logClass(target:any){ log(target); return class extends target{ apiUrl:any = "我是修改后的新数据"; getData(){ this.apiUrl = this.apiUrl + "----"; log(this.apiUrl); } } } @logClass class HttpClient{ public apiUrl:string | undefined; constructor(){ this.apiUrl = "我是构造函数里面的apiUrl" } getData(){ log(this.apiUrl) } var http= new HttpClient(); http.getData(); 属性装饰器 作用: 可以给属性赋值 // 类装饰器 function logClass(params:string){// params是下方传过来的参数 return function(target:any){// target相当于是默认传过来的 log(target); log(params); target.prototype.apiUrl = params; } } // 属性装饰器 function logProperty(params:any){ // 固定写法,参数中,target为类对象,attr为参数名称 return function(target:any, attr:any){ log(target); log(attr); target[attr] = params; } } @logClass("https://baidu.com")// 可以传参 class HttpClient{ // 这个属性修饰器的作用就是给url赋值初始值 @logProperty("http://baidu.com") public url:any | undefined; constructor(){ } getData(){ } } var http:any = new HttpClient(); console.log(http.apiUrl);// https://baidu.com 方法装饰器 用的是最多的 function get(params:any){ return function(target:any, methodName:any, desc:any){ log(target); // 类属性 log(methodName); // 方法名字 getData log(desc); // 方法的描述,desc.value是方法描述 target.apiUrl = "xxx"; // 修改雷属性 target.run=function(){ log("run"); } } } class HttpClient{ public url:any | undefined; constructor(){ } @get("https://www.baidu.com") getData(){ log(this.url); } } var http:any = new HttpClient(); log(http.apiUrl); // https://www.baidu.com‘ http.run(); // log run 修改当前的方法(主要作用是装饰方法,并把方法的参数给变换类型): // 这个方法装饰其主要作用就是把参数都给格式化成string类型 function get(params:any){ return function(target:any, methodName:any, desc:any){ log(target); // 类属性 log(methodName); // 方法名字 getData log(desc.value); // 方法 // 想修改下方法,装饰一下,让他们的所有参数变成string类型,并且打印出来 var oMethod = desc.value; desc.value = function(...args:any[]){ args = args.map((value) => { return String(value); }) // 利用apply进行对象冒充,对getdata进行修改,如果没有apply就相当于是把getData方法给替换掉了 oMethod.apply(this, args);// this就是指function(...args:any[])这个函数 } } } class HttpClient{ public url:any | undefined; constructor(){ } @get("https://www.baidu.com") getData(...args:any[]){ log(args); log("我是getData方法"); } } var http:any = new HttpClient(); http.get(123,"xxx"); // 就会先打印["123", "xxx"]后打印 我是getData方法 方法参数装饰器 用的比较少,类装饰器也可以实现这个功能 运行时候当做函数被调用,可以使用参数张诗琪为累的原型增加一些元素数据,传入下列三个参数: 1对于静态成员来说是类的构造函数,对于实例成员是类的原型对象 2方法的名字 3参数在函数参数列表中的索引 function logParams(params:any){ return function(target:any, methodName:any, paramsIndex:any){ log(params);// xxxx log(target); // 原型对象 log(methodName);// getData log(paramsIndex); // 0 } } class HttpClient{ public url:any | undefined; constructor(){ } getData(@logParams("xxxx") uuid:any){ log(uuid); // iii } } var a = new HttpClient(); a.getData("iii"); 先后输出: 1. xxxx 2. 原型对象 3. getData 4. 0 5. iii 装饰器执行顺序 当存在多个装饰器时候: 执行优先级:属性装饰器>方法装饰器>方法参数装饰器>类装饰器 如果有多个同样的装饰器,它会先从后边执行 其他参考资料 call()、apply()

一沙一世界,一花一天堂。君掌盛无边,刹那成永恒。