关注我们
关注【郑大钱呀】【公】【众】【号】,我们一起交流,一起学习。
TypeScript准备工作

什么是TypeScript
TypeScript(下文简称为ts)是微软开发的一个开源的编程语言,通过在JavaScript(下文简称为js)的基础上添加静态类型定义构建而成。ts通过其编译器或Babel转译为js代码,可运行在任何浏览器,任何操作系统。ts是js的超集,它支持所有js的语法,我们可以在ts中使用原生js语法,可以简单的理解成TS是JS的升级版。
ts与js两者的主要的区别如下:
| TypeScript | JavaScript |
|---|---|
| 强类型,需要强制转换类型 | js是弱类型,没有静态类型选项,会自己根据环境变化自动转换类型 |
| ts最终需要编程成js才能在浏览器中运行 | js可以直接在浏览器中运行 |
| ts是静态类型语言在编译期间就可以发现并纠正错误 | js是动态类型语言在运行时才知道错误 |
| 支持模块、泛型和接口 | 不支持模块、泛型和接口 |
TypeScript学习环境
学习一门编程语言,首先有其编程环境,为了能方便快速的学习TypeScript语言,推荐一个在线的TypeScript的演练场,打开浏览器,输入网址即可使用,如下:
https://www.typescriptlang.org/zh/play,界面如下:

TypeScript 安装
上面那种方式适合学习,但是工作时总不能这样来写代码,这里我们简单的说一下ts的环境搭建,首先我们需要先安装Node.js,我相信你已经会了,不会就百度嘛
然后我们打开命令行,执行如下命令:
npm install -g typescript
安装完成后,使用如下命令查看是否安装成功,如下:
tsc -v
然后我们打开vscode,创建一个ts文件(名为StudyTS.ts),写下第一条ts代码,如下:
console.log("我要关注郑大钱嗷,关注他,关注他");

