类型别名
类型别名顾名思义,即字面意思,其实断言
也是字面意思,就是断定类型的方法,你说是什么类型就是什么类型,推翻约定,扯远了,继续说类型别名
,举个🌰吧:
type Name = string; type NameResolver = () => string; type NameOrResolver = Name | NameResolver; function getName(n: NameOrResolver): Name { if(typeof n === 'string') { return n } else { reutrn n() } } 复制代码
类型Name
其实就是string
的别名,类型() => string
,一个函数返回一个字符串,这种格式就是类型NameResolver
,NameOrResolver
是一个联合类型,之前说过。
字符串字面量类型
字符串字面量类型,用来约束取值职能是某几个字符串
其中的一个字符串,举个🌰:
type EventSupport = 'click' | 'scroll' | 'mouseEnter' function handleEvent(ele: Element, event: EventSupport): void { // do something } handleEvent(document.getElementById('app'), 'scroll') // 完全ok handleEvent(document.getElementById('app'), 'dbclick') // 完全不ok 复制代码
元组(Tuple)
元组用来合并不同类型的项,举个🌰:
let tom: [string,number] = ['tom', 25] 复制代码
注意:
- 元组在定义赋值时,你定义的什么类型,初始赋值时,就得添加什么类型的值,必须全部添加完,不能多,也不能少
- 可以利用下标修改值,但是值必须是相同类型的
- 元组可以越界,越界的元素只能是你定义元组的时候的联合类型,不能是其他类型,越界的元素不能修改
.. 元组不是很好用,如果你真的不确定你的[]
里有啥,其实最好就用let tom: any[] = ['tom', 12]
枚举(Enum)
枚举一般用来做映射,举个栗子:
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat} 复制代码
上面这段Ts编译成Js是这样:
var Days; (function (Days) { Days[Days["Sun"] = 0] = "Sun"; Days[Days["Mon"] = 1] = "Mon"; Days[Days["Tue"] = 2] = "Tue"; Days[Days["Wed"] = 3] = "Wed"; Days[Days["Thu"] = 4] = "Thu"; Days[Days["Fri"] = 5] = "Fri"; Days[Days["Sat"] = 6] = "Sat"; })(Days || (Days = {})); 复制代码
枚举成员会被赋值为从0开始递增的数字,同事也会对枚举值到枚举名进行反向映射:
console.log(Days[0]); // Sun console.log(Days["Sun"]) // 0 复制代码
自定义枚举
enum Days { Sun = <any>"Sun", Mon = <any>"Mon", Tue = <any>"Tue", Wed = <any>"Wed", Thu = <any>"Thu", Fri = <any>"Fri", Sat = <any>"Sat", } console.log(Days["Sun"]); // Sun 复制代码
注意:这儿需要使用断言,让tsc无视类型检查
类
其实在JS在ES6发布之前从来都没有类的概念,都是通过构造函数去模拟类,ES6发布了class,但是大部分Js程序员对类的概念还是比较模糊的,这我解释一下什么是类、对象、OOP、封装、集成、多态、修饰符、抽象类、接口
类的概念
类
其实可以理解为一件事物的抽象,包含了这个事务的一些属性与方法举个简单的🌰,比如人,人就是一个大类,我们可以抽象出来他的一些特点,比如:唱
、跳
这是人的行为,智商
、情商
、性别
等是人的属性。
对象
对象
其实就是类的实例化,类
是一个抽象,对象
就是让他变得现实,一个类可以实例化多个对象,类似我们可以根据人
这个类,制造很多人。
面向对象 OOP
面向对象开发的三大特性:封装、继承、多态
封装
封装
的意思就是我们知道的意思,我们需要通过一些代码实现一个函数,这个函数就是一个封装,再通俗一点说,我们需要实现人
会跳
这个方法,细节呢?我们只需要封装起来,比如先下蹲一点,双腿发力,向上用力等等,把这些细节封装起来,就可以直接调用这个方法进行跳
,同时呢外界也不能直接去修改内部的数据。
继承
子承父业,儿子继承老爹的所有方法&属性,但除了拥有父类所有用的特性外,还有一些别具一格的特性。
多态
由于继承产生了很多相关的不同类,很多儿子继承了同一个老爹,子类对同一个方法可以有不同的响应。比如小王
和小李
都继承老爹老张
,但是分别实现了自己getMoney
的方法,此时针对一个实例,我们无需了解他是小王
还是小李
,就可以直接调用getMoney
方法,程序会自动判断出来应该如何执行getMoney
。
修饰符
修饰符是一些关键字,用于限定成员或者类型的性质,比如public
表示公有属性or方法
抽象类
抽象类是供其他类继承的基类
,抽象类不允许被实例化,抽象类中的抽象方法必须在子类中被实现
接口
不同类之间共有的属性和方法,可以抽象成一个接口,接口可以被类实现,一个类职能继承自另外一个类,但是可以实现多个接口。
ES6中类的用法
类的定义
使用class
定义类,使用constructor
定义构造函数,通过new
生成新实例的时候,会自动调用构造函数。
class Person { public IQ; publick name; constructor(IQ, name) { this.IQ = IQ this.name = name } saySomething() { return `hello, im ${this.name}, IQ: ${this.IQ}` } } let alice = new Person("Alice", 120) console.log(alice.saySomething()) 复制代码
类的继承
现在有了人的类,我们现在实现一个Cop
类,Cop
也属于人,也有名字&IQ,子类中用super
关键字来调用父类的构造函数与方法:
class Cop extends Person { public job; constructor(IQ, name, job) { super(IQ, name) this.job = job; } saySomething() { return super.saySomething() + 'my job js ${this.job}' } } let tom = new Cup('tom', 100, 'cop') console.log(tom.saySomething()) 复制代码
存取器
使用 getter 和 setter可以改变属性的赋值和读取行为:
class FakerCop extends Person { constructor(IQ, name, job) { super(IQ, name) this.job = job } get job() { return 'fakerCop' } set job(val) { console.log('u want my job is' + value + '?') } } let tony = new FakerCop("Tony", 200, 'fakerCop'); tony.job = 'realCop' console.log(tony.job) // fakerCop 复制代码
静态方法
使用static
修饰符修饰的方法成为静态方法,不需要被实例化,直接通过类来调用,举个🌰,定义一个判断真假cop的方法:
class Cop extends Person { public job; constructor(IQ, name, job) { super(IQ, name) this.job = job; } static isCop(c) { return c instanceof Cop; } saySomething() { return super.saySomething() + 'my job js ${this.job}' } } console.log(Cop.isCop(tom)) // true console.log(Cop.isCop(tony)) // false 复制代码
TypeScript中类的用法
public private protected
public
修饰的属性或者方法是公有的,可以在任何地方被访问,默认所有的方法和属性都是public
private
修饰的属性或者方法是私有的,不能再声明他的类的外部访问protected
修饰的属性或者方法是受保护的,他和private
类似,不同的是,它在子类中是可以访问的
public
举个栗子:
class Animal { public name; public constructor(name) { this.name = name } } let a = new Animal('Jack') console.log(a.name) // Jack a.name = 'Tom' console.log(a.name) // Tom 复制代码
private
举个栗子:
class Animal { private name public constructor(name) { this.name = name } } let a = new Animal('Jack') console.log(a.name) a.name = 'Tom' 复制代码
ts 会提示属性“name”为私有属性,只能在类“Animal”中访问。
子类中也不能访问,这里就不说了;
protected
举个栗子:
class Animal { protected name; public constructor(name) { this.name = name } } class Cat extends Animal { constructor(name) { super(name); console.log(this.name) } } 复制代码
子类可以访问父类的protected
属性
当构造海曙为protected
时,这个类只能继承,不能被实例化:
class Animal { protected name; protected constructor(name) { this.name = name } } class Cat extends Animal { constructor(name) { super(name) } } let a = new Animal('Jack') 复制代码
参数属性
修饰符和readonly
还可以在构造函数参数中使用,等同于类中定义该属性同时给该属性赋值,代码看上去会更简洁:
class Animal { // public name: string; public constructor(public name) { // this.name = name } } 复制代码
readonly
只读关键字
class Animal { public constructor(readonly name) { } } let a = new Animal('Jack') console.log(a.name) a.name = 'Tony' // Error 复制代码
需要注意的是,如果readonly与其他修饰符同时出现的时候,需要写在最后,改造上面的栗子:
class Animal { public constructor(public readonly name: string) {} } 复制代码