接口
TypeScript 的核心原则之一是对值所具有的结构进行类型检查。我们使用接口(Interfaces)来定义对象的类型。接口是对象的状态(属性)和行为(方法)的抽象(描述)
// 定义人的接口
interface IPerson {
// 只读属性: readonly
readonly id: number
name: string
age: number
// 可选属性: ?
sex?: string
}
const person1: IPerson = {
id: 1,
name: 'tom',
age: 20,
sex: '男'
}
函数类型
接口能够描述 JavaScript 中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外,接口也可以描述函数类型。
为了使用接口表示函数类型,我们需要给接口定义一个调用签名。它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。
// 定义一个接口用来做为某个函数的类型使用
interface SearchFunc {
// 定义一个调用签名
(source: string, subString: string): boolean
}
// 定义一个函数,该类型就是上面的接口
const mySearch: SearchFunc = function (source: string, sub: string): boolean {
return source.search(sub) > -1
}
// 调用函数`
console.log(mySearch('abcd', 'bc'))
类类型
类实现接口
与 C# 或 Java 里接口的基本作用一样,TypeScript 也能够用它来明确的强制一个类去符合某种契约。
/* 类类型: 实现接口
1. 一个类可以实现多个接口
2. 一个接口可以继承多个接口
*/
(()=>{
// 定义一个接口
interface IFly(){
// 该方法没有任何实现(方法中什么都没有)
fly()
}
// 定义一个类 这个类的类型就是上面定义的接口(也可以理解为,IFly接口约束了当前Person类)
class Person implements IFly{
fly(){
console.log('哈哈哈')
}
}
// 实例化对象
const person = new Person()
person.fly()
// 定义一个接口
interface ISwim(){
swim()
}
// 定义一个类,这个类的类型是IFly和ISwim
class Person2 implements IFly,ISwim{
fly(){
console.log('哈哈哈22222222')
}
swim(){
console.log('嘿嘿嘿2222222')
}
}
// 实例化对象
const person2 = new Person2()
person2.fly()
person2.swim()
// 继承
interface IMyFlyAndSwim extends IFly,ISwim{}
// 定义一个类
class Person3 implements IMyFlyAndSwim{
fly(){
console.log('哈哈哈333')
}
swim(){
console.log('嘿嘿嘿333')
}
}
// 实例化对象
const person3 = new Person3()
person3.fly()
person3.swim()
})()
注意:
接口和接口之间叫继承(extends),类和接口之间叫实现(implements)
类
对于传统的 JavaScript 程序我们会使用函数
和基于原型的继承
来创建可重用的组件,但对于熟悉使用面向对象方式的程序员使用这些语法就有些棘手,因为他们用的是基于类的继承
并且对象是由类构建出来的。 从 ECMAScript 2015,也就是 ES6 开始, JavaScript 程序员将能够使用基于类的面向对象的方式。 使用 TypeScript,我们允许开发者现在就使用这些特性,并且编译后的 JavaScript 可以在所有主流浏览器和平台上运行,而不需要等到下个 JavaScript 版本
(()=>{
// ts中类的定义以及使用
class Person{
// 定义属性
name: string
age: number
gender: string
// 定义构造函数:为了将来实例化对象的时候,可以直接对属性的值进行初始化
constructor(name: string='王昭君',age: number=18,gender: string='女孩子'){
// 更新对象的属性值
this.name = name
this.age = age
this.gender = gender
}
//定义实例方法
sayHi(str:string){
console.log(`我是${this.name},我今年${this.age}岁了,是个${this.gender}`,str)
}
}
//ts使用类,实例化对象,可以进行初始化操作
const person = new Person()
person.sayHi('谢谢大家')
})()
继承
在 TypeScript 里,我们可以使用常用的面向对象模式。 基于类的程序设计中一种最基本的模式是允许使用继承来扩展现有的类。
// 继承:类与类之间的关系
// 继承后类与类之间的叫法
// A类继承了B类,那么此时A类叫子类,B类叫基类
//子类 --->派生类
//基类--->超类(父类)
(()=>{
// 定义一个类
class Person{
// 定义属性
name: string
age: number
gender: string
// 定义构造方法
constructor( name: string='小明',age: number=18,gender: string='男孩'){
// 更新属性数据
this.name = name
this.age = age
this.gender = gender
}
// 定义实例方法
sayHi(str:string){
console.log(`我是${this.name}`,${str})
}
}
// 定义一个类
class Student extends Person{
constructor( name: string,age: number,gender: string){
// 调用父类的构造函数,使用的是super
super(name,age,gender)
}
// 调用父类的方法
sayHi(){
super.sayHi('哈哈')
}
}
// 实例化Person
const person = new Person()
// 实例化 Student
const stu = new Student('小甜甜',16,'女孩')
stu.sayHi()
// 类与类之间如果有继承关系,需要使用extends关键字
// 子类调用父类构造函数,使用 super关键字(包括调用父类的实例方法)
// 子类可以重写父类的方法
})()
多态
父类型的引用指向了子类型 的对象,不同类型的对象针对相同的方法,产生了不同的行为
(()=>{
// 定义一个父类
class Animai{
// 定义一个属性
name: string
// 定义构造方法
constructor( name: string){
// 更新属性数据
this.name = name
}
// 定义实例方法
run(distance:number=0){
console.log(`跑了${distance}米`,this.name)
}
}
// 定义一个子类
class Dog extends Animal{
// 构造函数
constructor(name:string){
super(name)
}
// 实例方法,重写父类的实例方法
run(distance:number=5){
console.log(`跑了${distance}米`,this.name)
}
}
// 定义一个子类
class Pig extends Animal{
// 构造函数
constructor(name:string){
super(name)
}
// 实例方法,重写父类的实例方法
run(distance:number=10){
console.log(`跑了${distance}米`,this.name)
}
}
// 实例化父类对象
const ani:Animal = new Animal('动物')
ani.run()
// 实例化子类对象
const dog:Dog = new Dog('大黄')
dog.run()
const pig:Pig = new Pig('佩奇')
pig.run()
//父类和子类的关系:父子关系,此时,父类类型创建了子类对象
const dog1:Animal = new Dog('小黄')
dog1.run()
const pig1:Animal = new Pig('八戒')
pig1.run()
function showRun(ani:Animal){
ani:run()
}
showRun(dog1)
showRun(pig1)
})()
修饰符
类中成员的修饰符:主要描述类中的成员(属性,构造函数,方法)的可访问性
类成员都有一个默认的访问修饰符,public,代表的是公共的,任何位置都可以访问类中的成员
private修饰符,代表的是私有的,类中的成员使用这个修饰符,外部是无法访问这个成员的数据的,当然子类也无法访问
protected修饰符,代表的是受保护的,类中的成员使用这个修饰符,外部无法访问这个成员的数据,子类可以访问
(()=>{
// 类
class Person{
// 属性
// public name:string
// private name:string
protected name:string
// 构造函数
public constructor(name:string){
// 更新属性
this.name = name
}
// 方法
public eat(){
console.log('今天天气真好',this.name)
}
}
// 子类
class Student extends Person{
constructor(name:string){
super(name)
}
play(){
console.log('就是玩',this.name)
}
}
// 实例化
const per = new Person('大蛇丸')
// 类的外部可以访问属性的成员
console.log(per.name)
per.eat()
const stu = new Student('红豆')
stu.play()
console.log(stu.name)
})()
readonly修饰符
类中的成员使用这个修饰符,该属性成员就不能在外部被随意的修改了
构造函数中,可以对只读的属性成员的数据进行修改
如果构造函数中没有任何的参数,类中的属性成员此时已经使用readonly进行修饰,那么外部也是不能对这个属性值进行修改
构造函数中的参数可以使用readonly进行修饰,一旦修饰,该类中就有了一个只读的成员属性,外部可以访问不能修改
构造函数中的成员也可以使用public,private和protected进行修饰,无论使用那个,该类中都会自动添加这么一个属性成员
(()=>{
// readonly修饰类中的成员属性
// 类
class Person{
readonly name:string
constructor(name:string){
this.name = name
}
sayHi(){
console.log('哈哈',this.name)
// 类中的普通方法也不能修改
this.name = '大甜甜'
}
}
// 实例化
const person:Person = new Person('小甜甜')
console.log(person)
console.log(person.name)
// 此时无法修改,因为name属性是只读的
person.name = '大甜甜'
console.log(person.name)
//readonly修饰类中的构造函数中的参数
// 类
class Person{
// 构造函数中的name参数,一旦使用readonly进行修饰后,那么该name参数可以叫参数属性
// 构造函数中的name参数,一旦使用readonly进行修饰后,那么Person类中就有了一个name属性成员
// 构造函数中的name参数,一旦使用readonly进行修饰后,外部也是无法修改类中的name属性成员值的
// constructor(readonly name:string='大甜甜'){
// this.name = name
// }
// 构造函数中的name参数,一旦使用public进行修饰后,那么这个Person类中就有了一个公共的name属性成员了
// constructor(public name:string='大甜甜'){
// this.name = name
// }
// 构造函数中的name参数,一旦使用private进行修饰后,那么这个Person类中就有了一个私有的name属性成员了
// constructor(private name:string='大甜甜'){
// this.name = name
// }
// 构造函数中的name参数,一旦使用protected进行修饰后,那么这个Person类中就有了一个受保护的name属性成员了(只能在本类和派生类中访问及使用)
// constructor(protected name:string='大甜甜'){
// this.name = name
// }
}
// 实例化
const person:Person = new Person('小甜甜')
console.log(person)
person.name = '爱蜜莉雅糖糖'
console.log(person.name)
})()
存储器
让我们可以有效的控制对对象的成员的访问,通过getters和setters来进行操作
(()=>{
// 外部可以传入数据,同时使用set和get控制数据,外部也可以进行操作
class Person{
firstName:string
lastName:string
constructor(firstName:string,lastName:string){
this.firstName = firstName
this.lastName = lastName
}
// 读取器===>负责读取数据的,自动调用
get fullName(){
return this.firstName + '-' + this.lastName
}
// 设置器===>负责设置数据的(修改),修改之后调用
set fullName(val){
let names = val.split('-')
this.fristName = names[0]
this.lastName = names[1]
}
}
const person:Person = new Person('爱蜜莉雅','糖糖')
console.log(person)
console.log(person.fullName)
person.fullName = '樱岛-麻衣'
console.log(person.fullName)
})()
静态属性
在类中通过static修饰的属性或方法,那么就是静态的属性及静态的方法,也称之为:静态成员
静态成员在使用的时候通过类名.方法调用
(()=>{
class Person{
// 静态属性
// 类中默认的有一个内置的name属性
static name1:string = '小甜甜'
// 构造函数是不能通过static来进行修饰的
constructor(){
// this是实例对象,name1是静态属性,不能通过实例对象直接调用静态属性来使用
// this.name1 = name
}
// 静态方法
static sayHi(){
console.log('爱蜜莉雅糖糖yyds')
}
}
// const person:Person = new Person('小甜甜')
// 实例属性
// console.log(person.name1)
// 实例方法
// person.sayHi()
// 通过类名.静态属性方法访问该成员属性
console.log(Person.name1)
// 通过类名.静态属性方法设置该成员属性
Person.name1 = '樱岛麻衣'
console.log(Person.name1)
// 通过类名.静态方法的方式调用内部的静态方法
Person.sayHi()
})()
抽象类
包含抽象方法(抽象方法一般没有任何的具体内容的实现),也可以包含实例方法,抽象类是不能被实例化的,为了让子类实例化及实现内部的抽象方法
抽象类的作用或者目的最终都是为了子类服务的
(()=>{
abstract class Animal{
// 抽象属性
// abstract name:string = '小甜甜'
// 抽象方法
abstract eat()
// 报错的,抽象方法不能有具体的实现
// abstract eat(){
// console.log('站着吃,躺着吃')
// }
// 实例方法
sayHi(){
console.log('哈哈')
}
}
// 定义一个子类
class Dog extends Animal(){
// name:string = '小黄'
// 重新的实现抽象类中的方法,此时这个方法就是当前Dog类的实例方法
eat(){
console.log('站着吃,躺着吃')
}
}
// 实例化Dog的对象
const dog:Dog = new Animal()
dog.eat()
// 调用的是抽象类的实例方法
dog.sayHi()
// console.log(dog.name)
// 不能实例化抽象类对象
// const ani:Animal = new Animal()
})()
总结:
TypeScript 在社区的流行度越来越高,它非常适用于一些大型项目,也非常适用于一些基础库,极大地帮助我们提升了开发效率和体验。