然后我们在使用命令行,编译它,将ts,转化成js,命令如下:
tsc StudyTS.ts
当你执行完这条命令的时候,就会理所当然的发现你会多了一个js文件,此时我们来执行这个js文件,命令如下:
node StudyTS.js
执行结果如下:
好了,到这里准备工作就完成了,下面正式开始发车
注释
常言道,兵马未动,粮草先行,我们首先说说注释,注释是不参与代码运行的,是给人看的,意义就是能够让代码更加的易读,更方便的理解代码,所以我们尽可能的养成写注释的好习惯。注释主要分为两种:单行注释和多行注释,示例代码如下:
// 这是单行注释
/**
* 这是多行注释
*/
变量
变量的命名规范
- 变量名称只能有字母、数字、下划线组成,且不要为TypeScript 保留关键字
- 当变量名称由多个单词组成的时候,可以使用驼峰命名发,或者下划线风格,如:personName,person_name,都可以,最好不要写成personname,易读性差。
变量浅尝
// 声明一个变量
let perName:string = "郑大钱嗷"
// 声明一个变量,不为其初始化
let sex:string;
// 声明一个常量
const age:number =10
console.log(perName);
console.log(age);
这里我们可以和编译后的js语法对比一下,如下:
"use strict";
// 声明一个变量
let perName = "郑大钱嗷";
// 声明一个变量,不为其初始化
let sex;
// 声明一个常量
const age = 10;
console.log(perName);
console.log(age);
可以发现我们在ts的代码中变量名称多了一个:和变量类型,这个就是限定变量的类型的,如果你想这个变量不需要类型显示,你可以使用js的语法,或者这样写
let perName:any = "郑大钱嗷"
此外假如你给一个变量限定类型为字符串,那么你赋一个整型的值就会报错,如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vRqSu1JR-1651992033169)(https://files.mdnice.com/user/18450/c2bceda2-7dec-420f-8038-06e0ad75ad0e.png)]
再者,如果我们声明了一个变量,但是没有初始化,如果没有使用该变量,则不会报错,但是如果使用的话就会报错。
变量的联合类型
我们上面限制的是一个类型,就是一个变量只能保存一个类型,我们也可以一个变量保存多个类型,这样这个变量就能保存多个类型的数据,示例代码如下:
let varPro:string|boolean =true;
console.log(varPro);
varPro="郑大钱嗷";
console.log(varPro);
运行结果如下:

数组
数组就是一组数据,主要用来保存类型相同,含义相同的数据,比如我们要定义一个数组用来保存数字,示例代码如下:
let num:number[] = [1,2,3,4,5,6]
console.log(`数字:${num}`);
运行结果如下:
上面我们是打印了所有的数组数据,假如我想取其中某个数据,这个时候需要指定索引,值得注意的是,索引是从0开始的,示例代码如下:
let num:number[] = [1,2,3,4,5,6]
console.log(`数字:${num[0]}`);
运行结果如下:
数组除了以上的声明方式之外,还可以使用Array类来声明,并且可以结合泛型来使用,具体在泛型类讲。
下面我们接着说数组的常用操作方法,直接看示例代码:
let array:number[] = [1,9,3,4,5,6];
console.log(`原始数组:${array}`);
// 在数组的最后位置,添加元素
array.push(7);
console.log(`数组的最后位置,添加元素${array}`);
// 在数组的最前面,添加元素
array.unshift(0);
console.log(`在数组的最前面,添加元素:${array}`);
//删除最后面的元素
array.pop();
console.log(`删除最后面的元素:${array}`);
//删除最前面的元素
array.shift();
console.log(`删除最前面的元素:${array}`);
//从第几位开始删除几个元素,注意索引从0开始,splice的用法不仅是删除,更新数组的内容更贴切,有其他很多用法,可以自行百度
array.splice(0,1);
console.log(`删除指定元素:${array}`);
// 合并两个数组
let array2:number[] =[10,11,12];
array = array.concat(array2);
console.log(`合并两个数组:${array}`);
// 查找元素的位置
console.log(`查找元素的位置:${array.indexOf(10)}`);
// 对数组进行正序排序
console.log(`对数组进行正序排序${array.sort()}`);
// 对数组进行倒序序排序
console.log(`对数组进行正序排序${array.reverse()}`);
运行结果如下:


元组类型
// 声明一个元组
let person:[string,string,number] =['郑大钱嗷','男',18];
// 输出
console.log(person);
console.log(person[0]);
运行结果如下:

元组和数组的区别在于元组知道每个元素的类型,简单的说元组就是在数组的基础上对各个元素加了类型的限制。
字典类型
字典类型由键值对组成,键和值,示例代码如下:
// 声明一个字典,并赋值
let person:{[key:string ]:string }={
"name":"郑大钱嗷",
"sex":"男"
}
// 获取name的值
console.log(person["name"]);
// 修改name的值
person["name"]="郑大钱嗷~";
console.log(person["name"]);
运行结果如下:
枚举类型
当一个变量有多种值,然后值又是比较固定的,比如颜色,通常使用的主要颜色就那么几种,这个时候就可以将其定义成一个枚举类型,示例代码如下:
enum Color{
red,
orange,
yellow,
green
}
console.log(Color.orange);
它最后会返回指定枚举的索引,示例代码如下:

模板字符串
模板字符串相当于定义一个模板,然后在模板里刨坑,然后再把值放在里面,这样就可以按照我们的要求显示字符串了,比直接使用+号拼接更加的方便。示例代码如下:
let perName:string = "郑大钱嗷"
console.log(`名字:${perName}`);
typeof 类型验证
这个typeof的功能就是返回当前变量的数据类型,示例代码如下:
let age = 18;
console.log(typeof age);
运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LBrZJcc2-1651992033183)(https://files.mdnice.com/user/18450/0efc4337-8555-45b8-b1eb-5d516937ee82.png)]
类型别名
这个用的不多,基本不用,就是你觉得系统的number类型这个名字不好听,觉得原来的名字太委屈
这个时候你就可以给他换个名字,示例代码如下:
type haoTingNum = number;
let age:haoTingNum=18;
console.log(age);
运行结果如下:
运算符
算数运算符
算数运算符就是加减乘除、取余,自增和自减示例代码如下:
let a:number = 5;
let b:number =6;
console.log(`a+b=${a+b}`);
console.log(`a-b=${a-b}`);
console.log(`a*b=${a*b}`);
console.log(`a/b=${a/b}`);
console.log(`a%b=${a%b}`);
运行结果如下:
自增和自减运算符:所谓自增/减,就是在之前值的基础上自己加1或减1,自增/减,又分为前自增/减,和后自增/减,示例代码如下:
let a:number = 5;
// 先使用,后自增,此时使用的时候a还是5,使用后a的值为6
console.log(`a++:${a++}`);
// 同样是先使用,后自减,此时使用的是a是6,使用后a的值是5
console.log(`a--:${a--}`);
// 这里不一样了,是先自增,在使用,此时使用时a为6
console.log(`++a:${++a}`);
// 这里是先自减,在使用,此时使用时a为6-1=5
console.log(`--a:${--a}`);
运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VL4oWsuR-1651992033187)(https://files.mdnice.com/user/18450/34f37cde-0825-4e15-9a10-7194a565998c.png)]
比较运算符
所谓比较运算符就是指大于、小于、等于(、=)、不等于、大于等于、小于等于,比较运算符返回结果一个布尔值即ture或者false,示例代码如下:
let a:number=1;
let b:number=2;
let c:number=1;
console.log(`a>b: ${a>b}`);
console.log(`a<b: ${a<b}`);
console.log(`a>=b: ${a>=b}`);
console.log(`a<=b: ${a<=b}`);
console.log(`a==b: ${a==b}`);
console.log(`a===b: ${a===b}`);
console.log(`a!=b: ${a!=b}`);
运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LUjhgltV-1651992033188)(https://files.mdnice.com/user/18450/66285d46-275c-4027-a73c-29e8da7da67a.png)]
这里补充一下==和===的区别,如果是==比较的是值相等,而===不仅要求值相等,还要求数据类型相等。
逻辑运算符
逻辑运算符,主要有三种,与、或、非,三种,我们举个例子,比如我们想吃冷饮,但是我们有要求,我们必须要吃哈根达斯而且至少还得三个球,必须满足这两个条件,否则不吃。
示例代码如下:
let iceName:string="哈根达斯";
let qiuNum:number=5;
if(iceName=="哈根达斯"&&qiuNum>=3){
console.log('好好吃')
}else{
console.log('少球了,哼,不吃');
}

赋值运算符
赋值运算符是一种简写,具体有如下几种:=、+=、-+、*=、/=、%=,这里我们以加法举例,示例代码如下:
let a:number = 1;
a+=1;
let b:number = 1;
b=b+1
console.log(`a:${a}`);
console.log(`b:${b}`);
运行结果如下:

也就是说a+=1就等同于a=a+1。
条件语句
判断语句
所谓判断,即使如果这样,我会怎么样,如果那样,又会怎么样,比如上面得那个例子,让我偷下懒,直接拿过来用
let iceName:string="哈根达斯";
let qiuNum:number=5;
if(iceName=="哈根达斯"&&qiuNum>=3){
console.log('好好吃')
}else{
console.log('少球了,哼,不吃');
}
判断语法主要有如下几种结构,if...、if...else...、if...elseif...else...,我们来看一个经典的案例,根据分数来判断成绩的等级,示例代码如下:
let score:number =60;
if(score>=90 && score<=100){
console.log("优秀");
}else if(score>=80 && score<90){
console.log("良好");
}else if(score>=70 && score<80){
console.log("中等");
}else if(score>=60 && score<70){
console.log("及格");
}else{
console.log("支棱起来,好嘛");
}
运行结果如下:

此外有个和判断语句类似的运算符,叫三目运算符,写起来更加的简单,具体示例如下:
//三目运算符 条件?值1:值2
let sex:string ="男";
console.log(sex=="男"?"好帅啊":"好美啊")
运行结果如下:

当我们有多种条件判断的时候,if判断语句不是特别的好用,我们可以使用switch语句,示例代码如下:
enum Color{
red,
black,
green
}
let color:Color = Color.red
switch(color){
case Color.red :
console.log("red");
break;
case Color.black :
console.log("black");
break;
case Color.green:
console.log("green");
break;
default:
console.log("其他颜色");
}
运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cNwSfDo5-1651992033195)(https://files.mdnice.com/user/18450/19845c0f-8946-429a-a6e4-a066be6235bc.png)]
这里要值得注意的是,每个条件分支要加break语句,否则匹配之后,还会继续匹配,这里的default就相当与if中的else,即所有分支都没有匹配到才会执行。
循环语句
所谓循环就是反复的执行某些事情,比如说,让唐僧念100遍经
我们肯定不可能这样写:
console.log("念经");
console.log("念经");
console.log("念经");
console.log("念经");
console.log("念经");
console.log("念经");
console.log("念经");
//...100遍
这样也可以实现,但是有点不太聪明的样子,于是我们需要更高级的魔法来实现,示例代码如下:
let i:number=1;
// 先判断已经念了多少次了,有没有超过100次,没有超过就接着念,超过就不念了
while(i<=100){
console.log(`唐僧念经,第${i}次`);
i++;
}
上面是while...do..的写法,即先判断,在执行,还有一种do...while...的写法,即先执行再判断,代码如下:
let i:number=1;
do{
console.log(`唐僧念经,第${i}次`);
i++;
}while(i<=100);
值得注意的一点是,循环的条件一定要有出口,即满足什么条件后,就不执行了,否则就会一直执行,就是死循环.
循环也可以遍历数据,示例代码如下:
let names:string[]=["郑","争","争"];
let i:number=0;
while(i<names.length){
console.log(names[i]);
i++;
}
运行结果如下:

除了有while循环外,还有for循环,示例代码如下:
let names:string[]=["郑","争","争"];
for(let j=0;j<names.length;j++){
console.log(names[j]);
}
对于数组,我们for循环还有另一种写法,更方便,更加简单,示例代码如下:
let names:string[]=["郑","争","争"];
//是用of,是把值给name,如果是in,则是把索引给name
for(let name of names){
console.log(name);
}
函数
函数就是一段代码块,目的是为了能够更好的实现代码的复用,但我们定义了一个函数后,当我们需要使用的时候,就可以直接调用,代码的复用性大大提高,代码量会大大的减少
让我们先简单的感受一下:
// 定义一个有参数的函数
function saySomething(word:string):void{
console.log(word);
}
// 调用函数
saySomething("你好");
saySomething("我很好,非常好,老好了");
运行结果如下:

我们上面定义了一个函数,function 为定义函数的关键字,函数名为saySomething,里面的word,为传入函数的参数,我们一般成为入参,同样有入参肯定就有出参,这里的void表示没有返回值,即没有出参,我们这个函数没有定义出参,下面我们定义一个加法的方法来返回一个出参,示例代码如下:
function add(num1:number,num2:number){
return num1+num2;// 返回计算结果
}
console.log(add(1,3));
运行结果如下:
上面的函数,还可以有如下2种写法,示例代码如下:
let add = function(num1:number,num2:number){
return num1+num2;
}
console.log(add(1,3));
let add2 =(num1:number,num2:number) =>{
return num1+num2;
}
console.log(add(1,3));
console.log(add2(1,3));
面向对象
什么是对象?在我们在说对象之前,我们了解一下类,我们把一切具有共性的事务可以定义成一个类,类就相当于一个模板,比如狗、猫、猪,都可以当成一个类,那什么是对象呢,对象就是对类的具体实现,比如猪是一个类,但是猪有好多种,有苏格兰打卤猪、黑猪、野猪、母猪等等,我们可以一句话总结一下:类是对象的抽象,而对象是类的具体化
我们先来感受一下,示例代码如下:
// 定义一个猪类
class Pig{
//定义类的熟悉
name:string="猪";
age:number = 1;
constructor(name:string,age:number){
this.name=name;
this.age=age;
}
eat():void{
console.log("吃东西")
}
}
//实例化对象
let pig1 = new Pig("飞猪",10);
pig1.eat();
我们受限创建了一个Pig的类型,里面定义了两个属性name和age,下面的constructor为构造方法,所谓构造方法就是当你对对象实例化的时候,就执行的方法,如果构造方法需要传参的时候,那么再实例化对象的时候就需要为其传入参数,否则会报错,下面的eat方法就是普通的方法。
我们实例化的时候,需要使用 new关键字来实例化,实例化完成后,我们就可以使用对象了。
静态(成员)属性和静态(成员)方法
静态属性、方法就是有static关键字修饰的,不需要实例化对象,使用类名就可以调用,而成员属性、方法是属于对象的,需要实例化后才能调用,实例代码如下:
// 定义一个猪类
class Pig{
//定义类的熟悉
name:string="猪";
age:number = 1;
static belong:string="动物"
constructor(name:string,age:number){
this.name=name;
this.age=age;
}
eat():void{
console.log("吃东西")
}
static sleep():void{
console.log("睡觉觉")
}
}
//实例化对象
let pig1 = new Pig("飞猪",10);
//成员方法调用
pig1.eat();
console.log(pig1.name);
//静态方法调用
Pig.sleep();
console.log(Pig.belong);
运行结果如下:

继承
所谓继承就是子类继承父类,就与儿子继承父亲的财产是一样,比如我们有一个父类为车类,然后我们有一个子类为面包车,为了方便代码的复用以及可拓展性,我们可以先定义父类,然后用子类继承父类,这样子类就不需要重新编写了,只需要再父类的基础上就行修改就可以了,示例代码如下:
// 定义一个Car类
class Car{
color:string = "black";
seats:number= 5;
startCar():void{
console.log("启动汽车")
}
}
// 子类继承父类
class MianBaoCar extends Car{
}
let car1 = new MianBaoCar();
car1.startCar();
我们定义了一个父类Car,然后子类MianBaoCar继承父类,这样子类就可以调用父类的方法了,但是如果父类的方法不适合子类的方法,我们想要覆盖父类的方法,应该怎么做呢,我们只需要在子类种定义一个方法与父类中的方法同名,就可以覆盖父类的方法,示例代码如下:
// 定义一个Car类
class Car{
color:string = "black";
seats:number= 5;
startCar():void{
console.log("启动汽车");
}
}
class MianBaoCar extends Car{
startCar():void{
console.log("启动面包车");
}
}
let car1 = new MianBaoCar();
car1.startCar();
运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rd8a75kl-1651992033203)(https://files.mdnice.com/user/18450/efefe9f6-2bf4-4938-aafe-4717c1fb1924.png)]
但是此时你想,我不要完全覆盖,我想要在父类之前的方法上拓展,怎么做呢,我们可以使用super关键字,来调用父类的方法后再拓展,示例代码如下:
// 定义一个Car类
class Car{
color:string = "black";
seats:number= 5;
startCar():void{
console.log("启动汽车");
}
}
class MianBaoCar extends Car{
startCar():void{
super.startCar();
console.log("呜呜呜~~~~");
}
}
let car1 = new MianBaoCar();
car1.startCar();
运行结果如下:

抽象类
所谓抽象类,就是抽取子类共有的部分,仅仅做定义,不做具体的实现,具体的实现需要在子类中实现,且子类必须实现,我们看一个简单的示例:
// 定义一个抽象类
abstract class Person{
abstract name:string;
abstract say():void;
}
class Student extends Person{
name="郑大钱嗷"
say(){
console.log("说话")
}
}
let stu:Person = new Student();
stu.say();
运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vb45Nph9-1651992033205)(https://files.mdnice.com/user/18450/83e654f1-fe4e-457c-a20a-f5c4b07f6ba3.png)]
接口
由于ts只能进行单继承,没有办法实现多继承,接口就是为了弥补ts没有多继承的特性,同时接口也是相当于一个规范,当子类使用一个接口的时候,需要实现接口里的所有方法,示例代码如下:
class Person{
name:string ='人'
}
interface Wolf{
bite():void;//只能定义,不能实现
}
class WolfMan extends Person implements Wolf{
bite(){
console.log("撕咬~")
}
}
let a = new WolfMan();
a.bite();
运行结果如下:

私有属性及其操作(属性寄存器)
如果有接触过java的同学,就会发现这个很熟悉,就是setter和getter方法,即对属性取值和赋值的时候,只能通过setter/getter方法,这样可以很好的保护属性的值,下面我们看一个示例:
class Person{
_hp:number = 100; //使用的名字的话 前面必须要加上_
// 取值
set hp(value){
// 如果hp小于0,直接赋值为0,这样就不会出现负数了
if (value<0){
this._hp=0;
}else{
this._hp=value;
}
}
// 赋值
get hp():number{
return this._hp;
}
}
let a = new Person();
a.hp=-10;
console.log(a.hp)
运行结果如下:
命名空间
命名空间的出现主要是为了区分代码中定义的标识符,通过 namespace 关键字声明命名空间,在命名空间外部需要通过完全限定名访问这些对象,这样即便有同名的类名、变量,也不会有冲突,示例代码如下:
// 声明命名空间
namespace space1{
// 需要导出,否则会有报错
export class Person{
}
}
namespace space2{
export class Person{
}
}
// 实例化的时候,需要使用命名空间.类名的形式
let a = new space1.Person();
泛型
所谓泛型根据字面意思了解,就是宽泛的类型,通常用于类和函数,用于对不特定数据类型的支持,也就是输入的参数类型是可变的,根据你的输入参数类型来决定,我们先看一段示例代码
function addT<T>(num:T):T{
if (typeof num=="number"){
num++;
return num;
}
return num;
}
console.log(addT(3));
运行结果

上面的的<T>表示的就是一个泛型,出参和入参都是可变的,且两者的类型一致,比如你输入的是一个number数据类型,那么上面的函数一定返回number类型。
在数组的声明中,我们也可以使用泛型,示例代码如下:
// 使用泛型声明一个空的数组
let array: Array<number> = new Array<number>();
console.log(array);
运行结果如下:

回调
我们之前在定义函数的时候,我们的入参一般是一个值,但是现在我们传入的不是一个值,而是一个函数体。
function func(value:Function){
value();
}
function add(){
console.log("测试方法");
}
func(add)
运行结果如下:

除了上面的写法外,我们还有另一种写法,匿名函数,示例代码如下:
function func(value:Function){
value();
}
func(function add(){
console.log("测试方法");
})
我们也可以使用箭头函数的方式,示例代码如下:
function func(value:Function){
value();
}
func(()=>{
console.log("测试方法");
});
上面的函数是没有传入参数的,我们也可以传入参数,示例代码如下:
function func(value:Function){
value("测试");
}
func((str:string)=>{
console.log(str);
});
正则表达式
正则表达式就是用来匹配一系列符合某些语法规则的字符串,正则表达式通常被用来检索、替换符合某个模式的文本。正则表达式是由普通字符、元字符、限定符组成,具体的这里就不展开说了,主要看一下,ts中正则表达式的使用,示例代码如下:
let reg =/\d{2}-\d{4}/g;
let str:string="1111-12345678";
// 输出匹配结果
let res = reg.exec(str);
console.log(`匹配的次数:${res.length}`);
// 输出匹配的内容
res.forEach(function(value,index){
console.log(`value:${value},index:${index}`);
});
运行的结果如下:

访问修饰符
public 修饰符
示例代码如下:
class Person{
// public 公开的,属性默认为public,表示属性和方法,内部和外部都可以访问
public name: string|undefined;
public say(){
}
}
class Student extends Person{
constructor(){
super();
}
}
let a:Person = new Person();
a.name="";
a.say();
public表示 公开的,属性默认为public,表示属性和方法,内部、外部、子类都可以访问
protected 修饰符
示例代码如下:
class Person{
protected name: string|undefined;
protected say(){
}
}
class Student extends Person{
constructor(){
super();
}
}
protected修饰符表示受保护的,在内部和子类都可以访问,但是外部无法访问。
private 私有的
示例代码
class Person{
private name: string|undefined;
private say(){
}
}
class Student extends Person{
constructor(){
super();
}
}
protected修饰符表示私有的,只能在内部访问,但是子类、外部无法访问。
单例模式
单例模式,就是通过单例模式的方法创建的类在当前进程中只有一个实例,示例代码如下:
class Manager{
// 内部创建自己的对象,并用静态修饰
static Instance = new Manager();
// 构造方法,私有化,不能让外部new对象
private constructor(){
}
}
console.log(Manager.Instance);
我们还有第二种写法,示例代码如下:
class Manager{
// 内部创建自己的对象,并用静态修饰
private static instance:Manager;
// 构造方法,私有化,不能让外部new对象
private constructor(){
}
static Instance(){
// 如果没有单例产生就创建单例
if(!Manager.instance){
Manager.instance = new Manager();
}
return Manager.instance;
}
}
Manager.Instance();
代理模式
为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。简单的说我要做一件事,但是我不做,我去找一个人(即:代理人)给我做,然后给我返回结果。
// 定义一个接口
interface Calc{
calc(a:number,b:number):number;
}
// 代理人1,实现接口,要想做代理人,必须要实现计算的方法
class Agent1 implements Calc{
calc(a:number,b:number){
return a+b;
}
}
// 代理人2
class Agent2 implements Calc{
calc(a:number,b:number){
return a+b;
}
}
class Person{
currAgent: Calc;
getNum(a:number,b:number){
console.log(this.currAgent.calc(a,b));
}
}
let person = new Person();
person.currAgent = new Agent1();
person.getNum(1,3);
运行结果如下:

观察者模式
当对象间存在一对多关系时,则使用观察者模式。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。示例代码如下:
interface Listener{
changeName(name:string):void;
}
class Person {
private _perName:string="";
// 所有观察者
obListeners:Array<Listener> = new Array<Listener>();
set perName(value:string){
this._perName=value;
for(let listener of this.obListeners){
listener.changeName(this._perName);
}
}
get perName():string{
return this._perName
}
}
class Test implements Listener{
changeName(newName:string){
console.log("名字有变化!,新名称:"+newName);
}
}
let per = new Person();
let listener1 = new Test();
let listener2 = new Test();
per.obListeners.push(listener1);
per.obListeners.push(listener2);
per.perName="郑大钱嗷"
运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FzBAHqNi-1651992033213)(https://files.mdnice.com/user/18450/747038e3-e0f7-445a-81b0-9cf98b9827d8.png)]
工厂模式
定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行,在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。示例代码如下:
enum CarType{
BenZ,
Audi,
Bmw
}
class Car{
name:string="";
// 工厂方法
static createCar(carType:CarType):Car{
let car:Car;
switch(carType){
case CarType.Audi:
car = new Audi();
break;
case CarType.BenZ:
car = new Benz();
break;
case CarType.Bmw:
car = new Bmw();
break;
}
return car;
}
}
class Benz extends Car{}
class Audi extends Car{}
class Bmw extends Car{}
Car.createCar(CarType.Audi);