一、TypeScript是什么?
TypeScript 是一种由微软开发的自由和开源的编程语言,它是 JavaScript 的一个超集,扩展了 JavaScript 的语法。
二、使用步骤
1.TypeScript 安装
在使用npm之前须安装node.js
npm install -g typescript
或者
cnpm install -g typescript
或者
yarn install -g typescript
2.运行编译
安装完成后我们就可以使用 TypeScript 编译器,名称叫 tsc,可将编译结果生成 js 文件。
要编译 TypeScript 文件,可使用如下命令:
tsc filename.tsc
一旦编译成功,就会在相同目录下生成一个同名 js 文件,你也可以通过命令参数来修改默认的输出名称。
默认情况下编译器以ECMAScript 3(ES3)为目标但ES5也是受支持的一个选项。TypeScript增加了对为即将到来的ECMAScript 6标准所建议的特性的支持。
三、变动内容总结
1、数据类型
布尔类型 boolean
数字类型 number
字符串类型 string
数组类型 array
元组类型 tuple
枚举类型 enum
任意类型 any
null 和 undefined
void类型
never类型
// boolean
var temp:boolean = true
// string
var a:string = '你好,ts'
// number可以定义浮点型
var a: number = 123
var a1: number = 12.3
// array两种写法
let arr:number[] = [1,2,3]
let arr1:Array<number> = [1,2,3]
// 元数组是数组的一种
let arr:[number,string,boolean] = [1,'123',true]
// enum 如果没有赋值,打印是索引值
enum Color1 { red, green, blue };
var c: Color1 = Color1.red
console.log(c) //0
enum flag { success = 3, error };
var a: flag = flag.success
var b: flag = flag.error
console.log(a, b) //3 4
// any
var oBox: Object= document.getElementById('box') // 没有Object类型会报错
var oBox: any = document.getElementById('box')
oBox.style.color = 'red'
// null undefined 或者可能更多类型
var num: number | null | undefined;
num = 1234
console.log(1234)
// void类型: typescript中的void表示没有类型,一般用于定义方法时没有返回值
// es5写法
function run() {
console.log('run')
}
run()
// ts
function run(): void {
console.log('run')
}
run();
// 如果有返回值
function run(): number {
return 123
}
run()
// never类型:是其他类型(包括null和undefined)的子类型,代表从不会出现的值。
// 这意味着声明never的变量只能被never类型所赋值,可以用any代替
var a: never;
var a= 123 // 报错
var a = (() => { throw new Error('错误') }) // 正常
2、函数
1、函数定义(对比es5中函数声明和匿名函数、添加传参和没有返回值写法)
//es5
//函数声明
function run(param){
return '123'
}
//匿名函数
var fun1 = function(){
return '111'
}
//ts中
//函数声明 返回值需指定类型
function run():string{
return '123'
}
//匿名函数
var run2 = function():number{
return 123
}
//ts中定义方法传参
function getInfo(name:string,age:number):string{
return '123'
}
getInfo('zhangsan', 20)
//ts中没有返回值的方法
function getInfo():void{
console.log('123')
}
2、方法可选参数
// es5中形参和实参不一样,但ts中必须保持一致,如果不一样需要配置可选参数
function getInfo(name:string,age:number) {
console.log(name,age)
}
getInfo('zhangsan') // 报错,因为未传入age参数
// 设置可选参数
function getInfo(name:string,age?:number) {
console.log(name,age)
}
getInfo('zhangsan') // 'zhangsan'
// 需要注意的是可选参数需要配置到参数的最后面
function getInfo(name?:string,age:number) {
console.log(name,age)
}
getInfo('zhangsan') // 'zhangsan'可以运行,但ts中不建议这样写
3、默认参数
function getInfo(name:string, age:number=20) {
console.log(name + '-' + age)
}
getInfo('zhangsan') // 'zhangsan-20'
getInfo('zhangsan', 30) // 'zhangsan-30'
4、剩余参数
function sum(...arr:number[]):number {
let sum = 0
for(var i = 0; i < arr.length; i++){
sum+=arr[i]
}
return sum
}
sum([1,2,3,4]) // '10'
// 另一种写法
function sum(a:number, b:number, ...arr:number[]):number {
let sum = a + b
for(var i = 0; i < arr.length; i++){
sum+=arr[i]
}
return sum
}
sum([1,2,3,4,5,6]) // '21'
5、函数重载
java中的重载是指两个或两个以上同名函数,但他们的参数不一样,这时会出现函数重载的情况;
ts中的重载是指通过为同一个函数提供多个函数类型定义来实现多种功能的目的;
ts为了兼容es5和es6重载的写法与java有区别
// es5中出现同名方法,下面的会替换上面的方法
function sum(){
return '123'
}
function sum(){
return '456'
}
// ts中写法 但是编译成es5后还是一个函数
function sum(name:string):string{
return '123'
}
function sum(age:number):string{
return '20'
}
function sum(str:any):any{
if (typeof str === string) {
return '111111'
} else {
return '222222'
}
}
sum('zhangsan') // 111111
sum(30) // 22222
6、箭头函数
箭头函数中this指向上下文
setTimeout(function(){
consolr.log('run')
}, 1000)
// 箭头函数写法
setTimeout( () => {
consolr.log('run')
}, 1000)
3、类
1、es5中的类、属性、实例方法、静态方法等实现
// es5中最简单的类
function Person {
this.name = '张三'
}
var p1 = new Person() // 实例化
console.log(p1.name) // '张三'
// 往构造函数和原型链里面添加方法
function Person {
this.name = '张三',
this.run = function(){
console.log(this.name + '在运动')
}
}
// 原型链上的属性会被多个实例共享,构造函数不会
Person.prototype.sex = '男'
Person.prototype.work = function(){
console.log(this.name + '在工作')
}
var p1 = new Person() // 实例化
p1.run() // '张三在运动'
p1.work() // '张三在工作'
// 类里面的静态方法
function Person {
this.name = 'zhangsan',
// 实例方法
this.run = function(){
console.log(this.name + '在运动')
}
}
// 静态方法
Person.getInfo = function(){
console.log('我是静态方法')
}
// 调用静态方法
Person.getInfo(); // '我是静态方法'
2.es5中类的继承
function Person {
this.name = '张三',
this.run = function(){
console.log(this.name + '在运动')
}
}
// 原型链上的属性会被多个实例共享,构造函数不会
Person.prototype.sex = '男'
Person.prototype.work = function(){
console.log(this.name + '在工作')
}
var p1 = new Person() // 实例化
// 实现web类来继承Person类
// 对象冒充实现继承 可以继承构造函数里的属性和方法,但没法继承原型链上的属性和方法
function Web() {
Person.call(this)
}
var w1 = new Web()
w1.run() // '张三在运动'
w1.work() // work is a not function
// 原型链实现继承 可以继承构造函数里的属性和方法,也可以继承原型链上的属性和方法
function Web() {
}
Web.prototype = new Person()
var w1 = new Web()
w1.run() // '张三在运动'
w1.work() // '张三在工作'
// 原型链上继承存在问题 实例化子类的时候没法给父类传参
function Person(name) {
this.name = name,
this.run = function(){
console.log(this.name + '在运动')
}
}
function Web() {
}
Web.prototype = new Person()
var w1 = new Web('赵四')
w1.run() // 'undefined在运动'
// 原型链和构造函数组合继承方式
function Person(name) {
this.name = name,
this.run = function(){
console.log(this.name + '在运动')
}
}
function Web(name) {
Person.call(this, name)
}
Web.prototype = new Person()
var w1 = new Web('赵四')
w1.run() // '赵四在运动'
w1.work() // '赵四在工作'
// 原型链和构造函数组合继承的另一种方式
function Person(name) {
this.name = name,
this.run = function(){
console.log(this.name + '在运动')
}
}
function Web(name) {
Person.call(this, name)
}
Web.prototype = Person.prototype
var w1 = new Web('赵四')
w1.run() // '赵四在运动'
w1.work() // '赵四在工作'
3、ts中的类、属性、实例方法等实现
// ts中定义类
class Person {
name:string; // 属性 前面省略了public关键词
constructor(name:string) { // 构造函数,实例化类的时候触发的方法
this.name = name
}
getName (name:string): string {
console.log(this.name)
}
setName (name:string):void {
this.name = name
}
}
var p1 = new Person('张三')
p1.getName() // '张三'
p1.setName('李四')
p1.getName() // '李四'
4、ts中类的继承
// ts中继承类 借助extends super
class Person {
name:string; // 属性 前面省略了public关键词
constructor(name:string) { // 构造函数,实例化类的时候触发的方法
this.name = name
}
run (): void{
console.log(this.name + '在运动')
}
}
class Web extends Person {
constructor(name:string) { // 初始化父类构造函数
super(name)
}
}
var p1 = new Web('张三')
p1.run() // '张三在运动'
// ts中父类和子类有相同方法探讨
class Person {
name:string; // 属性 前面省略了public关键词
constructor(name:string) { // 构造函数,实例化类的时候触发的方法
this.name = name
}
run (): void {
console.log(this.name + '在运动')
}
}
class Web extends Person {
constructor(name:string) { // 初始化父类构造函数
super(name)
}
run (): void {
console.log(this.name + '在运动-子类')
},
work (): void { // 子类可扩展自己的方法
console.log(this.name + '在工作')
}
}
var p1 = new Web('张三')
p1.run() // '张三在运动-子类' 有相同的方法会先从子类中找,子类中没有再去找父类
5、ts中类的修饰符
typescript中定义属性的时候提供了三种修饰符;
public: 公有属性 在当前类里面、子类、类外面都可以正常访问
protected 保护属性 在当前类里面、子类可以正常访问,类外部无法访问
private 私有属性 在当前类里面可以访问,子类和类外部都无法访问
属性如果不加修饰符默认就是公有(public)
// public
class Person {
public name:string; // 属性 前面省略了public关键词
constructor(name:string) { // 构造函数,实例化类的时候触发的方法
this.name = name
}
run (): void{
console.log(this.name + '在运动')
}
}
class Web extends Person {
constructor(name:string) { // 初始化父类构造函数
super(name)
},
work ():void {
console.log(this.name + '在运动')
}
}
var p1 = new Person('张三')
p1.run() // '张三在运动'
var p2 = new Web('李四')
p2.work() // '李四在运动'
console.log(p1.name) // '张三'
// protected
class Person {
protected name:string; // 属性 前面省略了public关键词
constructor(name:string) { // 构造函数,实例化类的时候触发的方法
this.name = name
}
run (): void{
console.log(this.name + '在运动')
}
}
class Web extends Person {
constructor(name:string) { // 初始化父类构造函数
super(name)
},
work ():void {
console.log(this.name + '在运动')
}
}
var p1 = new Person('张三')
p1.run() // '张三在运动'
var p2 = new Web('李四')
p2.work() // '李四在运动'
console.log(p1.name) // '张三' 虽然可以得到结果,是因为编译成es5可以通过,ts不建议这种写法
// private
class Person {
private name:string; // 属性 前面省略了public关键词
constructor(name:string) { // 构造函数,实例化类的时候触发的方法
this.name = name
}
run (): void{
console.log(this.name + '在运动')
}
}
class Web extends Person {
constructor(name:string) { // 初始化父类构造函数
super(name)
},
work ():void {
console.log(this.name + '在运动')
}
}
var p1 = new Person('张三')
p1.run() // '张三在运动'
var p2 = new Web('李四')
p2.work() // 报错
console.log(p1.name) // 报错
6、ts中类的静态属性、静态方法、抽象类
// jquery封装的静态方法和实例方法的实现
function $() {
return new Base()
}
function (element) {
this.element = '获取dom节点'
this.css = function (arr, value) {
this.element.style.arr = value
}
}
$.get('url', function(){}) // jq中静态方法
$('box').css('color', 'red') // jq中实例方法
// ts类的静态方法
class Person {
public name:string
public age:number = 20
// 静态属性
static sex:string = '男'
constructor (name: string) {
this.name = name
},
// 实例方法
run () {
console.log(this.name + '在运动')
}
// 实例方法
work () {
console.log(this.name + '在工作')
},
// 静态方法
static print () {
console.log('print')
}
// 静态方法无法直接调用类里面的属性
static print1 () {
console.log('print方法' + this.age)
}
// 静态方法只能访问类里面的静态属性
static print2 () {
console.log('print方法' + Person.sex)
}
}
var p = new Person()
// 静态方法调用
Person.print() // 'print方法'
Person.print1() // 'print方法undefined'
console.log(Person.sex) // '男'
Person.print2() // 'print方法男'
7、多态
多态:父类定义一个方法不去实现,让继承他的子类去实现,每一个子类有不同的表现
多态属于继承
class Animal () {
name:string
constructor (name:string) {
this.name = name
},
eat () {
console.log('eat')
}
}
class Dog extend Animal () {
name:string
constructor (name:string) {
super(name)
},
eat () {
console.log(this.name + '吃粮食')
}
}
class Cat extend Animal () {
name:string
constructor (name:string) {
super(name)
},
eat () {
console.log(this.name + '吃老鼠')
}
}
8、抽象类
typescript中的抽象类,他是提供其他继承的基类,不能直接被实例化;
用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现;
抽象方法只能放在抽象类中;
抽象方法和抽象类用于定义标准: 标准animal这个类要求它的子类必须包含eat方法
abstract class Animal {
public name:string
abstract eat ():any
}
var a1 = new Animal() // 报错 抽象类不能被实例化
class Dog extend Animal {
constructor () {
super(name)
}
// 抽象类的子类必须实现抽象类中的抽象方法
eat () {
console.log(this.name + '吃东西')
}
}
class Cat extend Animal {
constructor () {
super(name)
}
// 抽象类的子类必须实现抽象类中的抽象方法
eat () {
console.log(this.name + '吃东西')
}
}
var dog = new Dog('小黑')
dog.eat() // '小黑吃东西'
var cat = new Cat('小花')
dog.eat() // '小花吃东西'
四、接口(interface)
在面向对象编程中, 接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范的作用。接口定义了某一批类所要遵守的规范,接口不关心这些类的内部状态数据,也不关系这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些的方法的类就可以满足实际需要。typescript中的接口类似于java,同时还增加了更加灵活的接口类型,包括属性、函数、可索引和类等
1、属性接口(对传入对象的约束)、可选属性(?:)
// 属性接口
interface labelInfo {
firstName: string; // 以分号结尾
secondName: string;
}
function label (name:labelInfo) {
console.log(name.firstName+ '---' + name.secondName)
}
// 这种写法传入的对象必须只能有接口中规范的字段 顺序可不一样
label({age: 20, firstName: '张', secondName: '三'})
// 上述可替换成下面写法 可增加接口规范以外的字段
var obj1 = {age20, firstName: '张', secondName: '三'}
label(obj1)
// 对批量方法进行约束
function print (info:labelInfo) {
console.log(info.firstName+ '---' + info.secondName + info.age)
}
print ({age: 20, firstName: '李', secondName: '四'}) // 报错 age属性接口中未定义
var obj = {age: 20, firstName: '李', secondName: '四'}
print ({age: 20, firstName: '李', secondName: '四'}) // 报错 age属性接口中未定义
print ({firstName: '李', secondName: '四'}) // '李四' 方法中只能使用接口规范的参数
// 可选属性
interface nameInfo {
firstName: string; // 以分号结尾
secondName?: string;
}
function getName (name:nameInfo) {
console.log(name.firstName+ '---' + name.secondName)
}
getName({firstName: '王五'}) // '王五---'
// ajax可选属性实现
interface config {
type:string;
url:string;
data?:string;
}
function ajax(obj:config) {
var xhr = new XMLHttpRequest()
xhr.open(obj.url, obj.type, true)
xhr.send(obj.data)
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200){
console.log('成功')
console.log(xhr.responseText)
}
}
}
ajax ({
type: 'get',
url: '接口地址'
})
2、函数类型接口
对方法传入的参数和返回值进行约束 批量约束
// 加密的函数类型接口
interface encrypt {
(key:string, value:string):string
}
var md5:encrypt = function (a:string, b:string):string {
return a + b
}
console.log(md5('123', '456')) // '123456'
3、可索引接口
对数组、对象的约束,不常用
// 对数组的约束
interface userArr {
[index:number]:string
}
var arr1:userArr = ['123', '456'] // 数组索引是number类型
// 对对象的约束
interface userObj {
[index:string]:string
}
var arr2:userObj = {name: '张三'} // 数组索引是number类型
4、类类型接口
对类的约束,跟抽象类有点相似
// 类接口 借助implements实现
interface Animal {
name: string;
eat(str: string):void
}
class Dog implements Animal {
name: string
constructor (name: string) {
this.name = name
},
eat () {
console.log(this.name + '吃粮食')
}
}
class Cat implements Animal {
name: string
constructor (name: string) {
this.name = name
},
eat () {
console.log(this.name + '吃老鼠')
}
}
var dog = new Animal()
dog.eat('小黑') // '小黑吃粮食'
var cat = new Animal()
cat.eat('小花') // '小花吃老鼠'
5、接口扩展
接口可以继承接口
interface Animal {
eat ():void
}
interface Person extend Animal {
work ():void
}
// 接口继承接口,实例方法必须满足两个接口约束
class Web1 implements Person {
name:string
constructor (name:string) {
this.name = name
},
eat () {
console.log(this.name + '吃粮食')
}
work () {
console.log(this.name + '在工作')
}
}
var web = new Web1('小李')
web.eat() // 小李吃粮食
web.work() // 小李在工作
// 继承类+接口继承接口的复杂写法
class Programmer {
public name:string | undefined
constructor (name:string) {
this.name = name
},
coding (code:string) {
console.log(this.name + code)
}
}
class Web extend Programmer implements Person {
constructor (name:string) {
super(name)
}
eat () {
console.log(this.name + '吃粮食')
}
work () {
console.log(this.name + '在工作')
}
}
var web = new Web('小李')
web.coding('写ts代码') // '小李写ts代码'
五、泛型
软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
在像c#和java这类的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。这样用户就可以用自己的数据类型来使用组件。
通俗理解: 泛型就是解决类、接口、方法的可复用性,以及对不特定数据类型的支持。
1、泛型函数
// 传入string类型返回string,传入number返回number
// 第一种方式 代码冗余
function getData1(str: string): string {
return '123'
}
function getData2(str: number): number {
return 123
}
// 第二种方式 传入类型和返回类型不一致 使用any放弃了类型检查
function getData3(str:any):any {}
// 第三种方式 泛型可以支持不特定的类型 要求传入的参数和返回的参数类型一致
function getData<T>(str:T):T {
return str
}
getData<number>(123) // true
getData<number>('123') // false
getData<string>('123') // true
2、泛型类
// 实现最小堆算法 需要同时支持返回数字和字符串类型
// 普通版
class PushList1 {
public list:number[] = []
add (val:number) {
this.list.push(val)
}
min ():number {
var minNum = this.list[0]
for (var i = 0; i < this.list.length; i++) {
if (minNum > this.list[i]) {
minNum = this.list[i]
}
}
return minNum
}
}
var p = new PushList1()
p.add(1)
p.add(3)
p.add(5)
console.log(p.min()) // 1 只能判断number类型
// 泛型类版
class PushList<T> {
public list:T[] = []
add (val:T):void {
this.list.push(val)
}
min ():T {
var minNum = this.list[0]
for (var i = 0; i < this.list.length; i++) {
if (minNum > this.list[i]) {
minNum = this.list[i]
}
}
return minNum
}
}
var p1 = new PushList1<number>()
p1.add(1)
p1.add(3)
p1.add(5)
console.log(p1.min()) // 1
var p2 = new PushList<string>()
p2.add('a')
p2.add('c')
p2.add('z')
console.log(p2.min()) // a 字母通过ASCII判断大小
3、泛型接口
// 复习函数类型接口
interface List1 {
(value1: string, value2:string): string
}
var getData1:List1 = function (value1:string, value2:string):string {
return value1 + value2
}
// 泛型接口
interface List2 {
<T>(value1: T, value2:T): T
}
var getData2:List2 = function <T>(value1:T):T {
return value1
}
console.log(getData2('张三','李四')) // '张三李四'
// 泛型的另一种写法
interface List<T>{
(value1: T): T
}
function getData <T>(value1:T):T {
return value1
}
var myGetData:List<number> = getData
console.log(myGetData(20)) // '20'
4、把类作为参数的泛型类
把类作为参数来约束传入参数的类型
// 定义一个User类用来映射数据库字段,然后定义一个MyMongodb类用于操作数据库,
// 然后把user类作为参数传入到MyMongodb中
class User {
username:string | undefined
password:string | undefined
}
class MyMongodb {
add (user: User):boolean {
console.log(user)
return true
}
}
var u1 = new User()
u1.username = '张三'
u1.password = '123'
var mongodb = new MyMongodb ()
mongodb.add(u1) // {username:'张三', password:'123'}
// 泛型类实现
class MyMongodb1<T> {
add (info: T):boolean {
console.log(info)
return true
}
}
class User1 {
username:string | undefined
password:string | undefined
}
var u2 = new User1()
u2.username = '李四'
u2.password = '456'
var mongodb = new MyMongodb1<User>()
mongodb.add(u2) // {username:'李四', password:'456'}
5、ts中类型、接口、类、泛型的综合使用
// 实现:定义一个操作数据库的库, 支持mysql mssqpl mongodb
// 要求1: mysql mssqpl mongodb功能一样,并且都有add,update,delete,get方法
// 要求2: 约束统一规范,以及代码重用
// 解决方案:需要约束规范,所以需要定义接口;需要代码重用,所以用到泛型
// 1、接口:在面向对象编程中,接口是一种规范的定义,它定义了行为和动作的规范
// 2、泛型:通俗理解,泛型就是解决类、接口、方法的复用性
interface DBI<T> {
add (user: T):boolean;
update (user: T, id: number);
delete (id: number):boolean;
get (id: number):any[];
}
// 定义一个操作mysql数据库的类 要实现泛型接口,这个类也要是个泛型类
class Mysql<T> implements DBI<T> {
add(user: T): boolean {
console.log(user)
return true
}
update(user: T, id: number): boolean { return true };
delete(id: number): boolean { return true }
get (id: number):any[] {
var list = [{ title: '新闻', desc: '国内新闻'}, { title: '新闻1', desc: '国内新闻1'}]
return list
}
}
// 操作用户表 定义一个User类和数据表做映射
class User {
username: string | undefined
password: string | undefined
}
var u = new User()
u.username = '张三'
u.password = '李四'
// 把类作为参数来约束数据传入的类型
var mysql = new Mysql<User>()
mysql.add(u) // { username: '张三', password: '李四' }
// 获取id为4的数据
var data = mysql.get(4)
console.log(data) // [{ title: '新闻', desc: '国内新闻'}, { title: '新闻1', desc: '国内新闻1'}]
// Mssql、Mymongodb写法类似
六、模块
1、导出单个或多个
// 导出导入单个
export let myName = "张三";
import {myName} from "./test.js";
console.log(myName); // '张三'
// 导出多个
let name = "张三";
let age = 90;
let func = function() {
return "我是" + myName"
}
export { name, age, func }
// import
import { name, age, func } from "./test.js";
console.log(func()); // '我是张三'
console.log(age); // '90'
console.log(name); // '张三'
2、as关键词使用
// as使用
let firstName = "张三";
let minAge = 90;
let func = function() {
return "我是" + myName"
}
export { firstName as name, minAge as age, func as fn }
// import
import { name, age, fn } from "./test.js";
console.log(fn()); // '我是张三'
console.log(age); // '90'
console.log(name); // '张三'
3、default关键词使用
// default关键字
export default {
fn(){
return "默认导出一个方法"
},
name:"张三"
}
// import
import info from "./test.js";
console.log(info.fn(),info.name);//默认导出一个方法 张三
4、整体导出导入
// 整体导入
let name = "张三";
let age = 90;
let func = function() {
return "我是" + myName"
}
export { name, age, func }
// import
import * as info from "./test.js"; // 通过*来批量接收,as 来指定接收的名字
console.log(info.func()); // '我是张三'
console.log(info.age); // '90'
console.log(info.name); // '张三'
七、命名空间
命名空间: 在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放到命名空间内。同java的包、.net的命名空间一样,typescript的命名空间可以将代码包裹起来,只对外暴露在外部访问的对象,命名空间内的对象通过export导出供外部使用。
命名空间同样支持模块间的导出导入。(早期这种方式,typescript3.4.5版本已不支持)
和模块的区别:
命名空间是一个内部模块主要用于组织代码,避免命名冲突;
模块:ts外部模块的简称,侧重代码的复用,一个代码里可能有多个命名空间
namespace A {
interface Animal {
name: string;
eat(str: string):void
}
export class Dog implements Animal {
name: string
constructor (name: string) {
this.name = name
}
eat () {
console.log(this.name + '吃粮食')
}
}
}
namespace B {
interface Animal {
name: string;
eat(str: string):void
}
export class Dog implements Animal {
name: string
constructor (name: string) {
this.name = name
}
eat () {
console.log(this.name + '吃肉')
}
}
}
var dog = new A.Dog('狼狗')
dog.eat() // '狼狗吃粮食'
var dog1 = new B.Dog('小黑')
dog1.eat() // '小黑吃肉'
八、装饰器
定义:
是一种特殊类型的声明,它能够附加到类声明、方法、属性和参数上,可以修改类的行为;
通俗来讲装饰器就是一个方法,注入到类、方法、属性参数上来扩展类、方法、属性、参数的功能。
写法: 普通装饰器(无法传参), 装饰器工厂(可传参)
装饰器是js过去几年最大的成就之一,已是es7标准特性之一
1、类装饰器
类装饰器在类声明之前声明(紧跟着类声明)。类装饰器应用于类构造函数,可以用来监视、修改、替换或定义。传入一个参数
// 普通装饰器(无法传参)
function logClass (param:any) {
//params就是当前类
console.log(param)
param.prototype.apiUrl = '动态扩展的属性'
param.prototype.run = function () {
console.log('我是一个run方法')
}
}
@logClass
class HttpClient {
constructor () {
}
getData () {
}
}
var http:any = new HttpClient() // HttpClient{}
console.log(http.apiUrl) // '动态扩展的属性'
http.run() // '我是一个run方法'
// 装饰器工厂(可传参)
function logClass1 (param:any) {
return function (target: any) {
// target为当前类 param为传入参数
console.log(target, param)
target.prototype.apiUrl = 'xxx'
}
}
@logClass1('hello')
class HttpClients {
constructor () {
}
getData () {
}
}
var https:any = new HttpClients() // HttpClients{} 'hello'
console.log(https.apiUrl) // 'xxx'
// 重载构造函数的例子
// 类装饰器表达式会在运行时当作函数调用,类的构造函数作为其唯一的参数。
// 如果类装饰器返回一个值,它会使用提供的构造函数替换类的声明
function logClass2 (target:any) {
console.log(target)
return class extends target {
apiUrl:any = '我是修改后的数据'
getData () {
this.apiUrl = this.apiUrl + '---'
console.log(this.apiUrl)
}
}
}
@logClass2
class HttpClients1 {
public apiUrl:string | undefined
constructor () {
this.apiUrl = '我是构造函数里的数据'
}
getData () {
console.log(this.apiUrl)
}
}
var https:any = new HttpClients1() // HttpClients{} '我是修改后的数据---'
2、属性装饰器
属性装饰器表达式会在运行时当作函数使用,传入下列两个参数:
1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象,
2、成员的名字
function logProperty(param:string) {
return function (target:any, attr:string) {
console.log(target, attr)
target[attr] = param
}
}
class HttpClients {
@logProperty('lalala')
public url:string | undefined
constructor () {
}
getData () {
console.log(this.url)
}
}
var https:any = new HttpClients() // HttpClients{} 'url'
https.getData() // 'lalala'
3、方法装饰器
它会被应用到方法的属性描述符上,可以用来监视、修改或者替换方法定义
方法装饰器会在运行时传入3个参数
1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
2、成员的名字
3、成员的属性描述符
// 扩展属性和方法
function logMethods1 (params: string) {
return function (target: any, methodsName: any, desc:any) {
// desc.value为当前装饰的函数
console.log(target, methodsName, desc.value)
target.apiUrl = params
target.run = function () {
console.log('我是run')
}
}
}
class HttpClients1 {
public apiUrl: string | undefined
constructor() {
}
@logMethods1('lalala')
getData() {
console.log('我是getData中的方法')
}
}
var https: any = new HttpClients1() // HttpClients{} 'getData' f:getData() {console.log('我是getData中的方法')}
console.log(https.apiUrl) // 'lalala'
https.run() // '我是run'
// 替代当前装饰方法
function logMethods2 (params: string) {
return function (target: any, methodsName: any, desc:any) {
// 替换当前装饰的方法 将传入的参数都返回字符串
desc.value = function (...args:any[]) {
args = args.map((value) => {
return String(value)
})
console.log(args)
}
}
}
class HttpClients2 {
public apiUrl: string | undefined
constructor() {
}
@logMethods2('lalala')
getData(...args:any[]) {
console.log('我是getData方法')
}
}
var https: any = new HttpClients2()
https.getData(123, 'xxx') // ['123', 'xxx']
// 修改当前装饰方法
function logMethods (params: string) {
return function (target: any, methodsName: string, desc:any) {
// 替换当前装饰的方法 将传入的参数都返回字符串
var oMethods = desc.value
desc.value = function (...args:any[]) {
args = args.map((value) => {
return String(value)
})
console.log(args)
// 将args作为参数重新传入getData
oMethods.apply(this, args)
}
}
}
class HttpClients {
public apiUrl: string | undefined
constructor() {
}
@logMethods('lalala')
getData(...args:any[]) {
console.log(args)
console.log('我是getData方法')
}
}
var https: any = new HttpClients()
https.getData(123, 'xxx') // ['123', 'xxx'] ['123', 'xxx'] '我是getData方法'
4、方法参数装饰器
方法参数装饰器表达式会在运行时当作函数使用,可以为类的原型增加一些元素数据,传入下列3个参数:
1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象,
2、方法的名字
3、参数在函数参数列表中的索引
function logParams (params: any) {
return function (target:any, methodsName:any, paramsIndex:any) {
console.log(params, target, methodsName, paramsIndex)
target.apiUrl = 'xxx'
}
}
class HttpClients {
public apiUrl: string | undefined
constructor() {
}
getData(@logParams('lalala') uuid:any) {
console.log(uuid)
}
}
var http = new HttpClients() // 'lalala' HttpClients {} 'getData' 0
console.log(http.apiUrl) // 'xxx'
http.getData('123456') // 执行顺序为 'lalala' HttpClients {} 'getData' 0 '123456'
5、类、属性、方法、方法参数装饰器的执行顺序
执行顺序:属性>方法>方法参数>类
如果有多个同样的装饰器,它回先执行后面的
function logClass1(params:string) {
return function (target: any) {
console.log('类装饰器1')
}
}
function logClass2(params:string) {
return function (target: any) {
console.log('类装饰器2')
}
}
function logProperty(params:string) {
return function (target: any, attrName: any) {
console.log('属性装饰器')
}
}
function logMethods(params?:string) {
return function (target: any, methodsName: any, desc: any) {
console.log('方法装饰器')
}
}
function logParams1(params:string) {
return function (target: any, methodsName: any, paramsIndex: any) {
console.log('方法参数装饰器1')
}
}
function logParams2(params:string) {
return function (target: any, methodsName: any, paramsIndex: any) {
console.log('方法参数装饰器2')
}
}
@logClass1('111')
@logClass2('222')
class HttpClients {
@logProperty('333')
public apiUrl: string | undefined
constructor() {
}
@logMethods()
getData(@logParams1('444') uuid:any, @logParams2('555') uuids:any) {
return true
}
}
var http = new HttpClients()
// '属性装饰器' '方法装饰器' '方法参数装饰器2' '方法参数装饰器1' '类装饰器2' '类装饰器1'
总结
本文总结了ts3.x的学习内容,希望对大家会有所帮助。
参考视频: Typescript教程_Typescript视频教程 ts入门实战视频教程-2021年更新 包含Vue3+Ts_哔哩哔哩_bilibili