一些概念知识点
- javascript设计之初,存在缺陷
typeof NaN // 'number'
0.1+0.2 // 0.30000000000000004
true==1 //true
9+'1' //'91'
[]==0 // true
-
javascript的更新迭代
javascript与typescript的区别与联系
- ts是js的超集,typescript = type + javascript,额外增加了:类型系统;
- ts包含js,针对js一些缺陷进行限制,比如: 某个变量一旦定义了类型,那么无法二次定期其类型;
-
ts在编码阶段就可以进行语法校验,发现报错,而非要运行起来才发现,提高编程效率;
- 为什么一定要学习ts
- 除了可以提高编码效率,此外最重要的是,大型前端框架底层使用ts(vue3);
- vue3、react 创建项目时候都支持使用typescript;
- 大型项目都在推荐使用typescript;
- ts拥有类型推断机制,不需要在代码每个地方都显示标注类型,降低书写成本;
- ts已成为大中型前端项目的首先编程语言;
typescript安装与解析
- 安装ts与基本配置
-
因为node端与浏览器端,均不识别.ts文件,因此需要安装编译工具进行转换,生成js后才可被浏览器解析
- 浏览器端解析ts文件
- 安装
npm install -g typescript
- 解析
tsc test.ts
- 常用指令
//查看版本号
tsc -v
//初始化一个tsc配置文件(tsconfig.json)
tsc --init
- tsconfig.json配置
{
"compilerOptions":{
//自动编译
"watch": true,
//删除编译后注释
"removeComments": true,
//编译目标版本
"target": "es5"
}
}
-
类型声明文件的使用说明
xxxx.d.ts 为 xxxx.js 的类型约束说明文件
服务端解析ts文件
- 安装
npm i -g ts-node
- 解析
ts-node test.ts
类型注解分类
JS已有类型
原始类型(基本类型): number,string,boolean,null,undefined,symbol
对象类型(引用类型): object( 数组,函数,对象等)TS新增类型
联合类型、自定义类型(类型别名)、接口、元祖、字面量类型、枚举、void、any等
类型注解 - (javascript已有类型)
- 原始类型(基础类型)
- number数字类型
let num1: number = 6;
- string字符串类型
let name: string = '张三';
- boolean布尔值
let isTrue: boolean = true;
- 对象类型(引用类型)
- object对象类型
//方法名(): 返回值类型 -- sayFn(): string
let person: { name: string, age: number, sayFn(): string } = {
name: '张三',
age: 20,
sayFn(){
return '你好~';
}
}
通过接口可以简化对象的书写
interface Person {
name: string
age: number
sayFn(): string
}
let person: Person = {
name: '张三',
age: 20,
sayFn(){
return '你好~';
}
}
- array数组
//单一类型
let arr1: number[] = [1,2,3,4];
let arr2: string[] = ['张三','李四'];
let arr3: boolean[] = [true,false];
//联合类型,可以通过管道(|)将变量设置多种类型
let arr4: (number | string)[] = [10,'hello'];
- 函数类型注解
// 参数类型为数字类型,返回值为字符串
function someFn(name: number):string{
return '返回的是'+ name;
}
// 参数类型为数字类型,返回值为数字类型
function addFn1(num1: number,num2: number): number{
return num1+num2;
}
// 书写方式2
const addFn2 = (num1: number,num2: number): number => {
return num1+num2;
}
// 参数可选值
function mySlice(start?: number, end?: number){
console.log('开始索引',start,'结束索引',end);
}
-
同class约束兼容相同,函数也可以相互兼容
- class类
- 设置class中属性类型;
class Person {
name: string
age: number
constructor(name: string,age: number){
this.age = age;
this.name = name;
}
}
let P1 = new Person('张三',20);
console.log('类的使用: '+P1.name,P1.age);
- 设置class中方法
class Countobj {
x = 10
y = 20
//实例方法
scaleFn(n: number): void{
this.x*=n
this.y*=n
}
};
let P1 = new Countobj();
P1.scaleFn(2);
console.log(P1.x); //20
console.log(P1.y); //40
- class的继承,通过extends关键词实现
class Animal {
move(){
console.log('走~');
}
};
class Dog extends Animal {
say(){
console.log('汪~');
}
};
const Dog1 = new Dog();
console.log(Dog1.move() , Dog1.say());
- class的方法修饰符
public (公有的--默认值,可省略) protected(受保护的) private(私有的)
//-- 公有的,类与实例都可以用
class Animal {
public move(){
console.log('走~');
}
};
//-- 类与继承的类,里面都可使用,但是实例无法使用
class Animal {
protected move(){
console.log('走~');
}
say(){
this.move(); //可以调用
console.log('汪~');
}
};
let Dog = new Animal();
Dog.move(); //报错,无法使用
//-- 只有自己的类能用,继承的子类,实例均无法使用
class Animal {
private move(){
console.log('走~');
}
say(){
this.move(); //可以调用
console.log('汪~');
}
};
class Dog extends Animal {
eat(){
this.move(); //报错,无法使用
console.log('吃~');
}
};
let Dog = new Animal();
Dog.move(); //报错,无法使用
- class的属性修饰符readonly
class Person {
readonly age: number = 18
constructor(age: number){
this.age = age
}
};
let Person1 = new Person(20); //报错:属性无法修改,因为类型为readonly
- class类型约束,可以相互兼容 - (兼容规则: 约束参数少,可以被约束多的兼容)
class Point { x:number, y:number }
class Point3D { x:number, y:number, z:number }
const p: Point = new Point3D()
类型注解 - (typescript新增类型)
- 类型推断
- 没有明确指出类型的地方;
- 触发类型推论的2种场景:
情况1.声明变量并初始化时候
情况2.决定函数返回值时候;
注:
如果不知道类型,可以通过鼠标放在变量名称上,利用VSCode的提示来查看类型;
情况1: 声明变量并初始化值
let age: number
age = 18;
//等同于 声明变量类型,赋值为18
let age = 18;
情况2: 根据函数参数和内部处理,判断返回值
function addFn(num1: number, num2: number){
return num1+num2;
}
// 等同于
function addFn(num1: number, num2: number): number
- void、any、可选值、默认值
- 函数中没有返回值的情况下,设置其为void
function hiFn(name: string):void{
//函数没有返回值
console.log(name);
};
- 定义一个变量,不赋值,不设置任何类型约束,设置其为any
let some: any;
some = 1;
some = '你好';
- 可选值、默认值 - (可选值放参数最后)
function showMes( name: string, sex: string='female',age?: number ){
console.log('属性:', name, sex, age);
}
showMes('张三','male',15); //-- 属性: 张三 male 15
showMes('李四'); //-- 属性: 李四 female undefined
- 类型别名 - (通过关键词'type'简化书写)
type myArray = (string | number)[];
let arr1: myArray = [1,'hello']
-
同class约束兼容相同,类型别名也可以相互兼容
- 接口 - (通过关键词'interface'来描述对象类型,达到复用)
- 注意:接口类型中属性类型后没有;(分号)
interface Person {
name: string
age: number
say(): string
}
let person: Person = {
name: '张三',
age:20
say(){
return '你好~'
}
}
-
同class约束兼容相同,接口也可以相互兼容
- 类型别名与接口的区别与联系
- 相同点:都可以给对象指定类型;
- 不同点:接口只能为对象指定类型; 类型别名不仅可以为对象指定类型,也可为任意类型指定别名;
interface Person {
name: string
age: number
say(): string
};
type Person = {
name: string
age: number
say(): string
};
- 通过关键词implements,实现类的类型注解
interface Say {
say(): string
};
class Dog implements Say {
name: string
constructor(name: string){
this.name = name;
}
say(){
return '汪~';
}
};
let Dog1 = new Dog('小黑');
console.log(Dog1.say()); //'汪~'
console.log(Dog1.name); //'小黑'
- 元祖
- 数组存在一个问题,就是只能指定类型,但是无法指定个数,元素出来可以解决这个问题
let position: number[] = [51.51,96.56];
//通过元祖,来明确声明有几个变量
//明确只有两个元素,类型为number
let position: number[number,number] = [51.51,96.56];
- 枚举 -- (推荐使用字面量类型+联合类型,这比枚举要直观、简洁、高效)
- 通过关键词'enum'来声明枚举列表;
-
普通枚举 - 有值,从0开始自增长;
enum myList { up, down }
function showFn(name: myList){
console.log(name)
};
console.log(showFn.up); //0
console.log(showFn.down); //1
-
字符串枚举 - 不会自增长
enum myList { student1='zhangsan', student2='lisi' }
function showFn(name: myList){
console.log(name)
};
showFn(myList.student1); //'zhangsan'
- 通过索引找到值
enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2]; // 'Green'
- 字面量类型 -- (推荐使用!)
- 字面量类型与联合类型一起使用;
- 表示一组明确的可选值列表,在已有的选项中选择一项(只能在其中选择);
优势:
相对于string类型,使用字面量类型更加精确、严谨;
function showFn(direction:'up' | 'down' | 'left' | 'right'){
console.log(direction);
};
showFn('up'); //'up'
- 类型断言
- 使用as关键词实现类型断言;
- 关键词as后面明确一个具体的类型(HTMLAnthorElement是HTMLElement的子类型);
-
通过类型断言,aLink的类型变的更加具体,这样可以访问a标签的特有属性和方法;
技巧:
方法1:在浏览器控制台,通过console.dir()可以打印DOM元素信息,在属性列表最后,可查看元素的类型;
方法2:通过VSCode的类型推断,查看DOM的具体类型
//-- 将鼠标划入e,即可查看DOM具体类型 (如A标签的类型HTMLAnchorElement)
<input onChange={ e => {} } />
const aLink = document.getElementById('link') as HTMLAnchorElement;
const aLink = <HTMLAnchorElement>document.getElementById('link');
- 交叉类型
- 交叉类型关键词'&',功能类似接口继承extends,用于组合多个类型为一个类型;
interface Person { name: string }
interface Contact { phone: number }
type PersonDetail = Person & Contact
let person1: PersonDetail = {
name: '张三',
phone: 13717777777
};
- 通过交叉类型符号'&',解析后的效果:
type PersonDetail = {
name: string
phone: string
}
-
交叉类型与继承的关系
相同点:都可以合并对象类型
不同点:发生属性冲突时候,extends进行报错,&进行合并
- 泛型
- 无法提前定义类型约束,而是调用时候定义
- 下面代码中调用时候,可以根据泛型<xxxx>,从类型xxx约束参数和返回值
function showMes<Type>(value: Type): Type{
return value;
};
//-- 调用
console.log( showMes<string>('你好') ); //你好
console.log( showMes<number>(100) ); //100
- 泛型的继承,关键词(interface、extends)
表示传入的类型必须具有length属性
,否则会报错;
interface Length {
length: number
};
function showMes<Type extends Length>(value:Type):Type{
cosnole.log(value.length);
return value;
};
showMes([1,2,3]); //参数数组,返回数组,拥有length属性
showMes('abcd'); //参数字符串,返回字符串,拥有length属性
showMes({ length:10, name:'张三’ }); //参数对象,返回对象,拥有length属性
泛型的类型变量可以有多个,并且类型变量之间可以约束
-
实际使用中,数组就是一个泛型
-
通过关键词( 'Partial'、'Readonly'、'Pick' )来约束泛型
-
泛型 - 索引类型签名
类型映射
type Props = { a: number; b: string; c: boolean }
type Type3 = { [ key in keyof Props ]: number }
//-- 解析后
type Type3 = {
a: number
b: number
c: numner
}
实战使用ts
-
根据.js文件书写.d.ts文件
-
通过关键词‘declare’来区分js中已有的变量声明,表示仅为进行变量约束,而非重新定义变量