TypeScript
一、TypeScript 介绍
- TypeScript 是由微软开发的一款开源的编程语言
- TypeScript 是 JavaScript的超集,遵循最新的ES6、ES5规范。TypeScript扩展了JavaScript的语法
- TypeScript 更像后端Java、C#这样的面向对象语言。可以让JavaScript开发大型企业项目
- 谷歌也在大力支持TypeScript的推广,谷歌的 angular 2.x+就是基于TypeScript语法
- 最新的Vue、React 也可以集成TypeScript
- NodeJS 框架 Nestjs、midway 中用的就是TypeScript语法
TypeScript包含JavaScript
二、TypeScript 安装、编译
在使用npm 命令之前电脑必须安装nodejs
2.1、安装
npm install -g typescript
//查看版本、验证是否安装完成
tsc -v
2.2、运行
将 TS 文件编译为 JS文件
//终端进入目标文件所在文件夹
//执行:tsc 文件名.ts
//tsc .\01-index.ts
tsc greeter.ts
2.2.1、自动编译
1
//在目标目录执行
tsc --init
//生成tsconfig.json配置文件
//将下面代码注释取消掉
"outDir": "./", /* 将输出结构重定向到目录. */
//可指定输出js文件所在文件夹
//如:"outDir": "./js",
VScode编辑器中,点击 终端 ==> 运行任务 ==> typescript ==> tsc : 监视 ‘xxxxxxxx’
随后保存文件时就会自动编译ts文件
三、TypeScript中的数据类型
typescript中为了使编写的代码更规范,更利于维护,增加了类型校验,在typescript中主要提供了以下数据类型
- 布尔类型(boolean)
- 数字类型(number)
- 字符串类型(string)
- 数组类型(array)
- 元组类型(tuple)
- 枚举类型(enum)
- 任意类型(any)
- null和undefined
- void类型
- never类型
typescript中声明变量时要声明类型
3.1、布尔类型
//布尔类型只能说true或false
let a:boolean = true
//如果赋值不是对应数据类型,编译将不通过
3.2、数字类型
let b:number = 123
b = 12.3
3.3、字符串类型
let str:string = 'im string'
// 赋值类型不相符时会报错
// str = 123
// str = true
3.4、数组类型
1、使用 变量名:类型[] 定义数组
// 指定数组为数字数组
let arr:number[] = [123,34,21,2]
// 当包含其他数据类型时会报错
// arr = [1,2,3,'a']
// 其他类型同理
let arr1:string[] = ['a','d','c']
let arr2:any[] = ['a',123,{name:'sd'}]
2、使用 变量名:Array<数组中的数据类型> 泛型定义数组
Vue中是否支持泛型?
let arr2:Array<number> = [1,2,3,4]
3.5、元组类型
属于数组的一种
//后方赋值必须和定义的数组长度、类型位置一致
//否则将会报错
let arr3:[string,number,boolean] = ['a',23,true]
3.6、枚举类型
随着计算机的不断普及,程序不仅只用于数值计算,还更广泛地用于处理非数值的数据。例如:性别、月份、星期几、颜色、单位名、学历、职业等,都不是数值数据。
在其它程序设计语言中,一般用一个数值来代表某一状态,这种处理方法不直观,易读性差。如果能在程序中用自然语言中有相应含义的单词来代表某一状态,则程序就很容易阅读和理解。
也就是说,事先考虑到某一变量可能取的值,尽量用自然语言中含义清楚的单词来表示它的每一个值,这种方法称为枚举方法,用这种方法定义的类型称枚举类型。
简单理解为:使用不同数值表示不同的状态
// enum 枚举名 {
// 标识符[=整型常数]
// 标识符[=整型常数]
// ...
// 标识符[=整型常数]
// 标识符[=整型常数]
// }
#枚举名首字母大写
enum Flag {
success = 1,
error = -1
}
let ok:Flag = Flag.success
console.log(ok,Flag.error)
//当没有给标识符指定常数时,将会返回其下标
enum Color {
red,
blue,
yellow
}
let c:Color = Color.red
console.log(c)
enum Color {
red,
blue=5,
yellow
}
let c:Color = Color.red
//打印Color.red 0
//打印Color.blue 5
//打印Color.yellow 6 //当前标识符没有赋值,但前方标识符有值时,其值会紧接前方标识符的值 +1
console.log(c)
3.7、任意类型
// ========= 任意类型
// 允许变量为任何类型
let aaa:any = 'asd'
aaa = 123
3.8、null和undefined
let t3:null
// t3='as' //报错
let t1:undefined
console.log(t1) //undefined 不会报错
// let t2:number
// console.log(t2) //undefined 报错
3.9、void类型
typescript中的void表示没有任何类型,一般用于定义方法的时候 方法没有返回值
// 表示方法没有返回值
let fn11 = ():void => {
console.log('fn11')
}
fn11()
// 指定返回值类型为number
let fn12 = ():number => {
console.log('fn11')
return 123
}
fn12()
// 不指定(原本js写法
let fn13 = () => {
console.log('fn11')
return 123
}
fn13()
3.10、never类型
是其他类型(包括null和undefined)的子类型,代表从不会出现的值
这意味着设声明never的变量只能被never类型所赋值
使用较少
let a:never
a = (() => {
throw new Error('错误')
})
3.11、允许变量多个类型
// 指定多种数据类型
//允许变量为 数字 undefined null
let bbb:number | undefined | null
bbb = 123
// bbb = 'ass'
四、TypeScript中的函数
4.1、函数的使用
// ========== es5中定义函数
// 具名函数
function run() {
return 'run'
}
// 匿名函数
let run2 = () => {
return 'run2'
}
// ======== ts中定义函数
// 具名函数
function run3():string{
return 'str'
}
// 匿名函数
let run4 = ():number => {
return 123
}
console.log(run3(),run4())
4.2、函数传参
定义参数类型和返回值类型
let getName = (name:string,age:number):string => {
return `${name}-----${age}`
}
// console.log(getName('asd','20')) //报错,参数类型必须符合定义规范
console.log(getName('asd',20))
// 没有返回值的方法
let noBack = (name:string):void => {
console.log(name)
}
4.3、可选参数
在变量名后加 ? 表示该参数可传,可不传
let getName2 = (name:string,age?:number):string => {
if(age){
return `${name}-----${age}`
}else{
return `${name}-----未传年龄`
}
}
console.log(getName2('asd',20))
console.log(getName2('asd'))
4.4、设置默认参数
es5中不能设置默认参数,es6和ts中可设置默认参数
在指定类型后加 =默认值 ,设置默认参数
let getName3 = (name:string,age:number=20):string => {
return `${name}-----${age}`
}
console.log(getName3('asd'))
console.log(getName3('asd',13))
4.5、剩余参数
结合ES6中的扩展运算符 …
let sumFn = (...intNum:number[]):number => {
let sum = 0
intNum.forEach((item) => {
sum += item
})
return sum
}
console.log(sumFn(1,2,3,4,5))
4.6、函数重载
Java中的方法重载,重载指的是两个或两个以上的同名函数,但他们的参数不一样,这时会出现函数重载的情况
typescript中的重载,通过为同一个函数提供多个函数类型定义来实现多种功能的目的
// ES5中,下方函数会替换上方同名函数
// function infor(name){
// }
// function infor(age){
// }
// ts中的重载
function infor(name:string):string
function infor(age:number):number
function infor(arg:any):any{
if(typeof str === 'string'){
return '姓名' + arg
}else{
return '年龄' + arg
}
}
console.log(infor('nb'))
console.log(infor(23))
// console.log(infor(true)) //错误用法
#暂时没有理解重载优势
五、类
5.1、ES5\ES6中定义类
#=========== ES5中定义类
function Person(name){
this.name = name;
this.say = function() {
console.log(this.name)
}
}
let p1 = new Person('张三')
p1.say()
#=========== ES6中的定义类
class Student{
constructor(name,age){
this.name = name;
this.age = age
}
say(){
console.log(this.name + '--------' + this.age)
}
}
let stu1 = new Student('张三',12)
stu1.say()
5.2、ts中定义类
class Teacher{
// 先定义类中属性的类型
name:string;
age:number
constructor(n:string,age:number=20){
// this.name:string = n //这样定义类型会报错
this.name = n;
this.age = age;
}
say():void{
console.log(this.name + 'hi' + this.age)
}
getName():string{
return this.name
}
setName(newName:string):void{
this.name = newName
}
}
let teacher1 = new Teacher('小丽')
teacher1.say()
teacher1.getName()
teacher1.setName('小丽丽')
teacher1.say()
5.3、继承 extends
class Boy{
name:string;
age:number;
constructor(n:string,a:number){
this.name = n;
this.age = a
}
run():void{
console.log(this.name + 'is running')
}
}
let b1 = new Boy('小王',22)
console.log(b1)
b1.run()
// 使 Girl 继承 Boy
class Girl extends Boy{
}
let g1 = new Girl('小芳',18)
console.log(g1)
g1.run()
5.4、super关键字
// 使用 super 继承 Boy 的构造函数
// super关键字能将构造函数继承后指向 新类(Kid) 而非 原类(Boy)
class Kid extends Boy {
constructor(n:string,a:number){
super(n,a)
}
// 子类和父类中有相同的方法时,会调用子类的方法(就近原则
run():void{
console.log(this.name + '在游泳')
}
// 子类新增方法
play():void{
console.log(this.name + '在打游戏')
}
}
let k1 = new Kid('小弟弟',9)
console.log(k1)
k1.run()
k1.play()
5.5、TS类中的修饰符
typescript里面定义属性的时候给我们提供了三种修饰符
1 public:公有类型(默认类型) 在当前类、子类、类外面都可以访问
2 protected:保护类型 在当前类、子类可以访问,在类外部无法访问
3 private:私有类型 在当前类中可以访问,子类和类外部都无法访问
# === 父类
class Father{
// 设置为public
public name:string;
protected age:number;
private money:number
constructor(n:string,a:number,m:number){
this.name = n;
this.age = a;
this.money = m
}
sayName():void{
console.log(`姓名:${this.name}`)
}
sayAge():void{
console.log(`年龄:${this.age}`)
}
showMoney():void{
console.log(`现金:${this.money}`)
}
}
let f1 = new Father('父类',37,900000)
console.log({msg:'父类中访问属性'})
// 访问 public 属性
f1.sayName()
// 访问 protected 属性
f1.sayAge()
// 访问 private 属性
f1.showMoney()
# === 子类
class Son extends Father {
constructor(n:string,a:number,m:number){
super(n,a,m)
}
sonSayName():void{
console.log(`姓名:${this.name}`)
}
sonSayAge():void{
console.log(`年龄:${this.age}`)
}
sonShowMoney():void{
// console.log(`现金:${this.money}`) //子类不能访问父类私有属性,编译报错
}
}
let s1 = new Son('小兰兰',10,200)
console.log({msg:'子类中访问属性'})
// 访问 public 属性
s1.sonSayName()
// 访问 protected 属性
s1.sonSayAge()
// 访问 private 属性
s1.sonShowMoney()
# 类外部访问
console.log({msg:'类外部访问属性'})
// 访问 public 属性
console.log(f1.name)
// 访问 protected 属性
// console.log(f1.age) //外部不能访问父类保护属性,语法错误,编译报错
// 访问 private 属性
// console.log(f1.money) //外部不能访问父类私有属性,语法错误,编译报错
5.6、TS静态方法/属性
# ES5中
function Personfour(){
this.run = function(){
console.log('实例方法')
}
}
Personfour.say = function(){
console.log('静态方法')
}
// 实例方法需要 new 实例化对象后才能使用
let p4 = new Personfour()
p4.run()
// 静态方法不需要实例化即可调用
Personfour.say()
# TS中
class Student{
public name:string;
public money:number = 0
// 使用 static 定义静态属性
static sex:string = '女'
constructor(name:string){
this.name = name
}
// 实例方法
doThing():void{
console.log(`${this.name} is sleeping`)
}
// 使用 static 关键字设置静态方法
// 静态方法中,不能直接调用类里面的属性
static work():void{
console.log('静态方法')
// console.log(`${this.name} have ${this.money}`)
// 静态方法中可以访问静态属性
console.log(`sex is ${this.sex}`)
}
}
let stu = new Student('小恐龙')
stu.doThing()
// 访问静态方法
Student.work()
// 访问静态属性
console.log(Student.sex)
5.7、TS多态
父类定义一个方法不去实现,让继承他爱的子类去实现,每一个子类有不同的表现
多态属于继承
class Aniaml {
name:string;
constructor(name:string){
this.name = name
}
eat(){
console.log(`吃东西`)
}
}
class Dog extends Aniaml {
constructor(name:string){
super(name)
}
eat():string{
console.log(`吃东西`)
return this.name + '吃骨头'
}
}
class Cat extends Aniaml {
constructor(name:string){
super(name)
}
eat():void{
console.log(`吃东西`)
console.log(this.name + '吃老鼠')
}
}
5.8、抽象类
抽象类用来定义标准
其是提供其他类继承的基类,不能直接被实例化
使用 abstract 关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现,并且必须在派生类中实现
abstract 抽象方法只能放在抽象类中 !
抽象类中的抽象方法用来定义标准 !
如: Animals 类的子类必须包含eat方法
//定义抽象类
abstract class Animals{
public name:string;
constructor(name:string){
this.name = name
}
sleep():void{
console.log(this.name + 'is sleeping')
}
// 定义抽象方法
abstract eat():any
}
// let aaa = new Animals() //抽象类不能被实例化
class Fish extends Animals{
constructor(name:string){
super(name)
}
// 抽象对象的子类/派生类中必须实现抽象方法
eat():void{
console.log(this.name + '吃鱼')
}
}
let f = new Fish('鲸鱼')
f.eat()
六、接口
接口的作用,在面向对象的编程中,接口是一种规范的定义。它定义了行为和动作的规范,在程序设计里面,接口起到了一种限制和规范的作用。
接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节。
它只规定这些类里必须提供某些方法,提供这些方法的类就可以满足实际需要。
TypeScript中的接口类似于Java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等
简单理解:对传入的参数进行类型约束,定义标准
6.1、使用
// ts中定义方法
function showLabel(label:string):void{
console.log('showLabel'+label)
}
showLabel('lll')
// 1.属性接口
// 对某一个属性进行约束
function printfLabel(label:string):void{
console.log('printfLabel' + label)
}
// 对对象属性进行约束
function showInfor(fooInfor:{foo:string}):void{
console.log(fooInfor)
}
showInfor({foo:'heiheihei'})
6.2、使用interface定义接口对象
interface FullName{
firstName:string; //注意:这里为 ;
secondName:string
}
function printName(name:FullName){
console.log(name)
// console.log(name.firstName +'--' +name.secondName,name.age) //不能使用interface中没有规定的属性
}
printName({
firstName:'da',
secondName:'opp',
})
// 赋值传入时,只能包含 interface 约束中的属性
// printName({
// firstName:'小',
// secondName:'厘子',
// age:20
// })
let aa = {
firstName:'小',
secondName:'厘子',
age:20
}
// 直接传入对象,只要对象中满足条件就可以
printName(aa)
# 使用 ? 设置可选属性
interface Boy{
name:string;
age?:number;
}
function showBoy(infor:Boy):void{
console.log(infor)
}
showBoy({
name:'小张'
})
6.3、例子:封装简易ajax
// 定义接口约束
interface Config{
type:string;
url:string;
data?:string;
dataType:string
}
function ajaxFn(config:Config){
const xhr = new XMLHttpRequest()
// open的第三个参数表示是否为异步操作
xhr.open(config.type,config.url,true)
xhr.send(config.data)
xhr.onreadystatechange = () => {
if(xhr.readyState === 4 && xhr.status == 200){
console.log('OK')
console.log(xhr.response,xhr.responseType == config.type)
}
}
}
ajaxFn({
type:'get',
url:'http://www.2twl.com/api/getnewslength',
dataType:'json',
data:''
})
6.4、函数类型接口
对方法传入的参数,以及返回值进行约束
// 例子:加密的函数类型接口
interface yuesu{
// (参数:类型):返回值类型
(key:string,value:string):string
}
let mymd5:yuesu = function(key:string,value:string):string{
// 模拟返回值
return key + value
}
console.log(mymd5('zzz','qqq'))
6.5、可索引接口
通常对对象、数组的约束(不常用
// 对数组约束
interface Myarr{
// 索引:数组中的数据类型
[index:number]:string
}
let newarr:Myarr=['ac','dc']
console.log(newarr[0])
// 对对象约束
interface Myobj{
[index:string]:string
}
let zs:Myobj = {
name:'zhangsan',
// age:12 //局限性强
}
6.6、类类型接口
对类的约束 和抽象类类似(约束/标准中的属性和方法必须实现)
interface MyAnimal{
name:string;
eat(str:string):void
}
// 让 Mydog 类实现 myanimal 接口
class MyDog implements MyAnimal{
name:string;
constructor(name:string){
this.name = name
}
eat(food:string):void{
console.log(this.name + ' is eating ' + food)
}
}
let dd1 = new MyDog('opp')
dd1.eat('fish')
6.7、接口扩展
// 接口可以继承接口
interface Aanimal{
eat():void
}
// Aperson 类接口 继承 Aanimal 类接口
interface Aperson extends Aanimal{
work():void
}
class WebMan implements Aperson{
name:string;
constructor(name:string){
this.name = name
}
eat(){
console.log(this.name + ' eat food')
}
work(){
console.log(this.name + ' is working')
}
}
let xw = new WebMan('小王')
xw.eat()
xw.work()
6.8、继承类并实现指定接口
// 定义一个类
class WorkMan{
name:string;
constructor(name:string){
this.name = name
}
code():void{
console.log(this.name + ' is coding')
}
}
// 使新类 NewWebMan 继承旧类 WorkMan ,并且实现 Aperson 接口
class NewWebMan extends WorkMan implements Aperson{
constructor(name:string){
super(name)
}
eat(){
console.log(this.name + 'is NewWebMan,eat')
}
work(){
console.log(this.name + 'is NewWebMan,work')
}
}
let nn = new NewWebMan('牛牛')
nn.eat()
nn.work()
nn.code()
七、泛型
泛型:软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可复用性。
组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时提供十分灵活的功能
在像C#和Java这样的语言中,可以使用泛型来创建和复用的组件,一个组件可以支持多种类型的数据。
这样用户就可以以自己的数据类型来使用组件
通俗理解:泛型就是解决、类、接口、方法的复用性,以及对非特定数据类型的支持
泛型:可以支持不特定的数据类型
7.1、初步使用
// 1、要求:传入和返回的数据类型一致
// T表示泛型,具体什么类型由调用的时候决定
function getData<T>(value:T):T{
return value
}
// 使用
getData<number>(123)
// getData<number>('asd') //错误用法
// 2、要求:传入和返回的数据类型不一致
function fn1<T>(value:T):any{
return '123'
}
// 使用
getData<number>(123)
7.2、泛型类
// 最小堆算法,需要同时支持返回数字和字符串两种类型,通过类的泛型来实现
# 不使用泛型
class MinClass{
public list:number[] =[];
constructor(){
}
add(num:number){
this.list.push(num)
}
min():number{
// let minNum = Math.min(...this.list)
// return minNum
let minNum = this.list[0]
this.list.forEach((item,index) => {
if(minNum > item){
minNum = item
}
})
return minNum
}
}
# 使用泛型
class MinList<T>{
public arr:T[] = []
add(num:T){
this.arr.push(num)
}
min():T{
let minNum = this.arr[0]
this.arr.forEach((item,index) => {
if(minNum > item){
minNum = item
}
})
return minNum
}
}
// 实例化泛型类,并指定数据类型
let a1 = new MinList<number>()
a1.add(1)
a1.add(5)
a1.add(4)
a1.add(2)
a1.add(0)
console.log(a1.min())
// 实例化泛型类,并指定数据类型
let a2 = new MinList<string>()
a2.add('a')
a2.add('d')
a2.add('t')
a2.add('h')
a2.add('d')
console.log(a2.min())
7.3、泛型接口
// 1-第一种方法
// 普通函数接口
interface ConfigFn{
(value1:string,value2:string):string
}
let setData:ConfigFn = (value1:string,value2:string):string => {
return value1 + value2
}
// 泛型函数接口
interface ConfigFn2{
<T>(value:T):T
}
let getData2:ConfigFn2 = <T>(value:T):T => {
console.log(value)
return value
}
getData2<string>('sad')
// 2-第二种方法
interface ConfigFn3<T>{
(value:T):T
}
let getFn3 = <T>(value:T):T => {
return value
}
let myFn3:ConfigFn3<string> = getFn3
myFn3('asd')
八、接口、泛型综合使用
约束规范使用定义接口,代码复用使用泛型
// 1-普通接口
// interface Dbi{
// add():boolean;
// get():any[];
// updata():boolean;
// delete():boolean
// }
// 2-增加泛型
interface DBI<T>{
add(data:T):boolean;
get(id:number):any[];
updata(data:T,id:number):boolean;
delete(id:number):boolean
}
// 3-定义一个操作mysql数据库的类
// 注意:要实现泛型接口,这个类也应该是一个泛型类
class MysqlDB<T> implements DBI<T>{
constructor(){
console.log('连接数据库,在构造函数中进行连接')
}
add(data: T): boolean {
console.log(data)
return true
// throw new Error("Method not implemented.");
}
get(id: number): any[] {
throw new Error("Method not implemented.");
}
updata(data: T, id: number): boolean {
throw new Error("Method not implemented.");
}
delete(id: number): boolean {
throw new Error("Method not implemented.");
}
}
// myssl
class MysslDB<T> implements DBI<T>{
...
}
// 4-定义一个User类
class User{
username:string | undefined;
password:string | undefined;
}
let u = new User()
u.username = 'bob111'
u.password = '12345'
// 可以将类传入<>中作为约束条件
let oMysql = new MysqlDB<User>()
oMysql.add(u)
# 理解:以上方法可以轻松切换操作不同数据库(前提均已连接数据库)
九、模块
9.1、概念
模块的的概念(官方):
关于术语的一点说明:请务必注意一点,TypeScript 1.5里术语名已经发生了变化。“内部模块*现在称做"命名空间”。
“外部模块”现在则简称为“模块”模块在其自身的作用域里执行,而不是在全局作用域里;
这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确地使用export形式之一导出它们。
相反,如果想使用其它模块导出的变量,函数,类,接口等的时候,你必须要导入它们,可以使用import形式之一。
模块的概念(通俗理解):
我们可以把一些公共的功能单独抽离成一个文件作为一个模块。、
模块里面的变量函数类等默认是私有的,如果我们要在外部访问模块里面的数据(变量、函敞、类),
我们需要通过export暴露模块里面的数据(变量、函数、类...)。
暴露后我们通过import 引入模块就可以使用模块里面暴露的数据(变量、函数、类...)。
9.2、导出和引入
#// 导出
// 1、直接导出
export function aFn():void{
console.log('aFn')
}
let a = 'asd'
function bFn():void{
console.log('aFn')
}
// 2、统一导出
export {
a,bFn
}
// 3、export default
// 默认导出,每个模块中只能使用一次
#// 引入
//1、普通引入
// import { aFn,bFn } from "./08-mokuai"
// 2、别名引入
// import { aFn as a } from "./08-mokuai"
注意:编译之后会由ES6规范 变为 CommonJS规范,须在nodeJS环境下运行
可通过webpack等工具配置后在浏览器运行
十、命名空间
10.1、概念
命名空间:
在代码量较大的情况下,为了避免各种变量名相冲突,可将相似功能的函数、类、接口等放置到命名空间内
同Java的包,.net的命名空间一样,TypeScript的命名空间可以将代码包裹起来,只对外暴露要在外部访问的对象。
命名空间内的对象通过export导出
命名空间和模块的区别:
命名空间:内部模块,注意用于组织代码,避免命名冲突
模块:ts的外部模块的简称,侧重于代码的复用,一个模块里可能有多个命名空间
10.2、使用
// 定义命名空间A
// 命名空间中的方法默认为私有,想在外部使用需先export暴露
namespace A {
interface Animal{
name:string;
eat():void
}
class Dog implements Aanimal{
name:string;
constructor(name:string){
this.name = name
}
eat():void{
console.log(this.name + ' eat food')
}
}
export class Cat implements Aanimal{
name:string;
constructor(name:string){
this.name = name
}
eat():void{
console.log(this.name + ' eat fish')
}
}
}
// 使用 命名空间名.暴露属性名 进行访问
let c1 = new A.Cat('小花猫')
c1.eat()
// 命名空间B
namespace B {
interface Animal{
name:string;
eat():void
}
class Dog implements Aanimal{
name:string;
constructor(name:string){
this.name = name
}
eat():void{
console.log(this.name + ' eat food')
}
}
export class Cat implements Aanimal{
name:string;
constructor(name:string){
this.name = name
}
eat():void{
console.log(this.name + ' eat fish')
}
}
}
let c2 = new B.Cat('小花猫B')
c2.eat()
10.3、暴露命名空间
// 暴露命名空间C
export namespace C{
}
十一、装饰器
11.1、概念
# 不太理解实际使用,需后续学习
装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。
通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。
常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)
装饰器是过去几年中js最大的成就之一,已是Es7的标准特性之一
11.2、类装饰器
类装饰器在声明之前被声明(紧靠着类声明)。类装饰器应用于类构造函数,可以用来监视、修改或替换类定义
传入一个参数
# 普通装饰器(没有参数
// 1、声明装饰器
function zsFn(params:any){
console.log(params)
// params就是使用装饰器的当前类
// 在不修改类的前提下,扩展属性和方法
// 给当前类扩展属性
params.prototype.sex = '女'
//方法
params.prototype.sleep = () => {
console.log('sleep')
}
}
// 2、定义类,通过@使用装饰器
@zsFn
class Person{
constructor(){
}
eat(){
}
}
let p1 = new Person()
console.log(p1)
# 装饰器工厂(可传参
function zsqGC(params:string){
// return 函数中的参数为当前类
return function(target:any){
console.log(target,params)
target.prototype.addType = params
}
}
@zsqGC('修饰工厂')
class Tea{
fn(){
console.log('need water')
}
}
let tea1 = new Tea()
console.log(tea1)
11.3、属性修饰器
// 类修饰器
function ObjFn(params:string){
return function (target:any){
console.log(target,'类修饰器')
target.prototype.addNew = params
}
}
// 属性修饰器
function AttrFn(params:string){
return function(target:any,attr:any){
console.log(target,attr,'属性修饰器')
target[attr] = params
}
}
@ObjFn('olo')
class TestObj{
@AttrFn('llllllllll')
name:string | undefined;
constructor(){
this.name = 'deeeeee'
}
getName(){
console.log(this.name)
}
}
let t2 = new TestObj()
console.log(t2)
11.4、方法装饰器
它会被应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义。
方法装饰会在运行时传入下列3个参数:
1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
2、成员的名字。
3、成员的属性描述符。
// 修饰器
function Fn3(params:string){
return function(target:any,methodName:any,desc:any){
console.log(target,'方法装饰器')
console.log(methodName,'方法装饰器')
console.log(desc,'方法装饰器')
target.name = 'okk'
target.run = () => {
console.log('run!!!')
}
}
}
// 类
class MethodFn{
public name:string | undefined
constructor(){
}
// 使用修饰器
@Fn3('www.sex.bb')
getData(){
console.log(this.name)
}
eat(){
console.log(this.name + 'eat')
}
}
let m3 = new MethodFn()
console.log(m3)