ts的基本类型
1.布尔型:var a:boolean =false ,var a:Boolean=true
2.数值型:var a:number=2; var a :Number=2;var b:bigint 数值中的大整形 ;c=0xff 十六进制中的数值型
3.字符型 var a :string=''a" ;var b:string='b' ;var c=String=`c`
4.undefined : var a: undefined=undefined;
5.null类型 :var a:null=null ,null只能赋给对象,其他的用undefined
例子:var obj:object|null={a:1,b:2};//目的是清理对象,被垃圾回收车回收
obj =null
6.symbol类型:var a:symbol=symblo('a')
7.type类型 (或者):var a :undefined | number ; 等同于var a?:number 【只能在函数传参的时候用】,
例1:
function fn(a?:number){
}
fn();
例2
interface IC{
name:"IC";
a:number;
b:number;
}
interface ID{
name:"ID";
c:string;
d:string;
}
interface IE{
name:"IE";
e:boolean;
f:boolean;
}
type IF=IC | ID | IE;
function getsum(s:IF){
//这里s是一个对象于传入函数参数// var s:IE={name:"IE",e:true,f:true},s对象中有name属性
//通过Switch 对象的name,再进行操作
switch(s.name){
case "IC":
break;
case "ID":
break;
case "IE":
break;
}
}
//给了三个接口,每个接口都有一个name属性,并且IF是type类型,在函数调用的
7.1 obj?.a 当obj存在的时候才会调用
8.数组类型:var arr:number[]=[1,2,3] var arr:Array<number>=[1,2,3]
var arr :Array <number|string>=[1,2,'aa',3]
8.1二维数组 var arr :Array<Array<number>>=[ [1,2,3],[4,5,6] ]
9.元组类型 var arr:[string,number,boolean]=['aa',1,true]
10.枚举类型;enum COLOR{RED,YEELO,GREEN} 一般定义于类型常量
console.log(COLOR.RED) //0
enmu COLOR{RED='red',YELLOW='yellow',GREEN='green'}
console.log(COLOR.RED) //'red'
11.type类型 一般用于选项
type COLOR='red'|'yellow'|'green'
var c:COLOR:'red' //此时'red'|'yellow'|'green'这三个选项会弹出,要是写这三个以外的就会报错
12.any类型(定义了any类型就会任意类型都会使用)禁止使用
例子
var a :any="a"
a=1;a=true
13.不用返回值 void;var a :void
函数
14.function fn():number{ return 5} //返回值是数值型
15.function fn():never{ throw new Error("aa");}
回调函数中也需要写类型
例如:
fns(function(a:number,b:number):number
{
return a+b;
})
//fns是回调函数
function fns(callback:(a:number,b:number)=>number){ //第三个参数是回调函数返回的类型
callback(3,4);
}
15.declare:类型:为了声明,实现ball.d.ts,可以在写的时候会提示你所属于的类或者接口中有哪些类型,再调用的时候会出现提示
函数中的参数: 先必填参数,再默认参数,最后是缺省参数
a:number必填参数 b:number=2 默认参数 c?:number 缺省参数
...arg:Array<number>剩余参数
this:void this参数必须写在函数参数最前面
例子:
function fn(this:void,a:number,c:number=2,b?:number,...arg:Array<number>):number
{
return a;
}
对象
16.var o :object={a:1,b;2} var o :object=new Object({a:1,b:2})
o.c=3//不允许添加 o.a=10//不能修改 delect.o.a //不允许删除 console.log(o.a)不允许查看
用处:在函数中的参数,还有接口中
例如:
function fn(obj:object):void
{
console.log(obj.a)//还是不能查看
}
fn({a:1,b:2});
function fn({id,name,info} //前面相当于解构obj:{id:number,name:string,info:string}//这是对象属性值的类型):{id:number,name:string}//这里是函数返回的类型
{
return {id,name};
}
fn({id:10101,name:"ajas",info:"asdkajsd"});
可以增删改查对象的方法:
1.只可以查看与修改:
var obj:{a:number,b:number}={a:1,b:2}
obj.a=10,obj.b=20 ;console.log(obj.a)//不会报错,若是添加,删除就会报错
2.增删改查
var obj:{[key:string]:number|string|boolean}={a:1,b:2}
obj.c='a' delect obj.a obj.c=4;console.log(obj.b)
//此时都不会报错
例子:
var obj:{a:number,b?:number,c?:number}={a:1,b:2};
//可以修改a,b,c,但不能添加,也不能删除a
在类中
class B{
private bindHandler:(e:MouseEvent)=>void;
constructor(){
this.bindHandler=(e:MouseEvent)=>this.clickhandler(e);
document.addEventListener("click",this.bindHandler);
}
private clickhandler(this:B,e:MouseEvent):void //这里的this指向的B,所以是B类型,e:是MouseEvent类型
{
this.play();
document.removeEventListener("click",this.bindHandler);
}
private play():void
{
}
}
断言 as
例一:
clickHandler(e:MouseEvent):void
{
if((e.target as HTMLElement).nodeName!=="DIV") return;
}
例子2:
export default class Box{
elem:HTMLDivElement;
constructor(){
this.elem=document.createElement("div");
this.elem.addEventListener("click",e=>this.clickHandler(e));
}
appenTo(parent:string|HTMLElement){
if(typeof parent==="string") parent=document.querySelector(parent) as HTMLElement;
//因为不知道parent是什么类型就断言他是HTMLElement类型
if(parent) parent.appendChild(this.elem);
TS中类的方法
1.private:私有的,只能自己使用,继承自己的跟实例化的不可以用用private标记的方法
2.protected:受保护的,自己跟继承自己的子类都可以调用的方法,但是在实例化对象中不可以 使 用
3.public:共有的,都可以使用
4.readonly:只读属性,只能写在构造函数的参数中
例如:
export default class Ball extends Box{
constructor(readonly ab:number){//ab 参数只能读取,不能修改
super(ab);}}
interface IObj{
readonly a:number;
b:number;
}
var o:IObj={a:1,b:2};
console.log(o.a);
o.a=10;//会报错,因为a是只读属性
abstract 抽象类
export default abstract class Base{
constructor(){
}
public abstract update():void;
protected run():void
{
console.log("play");
}
}
在抽象类中的抽象对象,可以在继承的子类中,进行对父类的抽象方法进行修改,添加属性,在子类中不能用super()调用
import Base from "./Base";
export default class Rect extends Base{
constructor(){
super();
}
protected run(): void {
}
public update(): void {
// 因为update继承时,Base类中对于update方法修饰为抽象方法,因此不可使用super.update()实现父类方法
console.log("bbb");
}
}
接口 interface
实现对象的接口
1.1只实现查改
interface IObj={
a:number,
b:number
}
var obj:IObj={a:1,b:2}//只能进行查与改
2.实现增删改查
interface IObj={
[key:string]:number|string|boolean
}
var obj:IObj={a:1,b:2}//这时可以增删改查
函数中的接口
例一
var fn:(a:number,b:number)=>number=function(a:number,b:number):number
{
return a+b;
}
fn(4,6);
function fn(a:number,b:number):number
{
return a+b;
}
fn(4,6);
两者的区别是相当于1. function fn(){}与 var fn=function(){} 2.第一个可以传接口
1.函数接口写法
interface IFn={
(a:number,b:number)
return number
}
var fn:IFn=function(a:number,b:number):number{
return a+b
}
可索引类型
interface IArray{
[index:number]:number
}
var arr :IArray=[1,2,3,4]
console.log(arr[0])//可以通过索引拿到arr的值,
//注意这里arr不是数组类型,不能用数组的方法
var o:IArray={};
o[1]=10;
o[0]=20;
console.log(o);
类接口
注意:类通过implements去实现接口,类中的属性数量必须与接口的数量对应,不能在接口中定义了,但是在类中不写,但是可以添加自己的属性,属性也要对应,类中的属性必须是public,接口和抽象类都不允许实例化,原因是其内部只有属性名和方法名,并没有实际内容
接口与继承的区别
1.类继承的过程中只能实现一个父类的继承,单链性,继承会将所有实现方法及实现内容全部继承
2.同一个类可以实现多个接口。接口中不实现属性值和具体方法内容
例子
interface IA{
a:number; //不能有私有
play():void
}
interface IB{
b:string;
run():void
}
interface IC{
c:number;
jump():void
}
class A implements IA ,IB{ //通过implements实现接口
public a:number=2 //接口里有多少属性就要全部写出,并且全部必须是public
public:play():void{
console.log('aaa')
public b:string='3'
public:run():void{
console.log('bbb')
}
}
class B implements IA,IC{
public b:string='w' //这个接口中没有可以自己添加
public a:number=20;
public c:number=30;
public play(): void {
console.log("bbb")
}
public jump(): void {
console.log("ccc")
}
}
var a:A=new A() 注意这里实例化的a是A类型
var b:B=new B()
var arr:Array<IA>=[];
//把实例化对象存到数组中,条件是必须实现了IA方法,
//要是没有实现IA将存不进去,会吧实例化的所有属性都存进去
arr.push(a);
arr.push(b);
在抽象类中不能实例化案例
interface HTMLDivElement_1{
// style:CSSStyleDeclaration,
width:number,
onclick:(e:MouseEvent)=>void,
}
function createElement(type:string):HTMLDivElement_1
{
var elem:HTMLDivElement_1={ //elem相当于一个对象
width:1,
onclick:function(e:MouseEvent){
}
}
return elem;
}
var o:HTMLDivElement_1=createElement("div");
var o=new HTMLDivElement_1()//不可以,这样会报错
构造函数
例子
interface IA{
new (a:number,b:number):IUpdate //构造函数的接口 ,所以B类就是IA接口这个类型
}
interface IUpdate{ //构造函数中有什么方法,可以进行分类
update():void
class B implements IUpdate{
constructor(a:number,b:number){
}
public update(): void {
console.log("aa")
}
}
class C implements IUpdate{
constructor(a:number,b:number){
}
public update(): void {
console.log("bb")
}
}
function createB(className:IA):IUpdate
{
var b:IUpdate=new className(4,5);
//这里b就是Iupdata类型,因为再new构造函数的接口返回的就是IUpdate类型
console.log(b);
return b;
}
createB(B); //这里相当于用一个函数,用classB来实例化b
createB(C); //这里相当于用一个函数,用classC来实例化b
用泛型解决
function fn<T>(className:{new ():T}){
console.log(className);
}
fn<Ball>(Ball)
fn<Box>(Box)
接口继承类
class B{
public a:number=1;
// private b:number=2; //接口继承类,不能有私有的存在
public play():void
{
}
}
interface IA extends B{
c:number;
}
class C implements IA{
public a: number=1;
public c: number=2;
b:number=3;
public play(): void {
}
}
泛型
通过泛型实现接口
1.类中的泛型接口
export interface IA <T>{ //这里T就是泛型
a:T
}
在用的时候
import IA FROM './'
class B implements IA<number>{
public a: number =1
}
1.2反类型,在实例化传参的时候用
//定义一个泛类型,注意static静态属性不能给泛类型,因为静态只能自己使用,是用RECT.(点)的,不是new的
class RECT <T>{
public a:<T>
constructor(_a){
this.a =_a
}
}
//实例化的时候
var rect RECT:<number>=new RECT(1)
1.3 泛型继承类 :注意不能实现接口,因为接口不可以实例化
fn<T extends Box>(o:T){}
2.对象中的泛型接口
interface IB<T,U>{
a:T,
b:u
}
在使用的时候
var o:IB<number,string>={a:1,b:"a"};
var o1:IB<number,number>={a:1,b:2};
var o2:IB<string,boolean>={a:"a",b:true};
var o3:IB<Array<string>,Array<number>>={a:["a","b"],b:[1,2,3]};
//这时候属性值的类型就可以直接自己定义什么类型,不用直接写死
//之前的接口写法
interface IObj={
a:number,
b:number
} //这样在使用接口的时候只能是number类型的
var obj:IObj={a:1,b:2}//只能进行查与改
3.函数中的参数的泛型
function fn<T>(a:T){
}
var b:Box=new Box();
fn<Box>(b); //此时参数b就是Box类型
var c:Ball=new Ball();
fn<Ball>(c); //此时参数b就是Ball类型
混合交叉类型
理解:就是你不确定他返回什么类型就用交叉类型&
例子:
function minxi<T,U>(a:T,b:U):T & U //因为返回的是两种类型
{
var result=<T & U>{};
for(var key in a){
(<any>result)[key]=a[key]; //使用any因为刚开始你不知道result是什么类型
}
for(var key1 in b){
(<any>result)[key1]=b[key1];//使用any因为刚开始你不知道result是什么类型
}
return result;
}
interface IC{
name:"IC";
a:number;
b:number;
}
interface ID{
name:"ID";
c:string;
d:string;
}
//调用 var c:IC & ID=minxi<IC,ID>(o,o1);
装饰器
function f(){
return function(target:C,key:string){ //这里的target是类,key是类里的方法
// console.log(target,key);
console.log(key);
target.method(); //不需要调用就会执行C类里面的method()方法
}
}
class C{
@f() //z再实例化C类的时候会先执行f这个函数,注意@f不能写在函数内部
play(){
}
@f()
method() {
console.log("ab")
}
}
var c:C=new C();
// c.method();
通过单线程与中介实现解耦
理解:解耦的目的是拆分两个关联的类的代码,通过中介来把这两个代码关联起来,当你删除其中一个另一个不会报错,而这个中介就是通过单线程实现
一.实现单线程方法一
问题:在触发Box 中的play方法的时候就会执行ball中的run方法
class Box{
private a:number=1;
constructor(){
}
public play(){
this.a++;
ViewModel.instance.data=this.a;
}
}
---------------------------------------------------
class Ball{
constructor(){
ViewModel.instance.addEventListener("change",e=>this.run(e));
}
private run(e:Emitter){
console.log(e.a);
}
}
------------------------------------------------
import Emitter from "./Emitter";
import EmitterTarget from "./EmitterTarget";
export default class ViewModel extends EmitterTarget{
private static _instance:ViewModel;
public b:number=1;
private _data?:number;
private constructor(){
super();
}
public static get instance():ViewModel{
return ViewModel._instance || (ViewModel._instance=new ViewModel());
}
public set data(value:number){
this._data=value;
var evt=new Emitter("change");
evt.a=value;
this.dispatchEvent(evt);
}
public get data():number
{
if(!this._data) this._data=0;
return this._data;
}
}
//此时创建了一个单例ViewModel.instance,因为ViewModel.instance就是相当于ViewModel的实例化对象,所以就可以调用里面的public方法,再任何时候,只要引入了这个类,就可以使用ViewModel.instance.data,
//所以在BOX类中的play()方法中改变ViewModel.instance.data的值,然而在ViewModel.instance.data中的set方法在赋值的时候就会触发,所以就可以在set方法中抛发事件,并且在ball类中监听事件,事件函数是run,所以在在执行BOX类中的play方法的时候就会执行Ball中的run方法
思路:1.此时创建了一个单例ViewModel.instance,因为ViewModel.instance就是相当于ViewModel 的实例化对象,所以就可以调用里面的public方法,再任何时候,只要引入了这个类,就 可以使用ViewModel.instance.data,
2.所以在BOX类中的play()方法中改变ViewModel.instance.data的值,然而在ViewModel.instance.data中的set方法在赋值的时候就会触发,所以就可以在set方法中抛发事件,并且在ball类中监听事件,事件函数是run,所以在在执行BOX类中的play方法的时候就会执行Ball中的run方法
二.单例实现二
思路:此时 MainModel.getInstance()就相当于实例化的MainModel对象,因为a是公共的所以,在认可地方都可以调用MainModel.getInstance().a,因此可以通过a的改变状态,来抛发事件,别的类中监听事件,与例子一的思路一样,就是实现单例的方法不一样,这个也需要设置get,set
export default class MainModel{
private static _instance:MainModel;
public a:number=1;
private constructor() {
}
public static getInstance():MainModel
{
if(!MainModel._instance)MainModel._instance=new MainModel();
return MainModel._instance;
}
}
此时 MainModel.getInstance()就相当于实例化的MainModel对象,因为a是公共的所以,在认可地方都可以调用MainModel.getInstance().a,因此可以通过a的改变状态,来抛发事件,别的类中监听事件,与例子一的思路一样,就是实现单例的方法不一样,这个也需要设置get,set