学习笔记

视频地址 https://www.bilibili.com/video/av38379328

视频地址 https://www.bilibili.com/video/av77006938

1. 数据类型

  • 定义未赋值,设置默认值
  1. let a:number | undefined;
  2. a;// undefined
  3. a = 3;
  4. a; // 3
  • 定义数组
  1. // 方式1
  2. let arr: string[] = ["aa", "bb"];
  3. // 方式2
  4. let arr:Array<String> = ["aa", "bb"];
  • 定义枚举
  1. enum Flag {
  2. SUCCESS= 1,
  3. ERROR= 2
  4. };
  5. let leo:Flag = Flag.ERROR;
  1. enum Color {BLUE , RED, 'ORANGE'}
  2. let c:Color = Color.BLUE
  3. c; // 1

如果标识没有赋值 则它的值就是下标

  1. enum Color {BLUE , RED = 3, 'ORANGE'}
  2. let c:Color = Color.RED
  3. c; // 3

常量枚举:在编译阶段被移除。
使用场景:当我们不需要一个对象,而需要一个对象的值的时候。
好处:减少编译环境的代码。

  1. const enum Month {
  2. Jan, Feb, Mar
  3. }
  4. let month = [Month.Jan, Month.Feb, Month. Mar]
  5. // 下面是 编译结果 非常简洁
  6. var month = [0 /* Jan */, 1 /* Feb */, 2 /* Mar */];
  • 定义 void 类型,没有返回值
  1. function fun():void {
  2. }
  3. // 不能使用undefined 如果没有显式 return 语句的话,是默认返回 undefined。
  4. function fun():undefined {
  5. }
  • 定义 never 类型

包括 nullundefined 类型,是其他类型的子类型,代表从不会出现的值。
never 声明的变量只能用 never 类型的值所赋值。

  1. let a:never;
  2. a = 11; // err

2. 函数

  • 可选参数

必须定义在参数最后一个

  1. function fun(name:string, age?:number){
  2. // ....
  3. }
  • 剩余参数

三点运算符

  1. function fun(...res:number[]):number{
  2. // ....
  3. }
  • 函数重载
  1. function fun(name:string){
  2. // ....
  3. }
  1. function fun(age:number){
  2. // ....
  3. }

这时候 fun 方法传入 2 个参数,重载不会覆盖。

3. 类

3.1 概念介绍

类成员的属性都是实例属性,而不是原型属性。而类成员的方法都是实例原型的方法

  1. class Dog {
  2. constructor(name: string) {
  3. this.name = name;
  4. }
  5. name: string
  6. run(){}
  7. }
  8. console.log(Dog.prototype); // { run: f, constructor: f }
  9. let dog = new Dog("wangwang");
  10. console.log(dog); // Dog {name: "wangwang"}

类成员的方法编译后:
image.png
另外,实例的属性必须有初始值,或者在构造函数中被初始化:

  1. class Dog {
  2. // 第一种方式
  3. constructor(name: string) {
  4. this.name = name;
  5. }
  6. name: string
  7. // 第二种方式
  8. constructor(name: string) { }
  9. name: string = "dog"
  10. // 第三种方式
  11. constructor(name: string) { }
  12. name?: string
  13. }
  14. console.log(Dog.prototype); // { run: f, constructor: f }
  15. let dog = new Dog("wangwang");
  16. console.log(dog); // Dog {name: "wangwang"}

3.2 实例方法和静态方法

以 jquery 为例:

  1. // 静态方法 直接使用
  2. $.get(....)
  3. // 实例方法 需要先实例化
  4. $('.dom').css(....)

ts 中静态方法:

  1. class Per {
  2. static leo = "hi"
  3. static print () {
  4. console.log(Per.leo)
  5. }
  6. }
  7. // 使用静态属性
  8. Per.leo
  9. // 使用静态方法
  10. Per.print()

静态属性和静态方法转换后:
image.png

3.3 类的继承

  1. class Husky extends Dog {
  2. constructor(name: string, color: string) {
  3. super(name) // 必须调用 super 继承父类
  4. this.color = color;
  5. }
  6. color: string
  7. }

五种修饰符:

  • public : 对所有成员可见;
  • private :私有成员,只能在类里面调用,不能被类的实例调用,也不能被子类调用;
  • protected : 受保护成员,只能在类和子类中访问,不能在类的实例访问;
  • readonly : 只读属性,不能被更改,并且需要初始化;
  • static : 类的静态成员,只能通过类名来调用,不能通过类的实例调用。

