简介
定义
1.TypeScript,简称为TS,是JS的超级;Vue3.0就是使用TS开发出来的,并且推荐开发者使用TS进行开发;
2.TS是可以直接运行的(比如使用ts-node),最终编译为纯的JS运行在任意的平台之上,是开源的;
3.始于JavaScript,归于JavaScript!
特征
1.类型特征
1.TypeScript是静态类型:
类型系统按照「类型检查时机」来分类,可以分为动态类型和静态类型。
动态类型是指在运行时才会进行类型检查,这种语言的类型错误往往会导致运行时错误。JS是 一门解释型语言,没有编译阶段,所以JS是动态类型。
2.TypeScript 是弱类型:
类型系统按照「是否允许隐式类型转换」来分类,可以分为强类型和弱类型,TS是完全兼容JS的,它不会修改JS运行时的特性,所以它们都是弱类型。
2.适用于任何规模开发
1.TypeScript 非常适用于大型项目——这是显而易见的,类型系统可以为大型项目带来更高的可维护性,以及更少的 bug。
2.TypeScript 还可以和 JavaScript 共存。
3.与标准同步
TypeScript 的另一个重要的特性就是坚持与 ECMAScript 标准同步发展。
Ts的编译运行
1.在文件夹的地址栏输入cmd打开终端
2.在终端安装全局ts-node模块
3.直接使用代码:ts-node xxx.ts(xxx是文件名)
Ts的变量声明
1.与js相同,使用var、let或const等
2.声明变量指定类型(任意类型都可),这样的变量赋值时只能使用指定类型的值,以达到强类型语言变量的特点及其优点;
var age:number = 21;
age = '21'//编译出错
Ts的基础类型
布尔值
只有简单的true/false两个值,在JS和TS里叫做 Boolean
let isLogin: boolean = false;
let isLogin: boolean = true;
数字
和JS一样,TS里的所有数字都是浮点数,类型是 number。 支持2、8、10、16
等各种进制。但依然不适合科学计算。
let n1: number = 6;
let n2: number = 0xf00d;// ES6 中的十六进制表示法,会被编译为十进制数字
let n3: number = 0b1010;// ES6 中的二进制表示法,会被编译为十进制数字
let n4: number = 0o744;// ES6 中的八进制表示法,会被编译为十进制数字
let n5: number = NaN;
let n6: number = Infinity;
字符串
使用 string
表示文本数据类型。 和JS一样,可以使用双引号( "
)、单引号('
)和反引号(`)表示字符串。总之,和JS一样。
let myName: string = 'karen';
let str: string = `Hello, my name is ${myName}.
I'll be ${myAge + 1} years old next month.`;
字符串字面量类型
字符串字面量类型用来约束取值只能是某几个字符串中的一个。
type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
// do something
}
handleEvent(<Element>document.getElementById('hello'), 'scroll'); // 没问题
handleEvent(<Element>document.getElementById('world'), 'dblclick'); // 报错
数组
TS像JS一样可以操作数组元素。 有两种方式可以定义数组。 第一种,可以在元素类型后面接上 []
,表示由此类型元素组成的一个数组。第二种用Array<类型值>来定义。
[ ]
let score:number[] = [1,20, parseInt('50')];
let arr1: number[] = [1, 1, 2, 3, 5];//正确
let arr2: number[] = [1, '1', 2, 3, 5];//错误
let arr3: number[] = [1, 1, 2, 3, 5];
arr3.push('8');//错误
<>
let fruit:Array<string|number> = ['apple', 'banana', 'pear', 20];
let arr4: Array<number> = [1, 1, 2, 3, 5];//正确
let arr5: Array<string> = ["hello","h5"];//正确
元组(Tuple)
元组类型允许表示一个已知元素数量和类型的数组
,各元素的类型不必相同。
let tom: [string, number] = ['Tom', 25];
通过下标赋值
let tom: [string, number] = ['', 0];
tom[0] = 'Tom';
tom[1] = 25;
越界操作
let tom: [string, number];
tom = ['Tom', 25];
tom.push('male');//数量越界
tom.push(true);//报错,数量越界且类型越界
解构赋值
const lilei: [string, number] = ["Lilei", 23];
console.log(lilei);
const [myname, myage]: [string, number] = lilei;//解构赋值
console.log(myname, myage);
枚举
enum
类型是对JS标准数据类型的一个补充。 使用枚举类型可以为一组数值赋予友好的名字。 枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射,也可以手动的指定成员的数值。
//性别 一般是存储为数字的
enum Gender {Secret, Male, Female};
console.log(Gender.Male);// 1
//一周的每天
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
console.log(Days.Sun, Days.Mon, Days.Tue, Days.Sat);//0 1 2 6
console.log(Days["Sun"] === 0); // true
console.log(Days["Sat"] === 6); // true
//根据编号得到元素名console.log(Days[0] === "Sun"); // true
console.log(Days[6] === "Sat"); // true
Any
指该数据的类型可以任意值,且他的任何操作,返回的内容类型都是任意值。
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false;
Void
void,无效的,表示没有任何类型。 当一个函数没有返回值时,其返回值类型是 void
声明一个void
类型的变量没什么用,因为你只能为它赋予undefined
和null
:
let unusable: void = undefined;
Null和Undefined
undefined
和null
两者各自有自己的类型undefined和null。作用不大。
Never
never
类型表示的是那些永不存在的值的类型。never
类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型;
function error(message: string): never {
throw new Error(message);}
//推断的返回值类型为never
function fail() {
return error("Something failed");}
Object
object
表示非原始类型,也就是引用类型,是除number
,string
,boolean
,symbol
,null
或undefined
之外的类型,可以使用"对象", "数组对象"、string、number等为其赋值,null和undefined不行的。 之所以Object变量可以接收任何对象,是因为Object是所有类的父类。
let a:Object = [1, 2, 3]
console.log(a);
a = {id:1, name:'小明'}
console.log(a);
a = '{id:1, name:"小明"}'
console.log(typeof a); //string
类型别名和联合类型
多个类型的联合,它们之间是或的关系
type manyType = number | boolean | string;
let a: manyType
a = 0o700;
console.log(a);
a = true;
console.log(a);
a = '小明';
console.log(a);
类型推论
没有声明类型的变量被赋值一次后,变量的类型就确定了,之后再次被赋值时,不可改变赋值的类型。
let r = 100;//确定了r的类型为number
r = "fds";//编译报错,因为r的类型为number
类型断言
告诉编译器我们的这个变量类型是声明类型,类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误。
值as类型
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
<类型>值
xxxxxxxxxx let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
初识接口
TS的核心原则之一就是对值所具有的结构进行类型检查。TS里面接口的作用就是为这些类型命名 和 为你的代码 或 第三方代码定义契约。
在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。
TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。
定义格式
1. 只关注值的外形, 只要传入的对象满足提到的条件,那么它就是被允许的。
2. 对象是无序的:类型检查器不会去检查属性的顺序,只要相应的属性存在且类型是对的就可以。
interface person{
age:number,
name:string}
let p1:person = {age=20,name='小明'}
可选属性
在属性值后+?的为可选属性,可以不赋值。
interface SquareConfig {
color?: string;
width?: number;
}
只读属性
一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly
来指定只读属性:设置后值不能被修改
interface person {
age: number;
color: string;
readonly idcard: number;
sex?: string;
readonly huji?: string
}
let p1: person = { age: 12, color: "red", idcard: 123123 }
p1.idcard=1234//报错
函数
为函数定义类型
我们可以给每个参数添加类型之后再为函数本身添加返回值类型。 TypeScript也能够根据返回语句自动推断出返回值类型
function add(x: number, y: string): string {
return x + y;
}
可选参数和默认参数
传递给一个函数的参数个数必须与函数期望的参数个数一致。
function buildName(firstName: string, lastName: string) {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // error
let result2 = buildName("Bob", "Adams", "Sr."); // error
let result3 = buildName("Bob", "Adams"); // right
可以在参数名旁使用 ?
实现可选参数的功能, 如果带默认值的参数出现在必须参数前面
function buildName(firstName: string, lastName?: string) {
return firstName + " " + lastName;
}
console.log(buildName("小明"));//小明 undefiend
剩余参数
在js里面叫rest参数 ...restArr
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
console.log(employeeName);//Joseph Samuel Lucas MacKinzie
函数类型变量
1.变量的类型可以声明为函数类型;
let myAdd: (x: number, y: number) => number =
function(x: number, y: number): number { return x + y; };
2.函数类型属于自定义类型,包含两部分:参数类型和返回值类型;
//声明一个变量并指定类型为自定义的函数类型
let myadd:(x:number, y:number)=>number;
//声明一个函数
function add(x: number, y: number): number {
return x + y;
}
//把函数赋值给类型为函数类型的变量
myadd = add;
//赋值匿名函数
myadd = function(x: number, y: number): number {
return x + y;
}
//赋值箭头函数
myadd = (x: number, y: number):number=>{
return x + y;
}
3.只要参数类型是匹配的,那么就认为它是有效的函数类型,并不要求参数名一样,很多时候参数名是为了增加可读性
let myAdd: (baseValue: number, increment: number) => number =
function(x: number, y: number): number { return x + y; };
4.在函数和返回值类型之前使用( =>
)符号,返回值类型是函数类型的必要部分,如果函数没有返回任何值,你也必须指定返回值类型为 void
而不能留空
使用接口封装函数变量类型
自定函数类型代码往往很长,可以使用接口来封装该类型,之后使用接口来代表该类型
interface addType {
(number, number):number
}
function add(x: number, y: number): number {
return x + y;
}
let myadd1:addType = add;
let myadd2:addType = (x:number, y:number):number=>{
return x+y;
}
类
基本概念
简单认知
-
类是现实世界或思维世界中的实体在计算机中的反映,它将数据(属性)以及这些数据上的操作(方法)封装在一起。
-
对象是具有类类型的变量。类和对象是面向对象编程技术中的最基本的概念。
类与对象的关系
-
类是对象的抽象,而对象是类的具体实例,是通过new classname产生的。
-
类是抽象的,不占用内存,而对象是具体的,占用存储空间。
-
类是用于创建对象的蓝图(规划,抽象)。
专业术语
-
面向对象(OOP)的三大特性:封装、继承、多态;
-
封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据;
-
继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性;
-
多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 Cat 和 Dog 都继承自 Animal,但是分别实现了自己的 eat 方法;
-
-
存取器(getter & setter):用以改变属性的读取和赋值行为;
-
修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法;
-
抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现;
-
接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口;
类和对象语法
类的定义
class Class_name {
// 类体
}
-
定义类的关键字为 class;
-
类体中声明类的成员(都是可选的)
-
字段 − 也就是属性,可包含0到多个
-
构造函数 − constructor,可包含0到1个
-
方法 − 也就是行为动作,可包含0到多个
类的属性
class Car {
// 属性
engine:string; //没有初始化 (会报错)
price:number = 15;//初始化值为15
}
let car = new Car();
console.log(car.engine, car.price)
(1)声明属性时要声明类型
(2)声明字段时初始化值是可选的;
类的方法
类的方法参数和返回值都可声明类型,但都是可选的;
class Car {
// 属性
engine:string; //没有初始化
price:number = 15;//初始化值为15
//方法
disp():void {
console.log(this.engine, this.price);
}
toGo(len:number):boolean {
console.log(`走了${len}米远`);
return true;
}
//字段式的方法,本质上这不是方法,而是字段
m1 = (a:number, b:number):void=>{
console.log("m1()")
}
m2:(a:number,b:number)=>void = (a, b)=>{
console.log("m1()",a+b)
}
m3 = function():void {
console.log("m3()")
}
}
对象和内存
-
new多次本质上是创建了多个了对象, 只有创建了对象,才能使用对象访问对象的字段或方法;
-
对象在堆中,计算机堆中内存无名字,只能通过地址使用之,堆中对象地址通过hash后得到引用值,引用类型变量保存的就是引用值;
-
每个对象都有唯一引用, 只能通过对象的引用使用对象, new 会返回对象的引用(this);
-
当类中定义了属性,该类的每个对象都会独立开辟出属性成员,当前对象独享;
-
创建对象时,方法不会被开辟出来,同一个类的方法被所有该类对象共用(原型和原型链);
-
同一对象可被多个变量引用,当一个对象没有引用时,对象的生命就被GC(拉圾回收机制)管理了, 当对象销毁时,对象中独享的字段成员也会随之销毁;
-
只有类不能做事情,类的对象才有用, 类成员(静态成员例外);
this
方法中可以使用this,该this引用调用方法的对象, 所以方法里可以使用 this.方法
和this.字段
访问实例成员;
构造函数
-
函数名为 constructor;
-
可带0到多个参数 (构造函数没有返值,也不用声明返回值类型);
-
实例化对象;
3.1 构造函数无参时
new 类名();
3.2 构造函数有参时
new 类名(为构造函数传实参)
-
实例化过程(new 的过程发生了什么);
(1)在堆中实例化对象, 包括创建字段成员
(2)执行构造函数
(3)返回对象引用
-
构造函数存在的价值
(1)初始化一些操作并为对象属性赋值;
(2)构造函数中this值为新实例化的对象引用, 常通过"this.字段=值"为新对象的字段赋值;
class Car {
engine:string;
price:numbe;
constructor(engine:string, price:number) {
this.engine = engine;
this.price = price;
}
printf():void {
console.log(`engin=${this.engine}, price=${this.price}`);
}
}
let car = new Car("德国进口", 30);
car.printf();
静态方法
//静态方法
//使用 static 修饰符修饰的方法称为静态方法,它们不需要实例化,而是直接通过类来调用:
class Animal {
static isAnimal(a) {
return a instanceof Animal;
}
}
let a = new Animal('Jack');
Animal.isAnimal(a); // true
a.isAnimal(a); // TypeError: a.isAnimal is not a function
存取器
//存取器
使用 getter 和 setter 可以改变属性的赋值和读取行为:
class Animal {
constructor(name) {
this._name = name;
}
get name() {
return 'Jack';
}
set name(value) {
console.log('setter: ' + value);
}
}
let a = new Animal('Kitty'); // setter: Kitty
a.name = 'Tom'; // setter: Tom
console.log(a.name); // Jack