对象和接口-3:接口类型
1. 创建资源
开始实验之前,您需要先创建实验相关资源。
- 在实验室页面,单击创建资源。
- (可选)在实验室页面左侧导航栏中,单击云产品资源列表,可查看本次实验资源相关信息(例如IP地址、子用户信息等)。
说明:资源创建过程需要3~5分钟(视资源不同开通时间有所差异,ACK等资源开通时间较长)。完成实验资源的创建后,您可以在云产品资源列表查看已创建的资源信息,例如:子用户名称、子用户密码、AK ID、AK Secret、资源中的项目名称等。
实验环境一旦开始创建则进入计时阶段,建议学员先基本了解实验具体的步骤、目的,真正开始做实验时再进行创建。
资源创建成功,可在左侧的资源卡片中查看相关资源信息以及RAM子账号信息
2. 实验环境操作指南
在本系列实验中,有两种方法可以进行操作。一种是控制台方式,另一种是远程桌面的方式。接下来我们分别介绍这两种操作方式。
控制台方式
使用控制台方式操作实验,上手比较简单,对网络带宽的要求也不高。但是在编辑代码时,需要使用Vim工具进行文本编辑。对于初学者来说使用Vim工具需要熟悉其操作模式。
进入控制台环境
资源创建成功后,我们在右侧选择切换到Web Terminal。
切换到dev用户
本次实验的开发环境部署在dev用户的~/web目录中。因此我们执行这两条命令即可进入实验环境
su dev cd ~/web
Vim工具的用法
vim是一个命令行下的文本编译器。使用vim编辑文件时。我们只需要通过快捷键,而不需要鼠标就可以对文本文件进行编辑。但是这种文本编辑器的用法和常见的图形界面编辑器使用习惯差别较大。接下来我们简单介绍vim的使用。
3.1 进入Vim
在命令行中输入vim即可进入操作界面,这时的Vim没有打开任何文件。
vim
3.2 Vim的三种模式
和普通编译器不同的是,Vim包含了三种操作模式。分别是命令模式,编辑模式,末行模式。接下来我们来简单介绍一下三种模式的用法。
3.3 命令模式
在刚进入界面的时候,vim默认状态为命令模式。这种模式下,无法编辑文件,键盘输入也不会在屏幕上有任何显示,这种情况往往会让使用者感到困惑。这时候我们可以输入大写的ZZ,即可退出Vim。
3.4 编辑模式
我们继续输入vim回到编辑器。在命令模式下我们按下小写的i。此时我们会发现下部的提示已经变成了插入。这表示vim已经进入了编辑模式。
在编辑模式下我们就可以像正常的编辑器一样编辑文件。此处我们输入hello world。
在编辑完成之后,如果我们想退回命令模式,只需要按ECS键即可。按下该按钮之后我们会发现下部的插入提示消失了,这就表示vim回到了命令模式。
3.5 末行模式
回到命令模式之后,我们按下:键,这时下部的提示会变成:,这就表示vim进入了末行模式。在末行模式下,我们同样使用快捷命令操作vim,但是和命令模式不同的是,末行模式中输入的命令会显示的屏幕的最后一行。
接下来我们继续输入w demo.txt并按回车。会提示显示为如下内容。表示上面的内容已经保存到了demo.txt中。
最后我们按:q,该命令也可以退出vim。
4. 编辑index.js文件
4.1 创建新文件
当我们希望编辑特定文件时,我们可以输入vim 文件名。这时候在下部会看到"index.ts" [新]。表示vim自动创建了新文件。
vim index.ts
4.2 复制粘贴
在编辑模式下,vim同样可以使用ctrl+v进行粘贴。我们按i进入编辑模式之后,将下列内容粘贴到vim中。
//vim编辑模式和命令模式的常用命令: //在编辑模式中:可以使用 上 下 左 右 按键移动光标 //在命令模式中:可以使用 h j k l 命令移动光标 //在编辑模式中:可以使用退格或者DEL键删除字符 //在命令模式中:可以使用 x 命令删除字符 //在命令模式中:可以使用 dd 命令删除整行文字(谨慎使用!) //在命令模式中:可以使用 ggdG 命令删除全部文字(谨慎使用!) //在命令模式中:可以使用 u 命令撤销操作 //在命令模式中:可以使用 ctrl + r 命令重做操作。
4.3 文件内容的查找
在命令模式下,除了按:命令之外,还可以使用/命令进入末行模式,通过/进入末行模式之后,输入相关的内容即可进行全文检索。我们在命令模式下输入/dd。
光标就会移动到相关字符上。
4.3 文件的保存和放弃
当我们编辑完成之后,可以在命令模式中通过ZZ命令直接保存并退出编辑器,也可以通过在末行模式中通过:w单独保存文件。如果我们希望通过末行模式保存并退出,可以使用:wq。
但是需要注意的时,如果文件在修改之后没有保存,是无法通过:q退出的。如果编辑过的文件是只读属性也无法通过ZZ退出。这时我们就可以使用:q!命令放弃所有的编辑内容,强制退出vim。
远程桌面方式
使用远程桌面方式进行实验是,可以使用VS Code编译器进行代码的编写和操作,相对控制台方式更加的直观。但是这种方式对网络带宽的要求比较高。
进入网页环境
1.1 进入远程桌面
资源创建成功后,我们在右侧选择切换到远程桌面。
1.2 启动浏览器
在远程桌面页面中,点击Chromium网页浏览器。
登录RAM用户
2.1 打开网页
浏览器启动后会默认打开阿里云的RAM用户登录页。在登录页面中我们点击下一步按钮。
2.2 获取密码
接下来我们需要输入RAM用户的密码。密码显示在实验控制台左侧,我们点击子用户密码右侧的复制按钮。
复制完成后,我们在用户密码框中按Ctrl + V 复制密码,然后点击登录按钮
进入ECS的远程连接。
3.1 进入ECS控制台
登录成功后,页面跳转到控制台,我们在控制台的搜索框中输入ECS,然后点击云服务器ECS进入云服务器控制台。
3.2 进入远程连接界面
在ECS控制台中,我们找到实验创建的ECS,点击右侧的远程连接按钮。
3.3 选择VNC远程连接
在远程连接中选择通过VNC远程连接中的立即登录如果没有显示该选项,则可点击展开其他登录方式按钮。
登录VNC远程连接
4.1 初始化VNC密码
初次登录VNC远程连接时需要先设置VNC密码。点击重置VNC密码按钮。
输入两次新的VNC密码,并点击确认。
4.2 登录VNC
VNC密码设置成功后,输入密码并点击确认。
4.4 进入图形界面
VNC登录成功后会看到实验ECS的登录界面,在登陆界面中我们点击DEV用户图标
4.5 在图形界面中登录
在密码框中输入默认登录密码Dev12345。即可进入图形实验环境
使用VS Code编辑器
登录图形实验环境之后,接下来我们来启动VS Code 编辑器。
5.1 进入应用程序列表
首先我们点击界面的下方的显示应用程序按钮。
5.2 启动VS Code
在应用程序列表中选择最后一页
然后点击VS Code应用图标
5.3 选择文件
在VS Code 编译器中,左边区域为文件列表,可以选择要编辑的文件
5.4 编辑文件
在VS Code中,右上区域为文件编辑区,可以进行文件内容编辑
5.5 操作终端
在右下区域的终端选项卡,为控制台区域,可以进行命令输入执行
3. 接口类型入门
在使用对象类型时,我们发现随着对象中元素的增多,对象的定义往往会非常的冗长,导致代码的阅读变得困难,因此在需要使用包含大量数据的复杂结构时,我们可以使用TypeScript提供的interface类型(接口类型)代替原生对象类型。
- interface关键字
和对象类型不同的时,每一个接口类型都包含名称,接口类型定义的语法为interface 接口名{}。同时接口类型的定义和接口类型变量的定义是分开的,接口类型定义完成后,就可以使用接口名来定义接口的变量。
接下来的例子。我们来比较两种类型的定义。我们会发现接口类型的代码比对象类型的更清晰。我们修改index.ts文件为如下内容。
var p: { name: string; age: number; nickname: string; height: number }; interface person { name: string; age: number; nickname: string; height: number }
- 接口类型的初始化
接口类型变量定义完成后,我们再来添加接口类型的初始化和赋值语法,这时我们需要使用对象元素对接口进行初始化和赋值。我们修改index.ts文件为如下内容。
var p: { name: string; age: number; nickname: string; height: number }; interface person { name: string; age: number; nickname: string; height: number } //接口初始化 var i: person = { name: "阿里云", age: 10, nickname: "阿里巴巴", height: 100 }; console.log(i) //接口赋值 p = { name: "Aliyun", age: 20, nickname: "alibaba", height: 200 }; i = p; console.log(i)
在终端选项卡输入如下命令编译并查看。
tsc && node index.js
- 接口类型常见用法
接口类型同样支持对象类型的只读属性,可选属性,扩展属性等功能。同时也支持.语法和[]语法,我们修改index.ts文件为如下内容。
interface person { readonly id: string; name: string; nickname?: string; [p: string]: any; } var p: person = { id: "ID001", name: "Aliyun", nickname: "alibaba", age: 20, height: 200 }; var p1: person = { id: "ID002", name: "Aliyun" }; console.log(p.nickname); console.log(p1["name"]);
在终端选项卡输入如下命令编译并查看。
tsc && node index.js
- 接口的编译
接下来我们来观察以下上述代码编译的结果。在终端选项卡输入如下命令编译并查看。会发现对象和接口在编译后的代码是一致的。
cat index.js
4. 函数型接口
- 函数型接口
除了普通接口之外,TypeScript还支持另一种函数型接口。这种接口中并没有普通属性,而是包含了一个匿名的函数属性。这类接口在初始化时,使用一个函数对象进行赋值。我们修改index.ts文件为如下内容。
interface log { (word: string): void; } var l: log = function (word: string): void { console.log("log out : " + word); } l("hello world")
在终端选项卡输入如下命令编译并查看。
tsc && node index.js
- 函数型接口的实现原理
接下来我们来比较一下普通接口和函数型接口的差别。我们编写两种接口并编译,然后观察这两种接口的编译结果。会发现普通接口会被编译成对象,而函数型接口会被编译成函数。我们修改index.ts文件为如下内容。
interface log { (word: string): void; } var l: log = function (word: string): void { console.log("log out : " + word); } interface person { name: string; age: number; } var p: person = { name: "阿里云", age: 10, };
在终端选项卡输入如下命令编译并查看。
tsc && cat index.js
- 混合型接口
在JavaScript语言中,函数元素也可以作为对象类型添加属性。但是在TypeScript语言中,我们无法直接实现这个能力。如果我们需要实现这个功能,可以使用函数型接口配合可选属性的方式来实现。
在下面的例子中,我们创建一个带有可选属性的函数型接口,然后像普通函数接口那样进行初始化。在初始化之后,我们还可以针对可选属性进行赋值。我们修改index.ts文件为如下内容。
interface person { (word: string): void; person_name?: string; } var p: person = function (word: string): void { console.log("a person say : " + word); } console.log(p) p("hello world") p.person_name = "ALIYUN" console.log(p)
在终端选项卡输入如下命令编译并查看。
tsc && node index.js
- 混合型接口的编译
在例子编写运行完成后,我们继续观察编译结果,会发现即使在函数型接口中添加了属性,其编译之后的还是使用函数类型来实现。在终端选项卡输入如下命令编译并查看。
cat index.js
5. 接口的面向对象
在上面的小节中,我们介绍了普通接口类型和函数型接口类型。这两种类型的接口实际上是针对JavaScript中两个重要类型:对象类型和函数类型进行了封装。实际上接口类型除了封装了原生类型之外。还提供了针对面向对象的支持。
- 接口的继承
在面向对象中最重要的概念之一就是继承,通过继承我们可以在一个已有的接口定义上添加一些属性来创建一个新的接口。TypeScript中接口继承的语法格式为interface 派生接口名 extends 基接口名。
在例子中,我们先创建person接口类型,然后从person再派生出user接口类型。会发现user接口中除了包含自身定义的属性,还包含了person接口中的全部属性,我们修改index.ts文件为如下内容。
interface person { id: number; name: string; male: boolean; } interface user extends person { password: string; } var base: person = { id: 10000, name: "person", male: true } console.log(base) var extend: user = { id: 10001, name: "user", male: false, password: "PASSWORD" } console.log(extend)
在终端选项卡输入如下命令编译并查看。
tsc && node index.js
- 派生接口和类型转换
interface person{ id:number; name:string; male:boolean; } interface user extends person{ password:string; } var base : person = {id:10000, name:"person", male:true} console.log(base) var extend : user = {id:10001, name:"user", male:false, password:"PASSWORD"} base = extend; console.log(base)
在终端选项卡输入如下命令编译并查看。
tsc && node index.js
- 接口的多重继承
在面向对象设计中,继承是最重要的特性之一。在面向对象中,一个派生接口包含一个基接口的继承方式,称为单继承。一个派生接口包含多个基接口的继承方式,称为多继承。不同的面向对象变成语言对于是否支持多继承有不同的实现方式。
在TypeScript中,接口类型是支持多继承方式的。我们修改index.ts文件为如下内容。
interface person { id: number; name: string; male: boolean; } interface contact { addr: string; mail: string; } interface user extends person, contact { password: string; } var u: user = { id: 10001, name: "user", male: false, password: "PASSWORD", addr: "HANGZHOU", mail: "user@aliyun.com" } console.log(u);
在终端选项卡输入如下命令编译并查看。会发现u对象继承了person和contact接口中的全部属性。
tsc && node index.js
6. 接口的继承案例
在面向对象设计中,我们常常会将不同事物中相同或者相似能力进行提炼,然后用多重继承的方式进行能力的组合,来构建对象。在本小节中,我们以常见的动物能力为例子来演示多重继承。
- 定义能力接口
我们修改index.ts文件为如下内容。
interface IAnimal { name: string; } interface ISound { sound(): void; } interface IFly { fly(): void; } interface ISwim { swim(): void; }
- 定义动物接口
接下来我们定义fish(鱼),bee(蜜蜂),duck(鸭子)3个动物类的接口。这三个接口都继承了IAnimal接口。
同时我们使用多重继承的语法,为不同的动物类接口添加相应能力的接口。fish具有游泳的能力,因此继承了ISwim接口。bee具有飞和发声的能力,因此继承了IFly接口和ISSound接口。duck具有游泳,和发声的能力,因此继承了ISwim接口,IFly接口和ISSound接口。我们修改index.ts文件为如下内容。
interface IAnimal { name: string; } interface ISound { sound(): void; } interface IFly { fly(): void; } interface ISwim { swim(): void; } interface fish extends IAnimal, ISwim { } interface bee extends IAnimal, ISound, IFly { } interface duck extends IAnimal, ISound, IFly, ISwim { }
- 实现动物接口
最后我们来为3个接口创建对象元素进行初始化。在初始化时我们需要根据不同动物的能力,为函数属性进行初始化赋值。我们修改index.ts文件为如下内容。
interface IAnimal { name: string; } interface ISound { sound(): void; } interface IFly { fly(): void; } interface ISwim { swim(): void; } interface fish extends IAnimal, ISwim { } interface bee extends IAnimal, ISound, IFly { } interface duck extends IAnimal, ISound, IFly, ISwim { } var f: fish = { name: "鱼", swim: function () { console.log(this.name + "会游泳") } }; f.swim(); var b: bee = { name: "蜜蜂", fly: function () { console.log(this.name + "会飞") }, sound: function () { console.log(this.name + "嗡嗡嗡") } }; b.fly(); b.sound(); var d: duck = { name: "鸭子", fly: function () { console.log(this.name + "会飞") }, swim: function () { console.log(this.name + "会游泳") }, sound: function () { console.log(this.name + "嘎嘎叫") } } d.fly(); d.swim(); d.sound();
在终端选项卡输入如下命令编译并查看。在初始化赋值时,我们会发现通过接口的方式初始化,每次都要创建函数属性,代码非常繁琐。因此在后面的实验中我们会使用class重新实现这个例子。
tsc && node index.js
实验地址:https://developer.aliyun.com/adc/scenario/b2cec0bf8c30460db086722eb6114dd8