构造函数中的参数也可以使用修饰符,作用是将参数自动变为实例的属性,就可以省略在类中的定义。

  1. class Husky extends Dog {
  2. constructor(name: string, public color: string) {
  3. super(name) // 必须调用 super 继承父类
  4. this.color = color;
  5. }
  6. }

3.4 抽象类

它提供其他类继承的基类,只能被继承,不能被实例化的类https://www.tslang.cn/docs/handbook/classes.html

abstract 关键字是用于定义抽象类和在抽象类内部定义抽象方法

  1. abstract class Animal {}
  2. // let animal = new Animal() ; // 报错 无法创建抽象类的实例
  3. class Dog extends Animal {} ; //可以

还有这个案例:

  1. // 抽象类必须有抽象方法
  2. // 抽象方法必须在抽象类中
  3. abstract class A {
  4. public name: string
  5. constructor(name:string){
  6. this.name = name
  7. }
  8. abstract eat (){ console.log('吃···') }
  9. }
  10. // 抽象类的子类必须实现抽象类里面的抽象方法
  11. class B extends A {
  12. constructor(name:string){
  13. super(name)
  14. }
  15. eat (){
  16. return this.name + '哈哈哈'
  17. }
  18. }
  19. let aa = new B(' hello ')

好处:抽离一些事务的共性,有利于代码的复用和拓展。也可以实现多态。
多态:父类定义方法,子类各自实现。

  1. abstract class Animal {
  2. eat() { console.log("eat") }
  3. abstract sleep(): void; // 抽象类中定义抽象方法,子类中去实现
  4. }
  5. class Dog extends Animal {
  6. constructor(name: string) {
  7. super();
  8. this.name = name;
  9. }
  10. name: string;
  11. sleep() {
  12. console.log("sleep"); // 子类实现父类定义的抽象方法
  13. }
  14. }; //可以
  15. let dog = new Dog("wang");
  16. dog.eat(); // eat
  17. dog.sleep(); // sleep

4. 接口

行为和动作的规范,对批量方法进行约束。

  • 属性类型接口
  1. interface Name {
  2. fName: string; // 必须
  3. sName?: string; // 可选
  4. }
  5. function F(name:Name){
  6. console.log(name.fName, name.sName)
  7. }
  • 函数类型接口
  1. interface f{
  2. (key: string, value: string): string
  3. }
  4. let md5:f = function(key: string, value: string): string{
  5. return key + value
  6. }
  • 可索引接口 (约束数组、对象)

如字符串索引 ,含义:用任意类型的字符串索引List,会获得对应类型的值。

  1. interface Arr {
  2. [index: number]: string
  3. }
  4. let a: Arr = ["aa", "bb"]
  5. interface Obj {
  6. [index: string]: string
  7. }
  8. let o: Obj = {
  9. aa: 'aaa',
  10. bb: 'bbb'
  11. }

需要注意:数值索引的返回值,必须是字符串索引返回值的子类。因为当使用数值作为索引时, JavaScript 会进行类型转换。

  1. interface Names {
  2. // 正确
  3. [x: string]: string
  4. [z: number]: string
  5. // 错误
  6. [x: string]: string
  7. [z: number]: number
  8. // 正确
  9. [x: string]: any
  10. [z: number]: number
  11. }
  • 类类型接口

implements 实现后面的接口。

  1. interface C {
  2. name: string;
  3. fun(str: string):void;
  4. }
  5. class P implements C {
  6. name: string
  7. constructor(name: string){
  8. this.name = name
  9. }
  10. fun(){
  11. console.log(this.name + 'in here~')
  12. }
  13. }
  14. let aa = new P('leo')
  • 接口拓展

接口继承其他接口。

  1. interface A {
  2. fun1():void;
  3. }
  4. interface B extends A{
  5. fun2():void;
  6. }
  7. class C implements B {
  8. public name: string;
  9. constructor(name: string){
  10. this.name = name
  11. }
  12. fun1(){ console.log(this.name) }
  13. fun2(){ console.log(this.name) }
  14. }
  15. let p = new C()
  16. p.fun1('leo')

一个接口可以继承多个接口:

  1. interface E extends A, B {
  2. sideLength: number;
  3. }

5. 泛型

简单理解:泛型就是解决类、接口和方法的复用性,以及对不特定数据类型的支持。
可以使用泛型来创建可重用的组件,一个组件支持多种类型的数据。

