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<string> = ['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<T>(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<T>{
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;
}
}
// 调用,实例化时候要先声明参数类型<bumber
var m1 = new MinClass<number>();
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{
<T>(value:T):T;
}
var getData:ConfigFn=function<T>(value:T):T{
return value;
}
getData<string>("张三");
泛型接口写法2:
interface Config<T>{
(value:T):T;
}
function getData<T>(value:T):T{
return value;
}
var myGetData:ConfigFn<string>=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<T>{
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>();// 这一步很关键,要定义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<Article>();// 指定类型
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<T> implements DBI<T>{
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>();// 声明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()
Comments | NOTHING