本篇文章给大家带来了关于TypeScript入门的相关知识,希望对大家有帮助。
前言
ts是什么?ts其实是TypeScript的一个简称,就跟JavaScript简称为js一样,官方给的解释是,ts它是js的超集,其实js的代码本质上都是ts编译的,例如写一个js代码 document.write() ,上面的提示就是ts,如图:
系统里面写的是ts代码,所以ts是js的超集,是给js添加拓展功能的语言,这样的语言曾经还有一个叫as,但是这个语言已经没了,它其实就是flash语言。但我么开发需要完善js的话,必须得有这样的一个东西,所以微软拿着这个as的语法开发了ts这个语言,也就是说as和ts的语法基本是一样的,只不过ts是微软开发的,然后推广的不错,现在随着我们编程越来越全民化,ts使用的也就越来越多,包括在找工作中,如果你的薪资是在12k以上的话,基本上都会问你会不会ts,现在的编程很多情况下都是用到ts,因为它能给js添加拓展功能,
比如:可以进行各种各样的模块化开发,像之前提到的AMD,CMD开发,CommonJS,es6的模块化规范。假设现在在客户端开发,有没有一种开发场景,既可以用AMD,也可以用CMD,CommonJS,es6的,
答案是没有,因为CommonJS是只能在nodejs中使用,es6的模块化开发只能在服务器中用,它们都是有各自限制的。
但是ts中,想用什么都可以,它是支持全场景的。ts它更像后端,他的语法跟java最相似,这样有什么好处?大家知道js是弱类型语言,比如一个变量先定义一个数字型,然后再重新赋值一个字符串型的,这样的话这个变量的类型就改变了,这种弱类型开发是对项目有一定的安全隐患的,
比如就用这个变量去做其他事情,它有可能变成其他类型的数据,所以对开发来说是有安全隐患的,所以必须得有ts来规范js开发,消除这个安全隐患,这就是为什么ts像java、#c这些后端语言,这些强类型语言定义变量的时候,需要先声明这些变量的类型是什么,一旦定义这个变量的类型后,后期是不允许修改类型的,有了这样的规范约束后,开发就更加安全了。
现在ts使用的场景是非常广泛:js的提示(编辑器内置了规范语法的ts),主流框架vue,react在底层写框架的时候用的也是ts。
说这么多,下面直接介绍ts,也就是TypeScript。
TypeScript
简介
ts是一个给js添加特性的扩展语言。
- TypeScript是由微软开发的一款开源的编程语言。
- TypeScript是JavaScript的超集,遵循最新的es6、es5规范。TypeScript扩展了JavaScript的语法。
- TypeScript更像后端java、C#这样的面向对象语言,可以让js开发大型企业项目。
- 谷歌也在大力支持TypeScript的推广,谷歌的angular2.x+就是基于TypeScript语法。
- 最新的vue、React也可以集成TypeScript。
- nodeJs矿建Nestjs、midway中用的就是TypeScript语法。
能给js增加的功能有
- 类型批注和编译时类型检查
- 类型推断
- 类型擦除
- 接口
- 枚举
- Mixin
- 泛型编程
- 名字空间
- 元组
- Await
- 类
- 模块
- lambda 函数的箭头语法
- 可选参数以及默认参数
js 是一个弱类型的语言,但ts是强类型的,语法很相像,但ts算是扩展的js的语法,ts通过类型注解,提供编译js时的静态类型检查。
编译
ts无法直接运行,所以只能编译成js运行。类似sass用来编译css,不能直接运行。
编译工具 – typescript
在全局任意位置下,先检测是否安装过ts
tsc --version
npm install -g typescript
检测是否安装成功:
tsc -v
ts文件的后缀是ts,编译命令:
tsc 被编译的文件
会在被编译文件的同级目录下生成一个同名的js文件。
生成自定义名称路径的文件:
tsc 被编译的文件 --outFile 编译后的文件路径
初始化命令:
tsc --init
执行初始化命令后,会生成一个tsconfig.json文件,如下:
其中常用配置项说明:
代表ts要转换后js版本
"target": "es5"
如果ts是以模块形式书写的,使用什么样的模块化规范,默认是commonJS
"module": "amd"
配置输出目录,可自己设置
"outDir": "./"
上面的配置项配置好以后,执行监视命令会自动编译:
tsc -w
使用amd规范的时候,需要将require.js文件拷贝到项目根目录下,且需要一个出口文件:
<script src="require.js" data-main="main.js"></script>
将ts编译成js,自动生成目标js文件
tsc 目标js文件
ts基础类型
在定义变量的时候需要指定类型,且定义好类型以后就不能改变他的类型了 – 强类型。
数值型
let decLiteral: number = 6; // 十进制 let hexLiteral: number = 0xf00d; // 16进制 let binaryLiteral: number = 0b1010; // 二进制 let octalLiteral: number = 0o744; // 8进制 let num: Number = 5; // 此时大写Number类型可以赋值为数值对象类型 num = new Number(10);
布尔值
let isDone: boolean = false;let bool: Boolean = true; // 首字母大写的类型可以赋值为对象类型
boolean这种类型只能赋值两个值:true/false
var bool: boolean = true var bool: boolean = new Boolean(true)
Boolean这种类型除了上面的字面量方式的两个值,还可以使用构造函数方式
var bool: Boolean = false;var bool: Boolean = new Boolean(true)
字符串
var str: string = 'asdfasdf';var str1: String = new String('43563456')
数组
ts写的数组,其中的数据必须是同一个类型,但不指定长度
数组中所有数据的值类型必须是数字
var arr: number[] = [1,2,3];var arr: Array<string> = ['a','b'];
声明二维数组
var arr: number[]var brr: number[][] = [ [1,2,3], [4,5,6]];
元组 Tuple
ts中的元组表示不同类型数据组成的集合,通常会固定长度,同样可以使用下标访问元素和给元素赋值
元组中就可以放不同类型的数据
元组在定义的时候就将长度固定了
var x: [string,number] = ['a',2];console.log(x);console.log(x[0]);
错误
x[2] = 20
不能加长度
let x: [string, number];x = ['hello', 10];x[2] = 'world'; // 不能加长度
可以给元素push一个值,这个值必须是string或number的类型,其他类型不允许
x.push('aaaa')
错误
x.push(true) // 错误
当给元组中并不存在的下标进行赋值的时候,会使用联合类型:
x[3] = 'world'; // OK, 字符串可以赋值给(string | number)类型x 6] = true;// Error, 布尔不是(string | number)类型
枚举
ts中的枚举相当于在定义变量类型,这个类型有固定的取值范围,默认值从0开始,向后递增,使用指定的键来换换取到值,如果一个变量使用了这个类型,那他的值就必须从这个类型中选择,不能随便赋值:
枚举 – 必须使用指定的集合中的值
枚举类型,其实是给数字起了一些名字,让我们可以通过这个名字获取到对应的数字
默认情况,第一个名字对应的值是0,依次向后递增
enum Color {Red,Green,Blue};var c: Color = Color.Blue; // 2
如果给其中某一个名字赋值了,他后面的名字对应的值,是根据这里的值向后递增
enum Color {Red,Green = 5,Blue};var c: Color = Color.Blue; // 6
每个值可以指定
enum Color {Red=3,Green = 4,Blue=2}; var c: Color = Color.Blue; // 2
可以指定非数字的值
enum Color {Red='男',Green = '女',Blue='不男不女'};var c: Color = Color.Blue; // 不男不女
通过对应值的数字获取到对应的名字 – 名字是字符串类型
enum Color {Red,Green=5,Blue};var c: string = Color[6] // Blue
如果我们指定了其中的值是非数字型的,就不能使用这个骚操作了
enum Color {Red='red',Green = 'green',Blue='blue'};var c: string = Color['red']; // 这个地方的值必须是数字才行
Any
Any类型,表示弱类型,也就是当我们定义一个变量的时候,不能确定变量值的类型的时候,这个类型我们又爱又恨
使用:
var a:any = 20var b:any = 'asdfasdf'var c:any = [1,2,3]
注意:本来我们使用ts编写代码,为的是限制类型,减少安全隐患,但是如果使用了any类型,就跟直接写js一样了,失去了意义,所以若非迫不得已,尽量不要使用。
Void
这种类型,一般用于函数执行后,不使用return返回结果的时候,就指定返回结果是void
声明变量的时候不使用它 – 当函数没有返回值的时候,返回类型指定为void
function fn(a:number,b:number):void{ console.log(a*b);}
Undefined
这种类型主要用于参数和返回值,用于变量毫无意义,因为定义一个变量指定为undefined类型时,以后也就只能是undefined类型了,函数中的用法:
function fn(num:number|undefined):number|undefined{ return num;}
undefined – 未定义类型
var a:undefined = undefined
定义变量不用undefined,因为定义了没有用
通常会用在函数的参数里面
希望fn函数的参数a是可选项
function fn(a:number|undefined):void{ console.log(a);}fn(undefined)
函数可选项
参数名后面,类型的冒号之前加 ? 表示这个参数是可选项
undefined通常用在函数返回值,如果返回的是undefined就需要在返回值的地方指定undefined类型
function fn(a?:number):number|undefined{ return a;}fn()
Null
null类型 – 空 – 这个数据要被销毁啦
通常在定义复杂数据类型,且在后期需要给赋值为null的时候使用
var a:number|null = 10;
使用变量a计算 – 完成
让内存回收这个变量
a = null
Never
never类型表示永远不存在的值的类型,例如,一个函数中抛出的错误,函数中有死循环永远不可能返回 …
function fn():never{ throw new Error('错误')}function fn():never{ return fn()}fn()
Object
对象类型:
var obj: object = { name:"张三"}
错误写法 - 对象默认不允许添加键值对
obj.name = '张三';
类型断言
如果在一段代码执行后的类型种类的可能性比较多,就需要假设这是一种什么类型 – 这种操作就叫做断言。
如果一个表达式的结果有可能是多种类型,最终需要肯定其中一种
var abcd: any = [1, 2, 3, 4, 5];
断言abcd变量是一个数组
(abcd as [string,number]).push(6)(abcd as string) += 'ddd'
函数声明
在ts中,函数定义比起js中,多了参数类型和返回值的类型定义:
函数的定义,参数的类型声明,返回值的类型声明
function fn(a:number,b:number):number{ // console.log(a+b); return a+b}var res = fn(1,2)
参数默认值
function fn(a:number,b:number=3):number{ return a+b}var res = fn(1)
但是在表示参数为可选项的时候,写法稍有不同:
参数可选项 – ?表示可有可无
function fn(a:number,b?:number):number{ if(!b){ return a+5 } return a+b}// var res = fn(1)var res = fn(1,3)
带有默认值的参数,必须放在所有参数的最后面
可选项参数,必须放在所有参数的最后面
展开运算符和合并运算符同样可以使用在ts中,但是要注意运算符后面的变量的类型设置。
计算不定实参的和
function sum(...arr:Array<number>){ var sum = 0; for(var i=0;i<arr.length;i++){ sum += arr[i] } return sum;}var res = sum(1,2,3);console.log(res);
函数重载:通过 为同一个函数提供多个函数类型定义 来实现多种功能的目的。例:
function outputName(name:string):string{ return "我叫"+name}var s1 = outputName('张三')console.log(s1);function outputAge(age:number):string{ return "我今年"+age+"岁了"}var s2 = outputAge(12)console.log(s2);
有多个函数结构非常类似,可以声明一个函数的结构,让函数遵循这个结构
function output(name:string):string; 定义了一个函数结构 - 名字叫output function output(age:number):string; function output(name:any):any { return "我今年"+name+"岁了"; } var res = output(12) console.log(res); var res1 = output('李四') console.log(res1); var res2 = output(true) 报错的,因为函数的结构要求是参数string或number console.log(res2);
ts中的类
定义
定义方式跟es6的定义方式类似
class 类名{ constructor(){ }}
class Person{ // 静态属性 - 用类名直接调用的属性 static weight:number; // 类的属性要定义在这里 name:string; // 表示类中有一个属性叫name // 在ts类中,属性和方法前面可以加一个修饰符: /* public - 公开的 - 在哪里都能用 protected - 受保护的 private - 私有的 */ public age:number; // public可以省略的 protected sex:string; // 受保护的只能在类里面用,类的外面不能用的 private height:number; // 私有的只能在类里面使用,类外面不能用 constructor(name:string,age:number,sex:string,height:number,weight:number){ // 给属性赋值的时候,必须在这个类中是本来就有这个属性才行 this.name = name this.age = age this.sex = sex this.height = height; // this.weight = weight; Person.weight = weight; this.init() } private init(){ // console.log(this.age); // console.log(this.sex); console.log(this.height); console.log("这是初始化方法"); } static fly(){ console.log("飞的更高"); } } var p = new Person('张三',12,'男',120,150) console.log(p); // console.log(p.age); // console.log(p.sex); // 受保护的属性不能类的外面使用 // console.log(p.height) // 私有属性不能类的外面使用 // p.init() console.log(Person.weight); Person.fly()
继承
ts中类的继承和es6的继承是一样,使用extends关键字,然后在构造函数中调用super函数相当于在调用父类的构造函数。
如果子类和父类有同名的方法,在子类调用这个方法的时候先在子类里面找,如果子类没有再到父类里面找。
class Person{ // 静态属性 - 用类名直接调用的属性 static weight:number; // 类的属性要定义在这里 name:string; // 表示类中有一个属性叫name // 在ts类中,属性和方法前面可以加一个修饰符: /* public - 公开的 - 在哪里都能用 protected - 受保护的 private - 私有的 */ public age:number; // public可以省略的 protected sex:string; // 受保护的只能在类里面用,类的外面不能用的 private height:number; // 私有的只能在类里面使用,类外面不能用 constructor(name:string,age:number,sex:string,height:number,weight:number){ // 给属性赋值的时候,必须在这个类中是本来就有这个属性才行 this.name = name this.age = age this.sex = sex this.height = height; // this.weight = weight; Person.weight = weight; this.init() } private init(){ // console.log(this.age); // console.log(this.sex); console.log(this.height); console.log("这是初始化方法"); } static fly(){ console.log("飞的更高"); } } var p = new Person('张三',12,'男',120,150) console.log(p); // console.log(p.age); // console.log(p.sex); // 受保护的属性不能类的外面使用 // console.log(p.height) // 私有属性不能类的外面使用 // p.init() console.log(Person.weight); Person.fly()
类的修饰符
在类中定义属性的时候,提供了3个修饰符:
- public:公有的 – 在类里面、子类中、类的外面都可以访问
- protected:受保护的 – 在类里面、子类中可以访问,在类外面不能访问
- private:私有的 – 在类里面可以访问,在子类和类的外面都不能访问
静态属性和方法
es5中静态方法使用:
// 模拟jquery的封装function $(element){ return new Ele(element);}$.get = function(obj){ }function Ele(element){ this.element = document.getElementById("#"+element);}Ele.prototype.css = function(attr,value){ if(value){ this.element.style[attr] = value; }else{ return window.getComputedStyle(this.element)[attr]; }}$("#box").css("color","red");
在ts中定义静态的属性和方法使用static关键字。在静态方法中无法访问到普通的属性,只能访问到静态的属性。
class Person{ public name:string = "张三"; static age:number = 20; constuctor(){ } static print1(){ console.log(this.name); // 访问不到 } static print2(){ console.log(Person.name); // 可以访问到 }}Person.print1();Person.print2();
属性可以设置为只读
多态
面向对象的三大特点:封装、继承、多态
含义:多态就是说,父类定义一个方法不去实现,让继承它的子类去实现,这样每个子类都会有不同表现。多态其实也是继承的一种表现。
// 父类 - 动物类class Animal{ public tui:string = "有腿"; public eat(){ console.log("喜欢吃"); } public sport(){ console.log("能走"); } public tuiNum(){ console.log("有多条腿"); }}// 子类 - 人类class Person extends Animal{ sport(){ console.log("直立走"); } tuiNum(){ console.log("两条腿"); }}var p = new Person();console.log(p.tui); // 有腿p.eat(); // 喜欢吃p.sport(); // 直立走p.tuiNum() // 两条腿// 子类 - 鸟类class Bird extends Animal{ sport(){ console.log("很少走,通常都在飞"); } tuiNum(){ console.log("两条腿"); }}var b = new Bird();console.log(b.tui);b.eat();b.sport(); // 很少走,通常都在飞b.tuiNum(); // 两条腿// 子类 - 狗类class Dog extends Animal{ sport(){ console.log("通常都在跑,很少走"); } tuiNum(){ console.log("4条腿"); }}var d = new Dog();console.log(d.tui);d.eat();d.sport(); // 通常都在跑,很少走d.tuiNum(); // 4条腿
效果:
多态的表现 |
---|
**小总结:**多态就是多个子类继承自同一个父类,但是每个子类将继承下来的属性或方法做了改善,最终每个子类表现出来的结果是不一样的。
多态其实源于继承,也是方法的重载。
抽象类
在实际工作中,项目负责人通常会写一些标准(类似于大纲),然后将标准交给具体实现的攻城狮,由攻城狮将这个标准进行具体化开发。
ts中的抽象类就是为制作标准的。抽象类不能被实例化,只能被派生类继承并实现。
定义抽象类使用abstract关键字来修饰类。
abstract class Animate{ public name:string; constructor(name:string){ this.name = name; }}var ani = new Animate("动物"); // 报错class Dog extends Animate{ constructor(name:string){ super(name); }}var d = new Dog("小黄");
这种结构没有意义。跟普通的继承是一样的,并不能体现出标准的特殊。在抽象类中通常会有抽象方法 – 使用abstract修饰的方法。
抽象方法必须在抽象类中,且只需要定义方法结构,不要具体的实现。但是派生类中必须实现(完善)抽象方法。
abstract class Animate{ public name:string; constructor(name:string){ this.name = name; } abstract eat():void; // 抽象方法}class Dog extends Animate{ constructor(name:string){ super(name); } eat(){ // 实现了抽象方法 consolelog("小狗吃粮食"); }}
这个结构就能体现出标准的特殊:规定子类必须包含eat方法。
抽象方法只能放在抽象类中。
抽象类存在的意义就是被其他类继承,是其他类的基类。
接口
抽象类只能给方法定义标准,对于属性限制不够,所以ts设计了接口语法,它定义了属性和方法的规范,起到限制和规范的作用。接口并不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。
ts的接口跟别的主流服务器端语言的接口一样,同时还增加了更加灵活的接口类型,包括属性、函数、可索引和类等。
简单来说,接口也是在定义标准,只不过更加灵活和全面。
属性接口
属性接口专门为了约束属性而设计。
语法:
interface 接口名称{ 变量:类型; 变量:类型;}
使用方式:
function printInfo(info:接口名称){ console.log(info.属性名); // 属性名必须是接口定义过的,否则报错}
例:
// 以前对于数据的限制// 1.定义方法function printInfo():void{ console.log(123);}printInfo();// 2.传入参数function printInfo(info:number):void{ console.log(info);}printInfo(123);// 3.传入的参数对json限制function printInfo(info:{name:string}):void{ console.log(info);}printInfo({ name:"张三"});printInfo({ // 错误示例 - 键在函数中不存在 sex:"男"});// 这种函数只能对一个键做限制,要做批量限制很麻烦,要写很多函数// 使用接口限制// 1.定义接口interface info { name:string; sex:string;}// 2.定义函数使用接口类型function printInfo(data:info){ console.log(data.name); console.log(data.sex); // console.log(data.age); // 错误 - info中没有age键}// 3.使用printInfo({ name:"张三", sex:"男", age:20 // 错误 - info中没有age键});var obj = { name:"张三", sex:"男", age:20}printInfo(obj); // 正确// 接口可以批量对变量进行约束 - 参数的顺序可以不一样,但是不能少参数
定义接口中的可选参数:
interface info{ name:string; sex?:string; [propName:string]:any // 这里表示其他属性也可以加,也可以不加 } // 这个接口表示name是必须,sex是可选项 // 在属性前面可以使用readonly来修饰属性不可以修改
例:
// 对jquery的ajax的封装$.ajax({ type: "GET", url: "test.json", data: {username:$("#username").val(), content:$("#content").val()}, dataType: "json" });// 定义接口interface Config{ type?:string; url:string; data?:string; dataType?:string;}// 使用接口类型封装ajaxfunction sendAjax(config:Config){ var xhr = new XMLHttpRequest(); }// 调用sendAjax({ url:"", });
函数接口
函数接口是专门为了约束函数的参数和返回而设计。
语法:
interface 接口名称{ (参数:类型):返回值类型}
例:
// 加密的接口interface encrypt{ (key:string,value:string):string;}var md5:encrypt=function(key:string,value:string):string{ //模拟操作 return key+value;}console.log(md5('name','zhangsan'));
可索引接口
可索引接口是对数组、对象等有索引的数据做约束。
对数组的约束接口:
interface userArr { [index:number]:string; // 表示索引必须为数字,数据必须是字符串}
使用:
var arr:userArr = ["张三","李四"]
对对象的约束:
interface userObj{ [index:string]:string;}
使用:
var obj:userObj = {name:"张三"}
泛型
泛型:软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 泛型不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
通俗理解:泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持(类型校验)
泛型方法:
function getInfo<T>(value:T):T{ return value;}getInfo<number>(123);getInfo<string>('aaa');
例:
// 约束函数传入number返回number,传入string返回string// 以前:function fn(a:number):number;function fn(a:string):string;function fn(a:any):any{ return a;}// 使用泛型function fn<T>(a:T):T{ return a;}fn<number>(234);fn<string>("abc");
命名空间
多人合作开发项目的时候,避免不了会有函数、变量、类等数据的命名冲突。但是ts不允许出现同名的类、函数、变量(const定义的),这时候就需要使用命名空间来解决这个问题。
命名空间其实就单独做一个作用域,在当前命名空间下的数据跟别的命名空间下的数据重名也不会产生冲突。
命名空间语法:
namespace A{ // namespace 命名空间名称{} class Animal{ constructor(){ console.log("A命名空间下的动物类"); } }}// 使用动物类的时候A.Animal()
例:
命名空间
工作中一个项目都是协作开发,每个人负责一个文件,避免不了函数、变量、类、接口会重名。
但是在ts文件,不允许类、函数、let、const 重名
命名空间就是解决这个问题的。
命名空间:就是开辟自己的作用域
// 定义命名空间:namespace 空间名字{} namespace A{ // 相当于定义了一个单独的作用域叫A export class Animal{ name:string; constructor(name:string){ this.name = name; } } } namespace B{ export class Animal{ age:number; constructor(age:number){ this.age = age; } } } // 在这里实例化Animal类 // 使用命名空间:命名空间.数据 var a = new A.Animal("张三"); console.log(a.name); // 张三 var b = new B.Animal(20); console.log(b.age); // 20
从结果中可以看到,同名的类处在不同的命名空间下是不会冲突的。
此时,A命名空间就是一个单独的模块,进行模块化开发的时候,需要将命名空间导出,也就是说一个命名空间就是一个模块,而不是一个单独的文件了。
例:
// 导出export namespace A{ // 将命名空间导出 // 相当于定义了一个单独的作用域叫A export class Animal{ name:string; constructor(name:string){ this.name = name; } }}
导入的时候,导入当前文件,接收命名空间,通过命名空间来调用数据:
// 导入import { A } from "./demo"; // 导入的是一个命名空间var a = new A.Animal("张三"); // 实例化那个Animal
ts事件封装
为什么要封装?
因为在es5和es6中允许dom元素继承EventTarget,但是在ts中不允许继承。
所以需要重构EventTarget。
使用dispathEvent来抛发事件,需要使用Event。所以重构Event。
本质:观察者模式。
ts开发的规则
开发的时候通常都是在使用模块化开发
怎么进行模块化开发?一个模块一个类,通常类的首字母会大写,文件名称和类的名称保持一致。
封装
准备工作:
将ts配置文件中的module选项改成amd。
"module": "amd",
更改输入输出目录:
"outDir": "./js", "rootDir": "./ts",
新建html,导入amd所使用的require.js。
配置导入文件以及异步推迟加载。
<script src="./require.js" data-main="./dist/Main"></script>
新建MyEvent.ts文件:
import MyTarget from "./MyTarget";export default class MyEvent{ public type:string; [key:string]:any; public myTarget:MyTarget|null = null; public target:MyTarget|null = null; public data:any; constructor(type:string,data:any = null){ this.type = type; }}
新建MyTarget.ts
import IListener from "./IListener";import MyEvent from "./MyEvent";export default class MyTarget{ public listenerList:IListener = {}; constructor(){ } addEventListener(type:string,listener:Function):void{ if(!this.listenerList[type]) this.listenerList[type] = []; this.listenerList[type].push(listener); } removeEventListener(type:string,listener:Function):void{ if(!this.listenerList[type]) return; var index:number = this.listenerList[type].indexOf(listener); if(index>-1){ this.listenerList[type].splice(index,1); } } dispathEvent(evt:MyEvent):boolean{ var list:Function[] = this.listenerList[evt.type]; if(!list) return false; evt.myTarget = this; evt.target = this; for(var i:number=0;i<list.length;i++){ list[i].call(this,evt); } return true; }}
新建IListener.ts文件
export default interface IListener{ [key:string]:Array<Function>;}
在Main.ts中使用:
import MyEvent from "./MyEvent";import MyTarget from "./MyTarget";var doc = new MyTarget();var ev = new MyEvent("zi");ev.a = 10;// var ev1 = new MyEvent("ziji");// ev1.b = 20;// console.log(doc);doc.addEventListener("zi",handler1);doc.addEventListener("zi",handler2);doc.addEventListener("ziji",handler2);doc.dispathEvent(ev);doc.dispathEvent(ev);// doc.dispathEvent(ev1);function handler1(e:MyEvent){ console.log(e + "----------------");}function handler2(e:MyEvent){ console.log(e + "||||||||||||||||||||"); (e.target as MyTarget).removeEventListener("zi",handler2);}
效果:
第二次抛发的事件被删除 |
---|