5.1 泛型函数

  • 普通写法:
  1. // 只能返回一种类型
  2. function fun1 (name: string): string{
  3. return name;
  4. }
  5. // 可以返回多种类型
  6. // 使用 any 则放弃使用类型检查
  7. function fun2 (name: any): any {
  8. return name;
  9. }
  • 使用泛型:

由于如果使用 any 可能出现可以传入 string 返回 number ,所以需要使用泛型,使得传入参数和返回类型参数一致。

T 表示泛型,具体什么类型是调用这个方法的时候决定的。

  1. function fun3<T>(name: T): T{
  2. return name;
  3. }
  4. fun3<number>(123); // 调用方法的时候决定
  5. fun3<string>('123');

也可以这么写:

  1. type Log = <T>(value: T) => T;
  2. let myLog: Log = log;

5.2 泛型类

  1. class C<T>{
  2. public list:T[] = [];
  3. fun1(name: T):void{
  4. this.list.push(name)
  5. }
  6. fun2():T{
  7. return this.list[0]
  8. }
  9. }
  10. let a1 = new C<number>()

5.3 泛型接口

  • 方法1:
  1. interface Config{
  2. <T>(name: T): T;
  3. }
  4. let getData : Config = function<T>(name: T): T{ return name; }
  5. getData<number>(123);
  6. getData<string>('aaa');
  • 方法2:
  1. interface Config{
  2. (name: T): T;
  3. }
  4. function getData<T>(name: T): T{ return name; }
  5. let fun1: Config<string> = getData;
  6. fun1('aaa')

还可以这样:

  1. interface Log<T> { // 约束了整个接口,使用时就需要指定类型
  2. (value: T) : T
  3. }
  4. let myLog: Log<number> = log
  5. // 也可以指定默认类型
  6. interface Log<T = string> {
  7. (value: T) : T
  8. }
  9. let myLog: Log = log

5.4 把类作为参数类型的泛型类

  • 以前将类作为参数类型,可以检查参数是否类型符合
  1. class User{
  2. name: string | undefined;
  3. pwd: string | undefined;
  4. }
  5. class Db {
  6. add(data: User): boolean{ return true }
  7. }
  8. let dd = new Data();
  9. dd.name = "aaa";
  10. dd.pwd = "1233";
  11. let D = new Db()
  12. D.add(dd);
  • 把类作为参数类型的泛型类
  1. class Db<T>{
  2. add(data: T): boolean{ return true }
  3. }
  4. class Data{
  5. name: string | undefined;
  6. pwd: string | undefined;
  7. }
  8. let dd = new Data();
  9. dd.name = "aaa";
  10. dd.pwd = "1233";
  11. let D = new Db<Data>()
  12. D.add(dd);

5.5 泛型约束

  1. interface Length {
  2. length: number
  3. }
  4. function log<T extends Length>(value: T): T{
  5. console.log(value, value.length)
  6. return value
  7. }
  8. log([1])
  9. log("123")
  10. log({length:1})

6. 模块

export 暴露模块, import 导入模块。

  • 方式1
  1. // 1.js
  2. export fun(){}
  3. export let a = 1
  4. // 2.js
  5. import { fun, a } from './1.js'
  6. fun()
  • 方式2
  1. // 1.js
  2. fun(){}
  3. let a = 1
  4. export { fun, a }
  5. // 2.js
  6. import { fun, a as ha } from './1.js' // 将 a 重命名为 ha
  7. fun()
  • 方式3 默认导出
  1. // 1.js
  2. export default fun (){}
  3. // 2.js
  4. import fun from './1.js'

7. 命名空间

在模块内部,用于组织代码,避免命名冲突。

使用 namespace 关键字作为划分,将指定的代码放到命名空间中,并使用 export 暴露其中变量和方法(类似将命名空间当做模块),调用的时候,需要使用 空间名称 来获取对应变量方法。

  1. namespace A{
  2. interface Leo{
  3. name: string
  4. }
  5. export let aa = 1;
  6. // ....
  7. }
  8. namespace B{
  9. interface Leo{
  10. name: string
  11. }
  12. export let aa = 2;
  13. // ....
  14. }
  15. console.log(A.aa) // 1
  16. console.log(B.aa) // 2

8. 装饰器

是一种特殊类型的声明,能够被附加到类生命,方法,属性或参数上,可以修改类的行为。

常见:类装饰器,属性装饰器,方法装饰器,参数装饰器。

装饰器写法:普通装饰器(无法传参),装饰器工厂(可传参)

