この記事では、TypeScript の使用を開始するための関連知識を提供します。お役に立てば幸いです。
tsとは何ですか? ts は実際には TypeScript の略語であり、JavaScript が js と略されるのと同じです。公式の説明では、ts は js のスーパーセットであるとされています。実際、js コードは基本的に ts によってコンパイルされます。たとえば、js コード document.write ( )、図に示すように、上記のプロンプトは ts です。
システムは ts コードを記述するため、ts は js のスーパーセットであり、js に拡張機能を追加する言語です。 , so 以前はasという言語がありましたが、この言語はなくなってしまいました、正確にはFlash言語です。しかし、開発時に js を改善する必要がある場合は、そのようなものを持たなければなりません。そこで、Microsoft は as の構文を使用して ts という言語を開発しました。つまり、ts が Microsoft によって開発されたことを除けば、as と ts の構文は基本的に同じです。 , そして、昇進は良かったです。今、私たちのプログラミングがますます人気になるにつれて、ts はますます使用されています, 仕事を探すときも含めて. あなたの給料が 12,000 以上であれば、基本的に尋ねられます. 私はしませんts を知る 現在のプログラミングでは js に拡張機能を追加できるため ts が使われることが多い
例: 前述の AMD 、CMD 開発、CommonJS、es6 モジュラー仕様など、さまざまなモジュラー開発を実行できます。クライアント側で開発していると仮定すると、AMD、CMD、CommonJS、または es6 のいずれかを使用できる開発シナリオはありますか? CommonJS は、nodejs と es6 でのみ使用できるため、答えはノーです。モジュラー開発のみ使用できます。サーバーにはそれぞれ独自の制限があります。
しかし ts では、必要なものを何でも使用でき、すべてのシナリオをサポートします。 ts はバックエンドに似ており、その構文は Java に最も似ています。これにはどのような利点がありますか? js が弱い型指定の言語であることは誰もが知っています。たとえば、変数が最初に数値型として定義され、その後文字列型に再割り当てされると、変数の型が変更されます。この種の弱い型指定の開発には、一定のセキュリティがあります。はい、
たとえば、この変数を使用して他のことを行うと、他のタイプのデータになる可能性があるため、開発にとってセキュリティ リスクとなるため、標準化するには ts が必要ですjs の開発と排除 このセキュリティ リスクは、java や #c などの ts バックエンド言語が、これらの厳密に型指定された言語で変数を定義するときに、最初にこれらの変数の型を宣言する必要がある理由です。変数の型が定義されたら、このような仕様上の制約があると、開発はより安全になります。
現在、ts は非常に幅広いシナリオで使用されています。js プロンプト (エディターには標準化された構文を持つ ts が組み込まれています)、主流のフレームワーク vue、react も、下部にフレームワークを作成するときに ts を使用します。
そうは言っても、TypeScript である ts を直接導入しましょう。
#TypeScriptTypeScript は、Microsoft によって開発されたオープンソース プログラミング言語です。
型アノテーションとコンパイル時の型チェックが含まれます
任意のグローバルな場所で、最初に ts がインストールされているかどうかを確認します
tsc --version
npm install -g typescript
インストールが成功したかどうかを確認します:
tsc -v
tsc 被编译的文件
tsc 被编译的文件 --outFile 编译后的文件路径
tsc --init
其中常用配置项说明:
代表ts要转换后js版本
"target": "es5"
如果ts是以模块形式书写的,使用什么样的模块化规范,默认是commonJS
"module": "amd"
配置输出目录,可自己设置
"outDir": "./"
上面的配置项配置好以后,执行监视命令会自动编译:
tsc -w
使用amd规范的时候,需要将require.js文件拷贝到项目根目录下,且需要一个出口文件:
<script></script>
将ts编译成js,自动生成目标js文件
tsc 目标js文件
在定义变量的时候需要指定类型,且定义好类型以后就不能改变他的类型了 - 强类型。
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'];</string>
声明二维数组
var arr: number[]var brr: number[][] = [ [1,2,3], [4,5,6]];
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类型,表示弱类型,也就是当我们定义一个变量的时候,不能确定变量值的类型的时候,这个类型我们又爱又恨
使用:
var a:any = 20var b:any = 'asdfasdf'var c:any = [1,2,3]
注意:本来我们使用ts编写代码,为的是限制类型,减少安全隐患,但是如果使用了any类型,就跟直接写js一样了,失去了意义,所以若非迫不得已,尽量不要使用。
这种类型,一般用于函数执行后,不使用return返回结果的时候,就指定返回结果是void
声明变量的时候不使用它 - 当函数没有返回值的时候,返回类型指定为void
function fn(a:number,b:number):void{ console.log(a*b);}
这种类型主要用于参数和返回值,用于变量毫无意义,因为定义一个变量指定为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的时候使用
var a:number|null = 10;
使用变量a计算 - 完成
让内存回收这个变量
a = null
never类型表示永远不存在的值的类型,例如,一个函数中抛出的错误,函数中有死循环永远不可能返回 …
function fn():never{ throw new Error('错误')}function fn():never{ return fn()}fn()
对象类型:
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><p>函数重载:通过 为同一个函数提供多个函数类型定义 来实现多种功能的目的。例:</p> <pre class="brush:php;toolbar:false">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);
定义方式跟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个修饰符:
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');</string></number></t>
例:
// 约束函数传入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");</string></number></t>
多人合作开发项目的时候,避免不了会有函数、变量、类等数据的命名冲突。但是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
因为在es5和es6中允许dom元素继承EventTarget,但是在ts中不允许继承。
所以需要重构EventTarget。
使用dispathEvent来抛发事件,需要使用Event。所以重构Event。
本质:观察者模式。
开发的时候通常都是在使用模块化开发
怎么进行模块化开发?一个模块一个类,通常类的首字母会大写,文件名称和类的名称保持一致。
准备工作:
将ts配置文件中的module选项改成amd。
"module": "amd",
更改输入输出目录:
"outDir": "./js", "rootDir": "./ts",
新建html,导入amd所使用的require.js。
配置导入文件以及异步推迟加载。
<script></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><p>新建IListener.ts文件</p> <pre class="brush:php;toolbar:false">export default interface IListener{ [key:string]:Array<function>;}</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);}
效果:
第二次抛发的事件被删除 |
---|
更多编程相关知识,请访问:编程入门!!
以上がTypeScript を始めるための記事 (概要)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。