Typescript: 进入类型的世界
编程语言的类型:
1.动态类型语言 (Dynamically Typed Language)
指的是在运行期间才会做数据类型检查的语言,
在用动态类型语言编程的时候,不用给变量指定数据类型,一个变量可以是字符串,你也可以给它赋值变成一个数字,非常灵活
第一次赋值给变量的时候,在内部将数据类型记录下来
比如javascript,
也就是说javascript写好的程序只有我们在运行的时候才能发现有啥错误,这种做法听起来有点危险
所以前端发明了一系列静态类型代码的检查器,
比如eslint,在编码期间就可以根据一系列的规则,提示一些有问题地方
2.静态类型语言(Statically Typed Language)
数据类型检查发生在编译的阶段,也就是所写程序要声明变量的数据类型。
写程序要声明数据类型
比如c,c++,java
TypeScript究竟是什么:
Type指类型
script指javascript,
它的目标是把不看重类型的动态语言变成关注类型的静态语言
1.官网口号javascript that scales可以理解为可扩展的javascript
2.静态类型风格的类型系统
3.从es6到es10甚至是esnext的语法支持
4.兼容各种浏览器,各种系统,各种服务器,完全开源
TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法,
因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改,
TypeScript 通过类型注解提供编译时的静态类型检查。
为什么要学习typescript?
1.程序更加容易
函数或者方法输入输出的参数类型,外部条件等,
动态语言的约束:需要手动调试等过程,
有了typeScript:代码本身就可以回答上诉问题
2.效率更高
在不同的代码块和定义中进行跳转
代码自动保全
丰富的接口提示
3.更少的错误
编译期间能够发现大部分错误
杜绝大部分错误
安装typescript
查看node及npm版本
node -v (至少10以上)
npm -v
安装:npm install -g typescript (可以指定版本安装)
查看版本: tsc -v
ts文件转换成js文件: tsc filnaem.ts
# 函数
Typescript 文档地址:Functions
在javascript中函数是一等公民,
函数和其他类型的公民都一样,
可以作为参数,可以存入数组,可以被另外一个函数返回,可以被赋值给另外一个变量
由两部分构成:
输入:传参来实现
输出: 就是函数的返回结果
// 来到我们的第一个例子,约定输入,约定输出
function add(x: number, y: number): number {
return x + y
}
add(1,2) //参数必须按照规定类型添加
let result = add(1,2) //result也会变成number类型
// 可选参数(加个问号)
//在可选参数后面不可以再添加确定参数了,这样程序的判断就会发生混乱
function add(x: number, y: number, z?: number): number {
if (typeof z === 'number') {
return x + y + z
} else {
return x + y
}
}
//函数的表达式
const add = (x:number,y:number,z?:number): number =>{
if(typeof z === 'number'){
return x+y+z
}else{
return x+ y
}
}
//鼠标移到add上边,add获得了一个类型,
//所以说,一个函数不仅输入和输出有类型,函数本身也是有类型的,
//add是函数类型,
//let add2: string = add //会报错 函数类型和string类型是不适配的,
//所以我们要声明一个完全一样的类型,
在typescript中我们要这么写函数的类型,
let add2: (x: number, y: number, z?:number) => number
//(这个箭头不是es6的箭头函数,而是ts中声明函数类型返回值的方法。)
//这个时候我们就是声明add2是number类型,
//这个时候它们类型是一样的:let add2: (x: number, y: number, z?:number) => number = add
//不会报错
//在ts中 凡是在冒号后面都是在声明类型,和实际的代码逻辑没什么关系
// 函数本身的类型
const add2: (x: number, y: number, z?:number) => number = add
Interface---Duck typing的概念可以描述各种类型,
它可以描述函数的类型么?
声明一个函数类型
// interface 描述函数类型
interface ISum {
(x: number, y: number): number //在Interface返回使用的是冒号
}
const sum2: ISum = sum
//(对比一下)
let add2: (x: number, y: number, z?:number) => number //直接写返回使用的是箭头
//这时我们将add给它赋值为类型为ISum,类型也是完全的匹配
let ada2: ISum = add
在javascript的世界我们使用构造函数和原型链来实现继承,
到了es6时代,总算出现了class的概念,虽然它的内部还是原型链的机制来实现,
但是我们总算有了新的方法,从我们熟悉的面向对象的方式来审视这门语言了。
typescript对类的支持可谓是更加丰富,除了es6,es7已有的内容,还添加了一些术语。
//面向对象的一些术语:
1.类 Class:定义了一切事物的抽象特点,包含它的属性和方法。就像是一张蓝图,
//比如汽车是一个class,就像是一张造汽车的图纸。
2.对象 Object,类的实例,通过new生成,有了蓝图我们就可以创造实实在在的汽车。
//我们可以说一辆奥利Q5是汽车类的实例,也可以说一辆奔驰是汽车类的另一个实例。
3.面向对象OOP三大特征:封装 继承 多态。
封装:(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。
外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象。
继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特征。
多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。
//比如猫和狗,他们都可以继承一次animal,但是他们分别实现了自己的eat,吃的方法,
//此时针对某一个实例,我们无需了解它是猫或者狗,我们可以直接调用eat方法,
//程序会自动判断出来应该如何执行某个方法。
//面向对象编程给人一种做上帝的感觉。
//举个例子:
//创建了一个类,叫animal,它是一个基本类。
class Animal {
name: string;
constructor(name: string) { //里面有一个构造函数,构造函数是实例化执行的逻辑
this.name = name //一个方法
}
run() {
return `${this.name} is running`
}
}
const snake = new Animal('lily')
console.log(snake.run()) //输出lily is runing
// 继承的特性
class Dog extends Animal { //extends继承以后,自然有了父类属性和方法
bark() { //创建了一个新的方法
return `${this.name} is barking`
}
}
const xiaobao = new Dog('xiaobao')
console.log(xiaobao.run())
console.log(xiaobao.bark())
//多态
// 这里我们重写构造函数,注意在子类的构造函数中,必须使用 super 调用父类的方法,要不就会报错。
class Cat extends Animal {
constructor(name) {
super(name) //constructor这里进行了一个重写
console.log(this.name) //多添加了一行
}
run() {
return 'Meow, ' + super.run() //run方法也进行了重写
}
}
const maomao = new Cat('maomao')
console.log(maomao.run())
目前为止,我们都是讨论都是类的实例,实例上的属性和实例上的方法,
那我们类上面有没有直接可以访问的属性和方法呢?
//区别:不需要实例化,直接在类调用就可以
class Cat extends Animal{
static categories = ['mammal'] //创建了一个静态属性
constructor(name){
super(name)
console.log(this.name)
}
run(){
return 'Meow,'+super.run()
}
}
//console.log(Cat.categories())
为什么要有静态属性或者方法呢?
是因为这里面的定义和实例没太大关系,
typeCsript怎么增强类?
//三种访问修饰符,可以给类上的方法和属性提供权限管理,
//有些内容是不愿意暴露给外部使用的
public:修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是public的
private:修饰的属性或方法是私有的,不能在声明它的类的外部访问
protected:修饰的属性或方法是受保护的,它和private类似,区别是它在子类中也是允许被访问的
//有些属性只能读不能写,
//readonly修饰符号
class Animal{
readonly name: string;
constructor(name: string){
this.name = name
}
run(){
return `${this.name} is running`
}
}
//const snake = new Animal('lily)
//snake.name = '123' //会报错 Connot addign to 'name' because it is a read-only
# 类与接口
//继承的困境
//类可以使用implements来实现接口
//之前学过接口Interface,用于对对象的形状shape进行描述,
//我们还用它描述了函数的类型,
//本节介绍接口的另一个用途:对类的一部分行为进行抽象
//在面向对象的世界中一个类只能继承自另外一个类,
//有时候不同类之间会有一些共同的特新,使用子类继承父类的方法,很难来完成。
//这时候我们就可以把这些特性提取成接口,然后用一个称之为implements的关键字来实现。
//这样就大大提高了面向对象的灵活性。
//举个例子:
//创建一个称之为car的类,他有一个特性是switchRadio,
class Car{
switchRadio(trigger: boolean){
}
}
//现在又有一个蓝图出现了,负责创造一个手机类,手里类里面也有一个打开收音机的方法,
class Cellphone{
switchRadio(trigger: boolean){
}
}
//我们发现这两个类有相同的特性,所以我们考虑把他们给提取出来,
//如果用父类的形式,car和cellphone必须有一个合适的父类,
//感觉很难找,
//这是可以把这一个特性抽取成一个Interface,让这两个类都去实现它,
interface Radio { //里面有一个switchRadio方法
switchRadio(trigger: boolean): void; //void关键字代表什么都不返回
}
class Car implements Radio {
switchRadio(trigger) {
}
}
class Cellphone implements Radio {
switchRadio() {
}
}
//这时我们implements就发挥契约的作用,
//告诉car和cellphone要去实现这个方法,
//删掉这个方法的时候就会报错
//通过Interface完成了逻辑功能的提取和验证,
一个小功能:检查电池的容量,手机有,汽车没有
//新创建一个接口
interface Battery{
checkBatteryStatus(): void;
}
// 要实现多个接口,我们只需要中间用 逗号 隔开即可。
class Cellphone implements Radio, Battery {
switchRadio() {
}
checkBatteryStatus() {
}
}
接口之间还可以有继承的关系,注意这个是接口之间的继承,不是class之间的继承,
但其实是和class之间的继承是一样的,
新建一个接口
interface RadioWithCellphone extends Radio{ //extends关键字继承
checkBatteryStatus(): void
}
//RadioWithCellphone上边两个方法都有了,
//可以替换上边的写法
class Cellphone implements RadioWithCellphone{
switchRadio(trigger){
}
checkBatteryStatus(){
}
}
interface的一点点精髓:它仿佛是某种锲约可以定义和约束所有内容,
比如object的样子可能用interface的方法来抽象类的属性和方法,
还可以去定义一个函数类型,各种复杂的类型等等,
所以这就是Duck typing的精髓,只要它走路像鸭子,叫起来像鸭子,
不管你是什么东西,会用它来约束各种概念上毫不相关的内容。
这节我们学会用interface和implements来抽象和验证类的属性和方法。
网友评论