8.1 普通装饰器(无法传参)

  1. // 类装饰器
  2. function myClass(params: any){
  3. // params 当前类
  4. params.prototype.url = 'www.xxx.com'
  5. params.prototype.fun1 =function (){
  6. console.log('fun')
  7. }
  8. }
  9. @myClass
  10. class FunClass{
  11. constructor(){ }
  12. fun(){ }
  13. }
  14. let a: any = new FunClass()
  15. a.url; // 'www.xxx.com'
  16. a.fun1(); // 'fun'

8.2 装饰器工厂(可传参)

  1. // 类装饰器
  2. function myClass(params: string){
  3. // params 传入参数
  4. // target 当前类
  5. return function (target: any){
  6. console.log(params, target)
  7. target.prototype.url = params
  8. }
  9. }
  10. @myClass('www.xxx.com')
  11. class FunClass{
  12. constructor(){ }
  13. fun(){ }
  14. }
  15. let a: any = new FunClass()

8.3 重载构造函数

用来修改当前类的构造函数,属性和方法。

  1. // 类装饰器
  2. function myClass(target: any){
  3. // target 当前类
  4. return class extends target{
  5. link : any = 'bbbbbb'
  6. fun1(){
  7. console.log('-----' + this.link)
  8. }
  9. }
  10. }
  11. @myClass
  12. class FunClass{
  13. public link : string | undefined;
  14. constructor(){
  15. this.link = 'aaaaaaaaaaaa'
  16. }
  17. fun(){
  18. console.log(this.link)
  19. }
  20. }
  21. let a: any = new FunClass()
  22. a.fun1(); // '-----bbbbbb'

8.4 属性装饰器

  1. // 类装饰器
  2. function myClass(params: string){
  3. return function (target: any){
  4. }
  5. }
  6. // 属性装饰器
  7. function myAttr(params: any){
  8. return function (target:any , attr:any){
  9. // target 当前类原型对象 , attr 当前属性
  10. target[attr] = params
  11. }
  12. }
  13. @myClass('www.xxx.com')
  14. class FunClass{
  15. @myAttr('leo')
  16. public link : any | undefined
  17. constructor(){ }
  18. fun(){ console.log(this.link) }
  19. }
  20. let a: any = new FunClass()
  21. a.fun(); // 'leo'

8.5 方法装饰器

用来监视、修改或替换方法的定义。
方法装饰会在运行时,传入三个参数:

  • 对于静态方法来说是类的构造函数,对于实例方法是类的原型对象。
  • 方法的名称。
  • 方法的属性描述符。
  1. function logMethod(params: any){
  2. return function(target:any, methodName:any, desc:any){
  3. console.log(target, methodName, desc)
  4. // 拓展原型方法和属性
  5. target.url = 'www.baidu.com'
  6. target.leo = function(){
  7. console.log('hi leo~~~')
  8. }
  9. // 修改原型方法 eg将参数转换成 string 类型
  10. let old = desc.value;
  11. desc.value = function(...args:any[]){
  12. args = args.map(val => String(val))
  13. console.log(args)
  14. old.apply(this,args)
  15. }
  16. }
  17. }
  18. class Myclass {
  19. public url: any | undefined;
  20. constructor(){}
  21. @logMethod('hi leo')
  22. getData(){
  23. console.log('this.url',this.url)
  24. }
  25. }
  26. let my:any = new Myclass()
  27. console.log(my.url)
  28. my.leo()
  29. my.getData(123,'aaa')

8.6 方法参数装饰器

方法参数装饰器表达式会在运行时当做函数被调用,可以使用参数装饰器为类的原型添加一些元素数据。

方法装饰会在运行时,也是传入三个参数,和方法装饰器一样。

  1. function logMethod(params: any){
  2. return function(target:any, methodName:any, paramsIndex:any){
  3. console.log(params, target, methodName, paramsIndex)
  4. target.url = 'www.baidu.com'
  5. }
  6. }
  7. class Myclass {
  8. public url: any | undefined;
  9. constructor(){}
  10. getData( @logMethod('uuid') uuid:any){
  11. console.log('this.uuid',uuid)
  12. }
  13. }
  14. let my:any = new Myclass()
  15. my.getData(123)
  16. console.log(my.url)

8.7 装饰器执行顺序

属性装饰器 - 方法装饰器 - 方法参数装饰器 - 类装饰器

多个相同类型装饰器,会从下开始执行,比如:

  1. @fun1('b')
  2. @fun2('a')
  3. class Hello {}

则先执行装饰器 fun2 再执行装饰器 fun1