TypeScript学习笔记-入门到实战

TypeScript学习笔记

说明

根据B站视频Typescript教程_Typescript视频教程 ts入门实战视频教程-2019年5月更新整理,纯手敲

TS简介

Ts微软开发,包含ES6、包含ES5

编译 tsc xx.ts
每一次都得编译,可以自动编译

开发工具中配置typescirpt自动编译

vscode:

  1. 创建tsconfig.json文件 tsc --init 生成配置文件
  2. tsconfig.json配置文件中,修改outDir配置项,取消注释然后修改为.js
  3. vscode中,点击上方栏位run task,选择ts监听
  4. 完成

TS类型

与es5中的区别

    // es5:类型变化不报错
    var flag = true;
    flag = 234;

    // ts:必须指定类型
    typescript
    var flag:boolean=true;
    flag = 131;//报错

TS类型:
1.boolean
2.number
3.string
4.array数组:
方式1:var arr:number[] = [1,2,3]//制定arr里面全是数字
方式2:var arr:Array= [1,2,3]

5.元组类型(tuple)
方式1:属于数组的一种,即数组中每一个元素指定类型
方式2:var arr:[number, string]=[123,“this is ts”];
6.枚举类型(enum)

	// 常用来标识状态码
	enum Flag{
    	success=1,
        error=2
    }
    let f:Flag=Flag.error;
    console.log(f);// 2

	// 如果 没有标识符没有赋值,那么打印的就是下标
	enum Color{blue,red,orange};
    var c:Color=Color.red;
    console.log(c);  //1,下标
    enum Color{blue,red=3,orange};
    var c:Color=Color.red;
    console.log(c);  //3
    
    // 常用来标识状态码
    enum Err{
    	'undefined'=-1,
        'null'=-2,
    }
    var c:Err=Err.null
    console.log(c) // -2

7.任意类型any
类似ES5不指定变量类型的var
var num:any=123;
num = true;// 不报错

8.null类型和undefined其他数据类型的子类型
变量定义之后没有赋值,报undefined

// 一个元素可能是number类型,可能是null或者undefined
var num:number | undefined | null;

9.void,和java一样 没有返回值类型

    // 如果方法没有返回值
    function run():void{
        console.log('asdf')
    }
    // 如果方法有返回值:
    function run():number{
     return 1;
    }

10.never类型,代表从来不会出现的值,是其他类型(包括null‘和undefined)的子类型,代表从不会出现的值
自己理解为上述类型之外的数据类型

	// 如下,接收Err类型的数据
    var a:never;
    a = undefined;//报错
    a = (()=>{
        new Throw Err("报错")
    })()

函数的定义

ES5中:

    // 函数声明法
    function run(){
        return ...
    }
    //匿名函数
    var run2 = function(){
        return ..
    }

TS中:

    //函数声明法
    function run():number{
        return 123;
    }
    // 匿名函数
    var fun2=function():number{
        return 123;
    }

ts中定义方法传参

    function getInfo(name:string, age:number):string{
        return name + " " + age;
    }
    var getInfo= function(name:string, age:number):string{
        return name+age;
    }

方法可选参数

// es5里方法实参和形参可以不一样,但是ts必须一致,如果不一样就需要配置可选参数

  • 参数后边加?可以设置参数可选传
  • 可选参数必须配置到参数的最后边
    function getInfo(name:string, age?number):string{
    return …
    }

默认参数

// 默认参数,直接在形参赋值
function getInfo(name:string, age:number=20):string{
return…
}

剩余参数

function sum(a:number, b:number, c:number, d:number):number{
return a+b+c+d;
}

// 三点运算符:接收不固定参数的(剩余参数)的值
function sum(…rest:number[]):number{
var sum= 0 ;
for(var i=0; i<rest.length;i++){
sum+=rest[i];
}
return sum;
}

函数重载

// 类似java,同名但是不同参数的多个函数方法
// ts为了兼容es5,以及es6,和java有区别
// es5中,出现同名方法时候,下面的方法会替换上面的方法
ts中的重载:

    function getInfo(name:string):string;
    function getInfo(age:number):string;
    function getInfo(str:any):any{
        if(typeof str ==="string"){
            return "我叫:"+ str;
        }else{
            return "我的年龄是:" + str;
        }
    }

箭头函数

箭头函数里面的this指向上下文

1、ES5中定义类:

    function Person(){
        this.name='张三';
        this.age = 20;
    }
    var p = new Person();
    alert(p.name);

2、构造函数和原型链里面定义

	// 声明的构造方法
    function Person(){
        this.name = "张三";
        this.age=20;
        this.run = function()){
            alert(this.name+"在运动");
        }
    }
    // 原型链的属性和方法
    Person.prototype.sex="男";
    Person.prototype.work=function(){
        alert(xx)
    }
    var p = new Person();
    p.work();

3、静态方法

4、es5中的继承-对象冒充继承

    // 要实现Web类 继承 Person类  原型链+对象冒充的组合继承模式
    function Person(){
        this.name = "张三";
        this.age=20;
        this.run = function()){
            alert(this.name+"在运动");
        }
    }
    // 原型链的属性和方法
    Person.prototype.sex="男";
    Person.prototype.work=function(){
        alert(xx)
    }
    // 要实现Web类 继承 Person类
    function Web(){
        Person.call(this); //对象冒充继承
    }

    var w = new Web();
    w.run();//执行父类Person的run,对象冒充可以继承构造函数里面的属性和方法
    w.work();// 对象冒充可以继承构造函数的属性和方法 但是没办法继承原型链的属性和方法(prototype)

关于call:

    function add(c, d) {
      return this.a + this.b + c + d;
    }

    const obj = { a: 1, b: 2 };

    console.error(add.call(obj, 3, 4)); // 10
    大统上的说法就是,call改变了this的指向。然后,介绍this xxx什么一大堆名词,反正不管你懂不懂,成功绕晕你就已经ok了,但是实际发生的过程,可以看成下面的样子。

    const o = {
      a: 1,
      b: 2,
      add: function(c, d) {
        return this.a + this.b + c + d
      }
    };

    给o对象添加一个add属性,这个时候 this 就指向了 o,
    o.add(5,7)得到的结果和add.call(o, 5, 6)相同。

5、原型链继承方法

    function Person(){
        this.name = "张三";
        this.age=20;
        this.run = function()){
            alert(this.name+"在运动");
        }
    }
    // 原型链的属性和方法
    Person.prototype.sex="男";
    Person.prototype.work=function(){
        alert(xx)
    }
    // web原型链方式继承 person
    function web(){

    }
    web.prototype= new person();// 原型链继承

    web.work();// 可以工作,可以继承原型链属性方法

6、原型链实现继承的问题??无法给父类传参

    function Person(){
        this.name = "张三";
        this.age=20;
        this.run = function()){
            alert(this.name+"在运动");
        }
    }
    // 原型链的属性和方法
    Person.prototype.sex="男";
    Person.prototype.work=function(){
        alert(xx)
    }
    var p = new person('李四', 20);
    p.run(); // 没问题
    // 继承,无法给父类传参
    function Web(name,age){

    }
    Web.prototype= new Person();
    var w = new Web('sss', 20);
    w.run();// 父类会alert出来“undefiend在运动”
    // 实例化子类时候没法给父类传参

7、原型链+构造函数的组合继承模式

    function Person(){
        this.name = "张三";
        this.age=20;
        this.run = function()){
            alert(this.name+"在运动");
        }
    }
    // 原型链的属性和方法
    Person.prototype.sex="男";
    Person.prototype.work=function(){
        alert(xx)
    }
    var p = new person('李四', 20);
    p.run(); // 没问题
    // 继承,无法给父类传参
    function Web(name,age){
        Person.call(this,name,age); // 对象冒充继承 可以继承构造函数里面的属性和方法 实例化子类可以给父类传参
    }
    Web.prototype = new Person();// 实例化
    var w = new Web('sss', 20);
    w.run();// 父类会alert出来“undefiend在运动”
    // 实例化子类时候没法给父类传参

8、原型链+对象冒充的另一种写法

    function Person(){
        this.name = "张三";
        this.age=20;
        this.run = function()){
            alert(this.name+"在运动");
        }
    }
    // 原型链的属性和方法
    Person.prototype.sex="男";
    Person.prototype.work=function(){
        alert(xx)
    }
    var p = new person('李四', 20);
    p.run(); // 没问题
    // 继承,无法给父类传参
    function Web(name,age){
        Person.call(this,name,age); // 对象冒充继承 可以继承构造函数里面的属性和方法 实例化子类可以给父类传参
    }
    Web.prototype = Person.prototype; // 和方法7中不同的是这里!!!
    var w = new Web('sss', 20);
    w.run();// 父类会alert出来“undefiend在运动”
    // 实例化子类时候没法给父类传参

类的定义

1、ts中定义类,类似java:

    class Person(){
        name:string; //属性 前面省略了private
        construtor(n:string){
            this.name = n;
        }
        run():void{
            log(this.name);
        }
    }

2、继承

    class Web extends Person{
        constructor(name:string){
            super(name);
        }
    }

    var w = new Web("李四");
    alert(w.run());

3、类里面的修饰符

    ts提供了三种修饰符:
    public(默认的): 公有 在类里面、子类类外边都可以访问
    protected:在类里面、子类里面可以访问、在类外部无法访问
    private:在类里面可以访问、子类、类外部没法访问

静态属性 静态方法

    function Person(){
        this.run1=function(){// 实例方法

        }
    }

    Person.run2=function(){} // 静态方法

    调用实例方法:(实例化之后才能调用的)
    var p = new Person();
    p.run1();

    调用静态方法:
    Person.run2();

为什么会有静态方法和实例方法之分?
JQuery的实例方法css()方法和静态方法$.get()方法大概原码为:

// 生命一个节点/元素对象
function Base(element){
    this.element = 获取dome节点的方法;
    this.css = function(str, value){
        this.element.style[str] = value;
    }
}
// $方法去实例化这个BAse对象、实例方法
function $(element){
    return new Base(element); // 实例化一个方法

}
// 静态方法get
$.get(){
    。。。
}
// 那么css调用时候就可以这样写
实例方法:
$("#box").css("color", "red");

静态方法:
$.get('url', function(){

)

另一种方式声明静态方法,利用static关键字声明:

class Person{
    public name:string;
    static sex = "男";
    constructor(name:string){
        this.name = name;
    }
    static print(){// 静态方法 里面没办法直接调用类里面的属性,
        alert("静态方法:"+Person.sex);// 如果调用this.name就会报错!!!
    }
}
// $.get(){// jq里面的get就是静态方法

}

多态

父类定义一个方法不去实现,让继承他的子类去实现,每一个子类都有不同的表现

抽象方法

// 用来定义一个标准
// ts中的抽象类,它是提供其他类继承的基类,不能直接被实例化
// 用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现
// abstract 抽象方法只能放在抽象类中
// 抽象类和抽象方法用来定义标准,基类要求他的子类必须包含某种方法

// 抽象方法只能出现在抽象类中
// 抽象类中必须包含至少一个抽象方法,不然没有意义

    abstract class Animal{

        // 省略构造方法
        abstract eat():any;
    }

    // 抽象类无法直接实例化
    var a = new Animal();// 这句话是错的

    class Dog extends Animal{
        // 省略构造方法
        eat(){
            console.log(this.name + '吃');
        }
    }

    var d = new Dog("sss")
    d.eat();// sss吃

接口

// 也是定义标准,定义了行为和动作的规范
1、属性接口

    // 定义了这个方法的参数是jsonObjec,而且必须有
    function printLabel(labelInfo:{label:string}):void{
        console.log(labelInfo.label);
    }
    printLabel("ssss"); // 错误

    printLabel({name:"asdf"}); // 错误
    printLabel({label:"sss"}); // 正确,打印sss

2、接口,对批量方法进行约束

    // 对批量方法传入参数进行约束
    // 传入对象的约束

    // 声明类型
    interface FullName{
        firstName:string; // 注意是;结束
        secondName:string;
    }
    // 方法名中引用FullName类型
    function printName(name:FullName){
        log(name.firstName +"  "+ name.secondName);
    }

    // 调用方式一(下方调用方式是会报错的,interface定义的属性object只能包含firstName和secondName)
    printName({
        age:20,
        firstName: "张",
        secondName: "三"
    })

    // 调用方式二,下方引入其他的object即可忽略多余参数
    var obj = {
        age:20,
        firstName: "张",
        secondName: "三"
    };
    printName(obj);// 这个不报错

3、接口、可选属性,加?问号表示可传可不传

    interface FullName{
        firstName:string;
        secondName?:string;// secondName可传可不传
    }

4、模拟封装一个ajax

    interface Config{
        type:string;
        url:string;
        data?:string;
        dataType:string;
    }

5、函数类型接口、对方法传入的参数、以及返回值进行约束、批量约束
// 例子:定义加密的函数类型接口

interface encrypt{
	// 定义了函数参数为string、value,返回string
	(key:string,value:string):string;
}

var md5:encrypt=function(key:string, value:string):string{
	// 实现encrypt类型的函数	return key+value;//模拟下
}

6、可索引接口:对数组、对象的约束
ts定义数组方法:

     var arr:number[]=[123,234];
     var arr1:Array<string> = ['123', '222'];

// 对数组的约束,数组类型接口

    interface UserArray{
        // 表示数组中index必须是number,value必须是string
        [index:numer]:string;
    }
    var arr:UserArray=['123', '22312'];

// 对对象的约束,对象类型接口

    interface UserObj{
        [index:string]:string;
    }
    var obj:UserObj={name:"2342"};

// 对类的约束,类类型接口,和抽象类有点相似

    interface Animal{
        // 规定实现类必须要有name属性和eat方法
        name:string;
        eat(str:string):void;
    }
    class Dog implements Animal{
        name:string;// 若没此属性,ts会编译报错
        constructor(name:string){
            this.name = name;
        }
        eat(){
            log("eat")
        }
    }

// 接口的扩展:接口可以继承接口

	interface Animal{
    	eat():void;
    }

    interface Person extends Animal{
    	work():void;
    }

    class Web implements Person{
    	public name:string;
        constructor(name:string){
        	this.name = name;
        }

        // eat必须定义
       	eat(){
        	log(this.name+"吃")
		}

        // work也必须定义
       	work(){
        	log(this.name+"工作")
		}
    }
	interface Animal{
    	eat():void;
    }

    interface Person extends Animal{
    	work():void;
    }

	class Programmer{
    	构造方法省略
        
        coding(code:string){
        	log(this.name+ "  "+code)
        }
    }
    class Web extends Programmer implements Person{
    	public name:string;
        constructor(name:string){
        	this.name = name;
        }

        // eat必须定义
       	eat(){
        	log(this.name+"吃")
		}

        // work也必须定义
       	work(){
        	log(this.name+"工作")
		}
    }

泛型

和any有什么区别?

  • any放弃了类型检查
  • 如果想做到传入什么类型就返回什么类型,例如传入number就返回number,这时候就可以使用泛型
	function getData(value:any):any{
    	return ""//什么类型都可以
    }

泛型:

  • 软件工程中,我们不仅要创建一致的定义好的API,同时也要考虑可重用性,组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能
  • 在像c#和java中,可以使用泛型来创建可重用的组件,一个组件可支持多种类型的数据,这样用户就可以以自己的数据类型来使用组件
  • 通俗理解:泛型就是解决 类 接口 方法的重用性、以及对不特定数据类型的支持
  • 可以支持不特定的数据类型
    function getData<T>(value:T):T{
        return value;//传入什么返回什么
    }
    // 这样调用
    getData<number>(123123);
    getData<string>("12131");

    // 也可以写成:
    function getData<T>(value:T):any{
        return value;//传入什么返回什么
    }

// 泛型类,比如有个最小堆算法,需要同时支持返回数字和字符串两种类型,通过类的泛型来实现,示例:

    // 定义泛型类
    class MinClass{
        public list:number[]=[];
        add(num:number){
            this.list.push(num);
        }
        min():number{
            var minNum = this.list[0];
            for(var i = 0;i<this.list.length;i++){
                if(minNum>this.list[i]){
                    minNum=this.list[i];
                }
            }
            return minNum;
        }
    }
    // 调用
    var m = new MinClass();
    m.add(3);
    m.add(2);
    log(m.min());// 2

但是上边的只能传入数字类型,是否可以用泛型解决?可以:

    class MinClass<T>{
        public list:T[]=[];
        add(num:T):void{
            this.list.push(num);
        }
        min():T{
            var minNum = this.list[0];
            for(var i = 0;i<this.list.length;i++){
                if(minNum>this.list[i]){
                    minNum=this.list[i];
                }
            }
            return minNum;
        }
    }
    // 调用,实例化时候要先声明参数类型<bumber
    var m1 = new MinClass<number>();
    m1.add(2);
    m1.add(4);
    log(m.min());// 2
函数类型接口

指定特殊类型的方法:

	interface ConfigFn{
    	(value1:string, value2:string):string;
    }

    var setData:ConfigFn=function(value1:string, value2:string):string{
    	return value1 + value2;
    }
    setData("name", "张三);

泛型接口写法1:

	interface Config{
    	<T>(value:T):T;
    }

    var getData:ConfigFn=function<T>(value:T):T{
    	return value;
    }

    getData<string>("张三");

泛型接口写法2:

    interface Config<T>{
    	(value:T):T;
    }

	function getData<T>(value:T):T{
    	return value;
    }

    var myGetData:ConfigFn<string>=getData;

    myGetData("张三");

// 把类作为参数来约束数据传入的类型

	class User{
    	username:string | undefined;
        password:string | undefined;
    }
    class MySqlDb{
    	add(user:User):boolean{
        	console.log(user);
            retrun true;
        }
    }

    // 调用
    var u = new User();
    u.username="张三";
    u.password="123456";

    var Db = new MySqlDb();
    Db.add(u);// console.log(u)

// 上述方法可以改为泛型类

	// 操作数据库的泛型类,这样可以规范插入数据库数据的类规范
	class MySqlDb<T>{
    	add(info:T):boolean{
        	log(info);
            return true;
        }
    }
    
    // 想给User表增加数据
    // 1、定义一个User类 和数据库进行映射
    class User{
    	username:string | undefined;
        password:string | undefined;
    }
    var u = new User();
    u.username= '张三';
    u.password="2312";
    var Db = new MySqlDb<User>();// 这一步很关键,要定义User类型
    Db.add(u);

    // 2、文章类,数据库映射
    class Article{
    	title:string | undefined;
        desc:string | undefined;
        status:number | undefined;
        constructor(params:{
        	title:string | undefined;
            desc:string | undefined;
            status?number | undefined;// status可选参数
        }){
        	this.title=params.title;
            this.desc=params.desc;
            this.status=params.status;
        }
    }

    // 调用
    var a = new Article({
    	title:"分类",
        desc:"111",
        status:1
    })

    //类当前参数的泛型类
    var Db = MySqlDB<Article>();// 指定类型
    Db.add(a);// log a

实战:要实现TS封装统一操作Mysql Mongodb Mssql的底层库

	// 先定义一个接口,用于提供各类型数据库规范
    interface DBI<T>{
    	add(info:T):boolean;
        update(info:T, id:number):boolean;
        delete(id:number):boolean;
        get(id:number):any[];
    }

    // 定义一个操作mysql的类,注意 要实现泛型接口 这个类也应该一定是个泛型类
    class MysqlDb<T> implements DBI<T>{
    	add(info:T): boolean{
        	log(info);
        }
        update...
        delete...
        get...
    }

    // 调用 操作数据表,定义一个User类和数据库进行映射,并进行MySql数据的插入操作
    class User{
    	username:string | undefined;
        password:string | undefined;
    }

    var u = new User();
    u.username = "张三";
    u.password="213";

    var oMysql = new MysqlDb<User>();// 声明User类型参数
    oMysql.add(u);// 插入

模块

概念:

  • 把一些公共的功能单独抽离成一个文件作为一个模块
  • 模块里面的变量 函数 类等默认都是私有的,如果我们要在外部访问模块内的数据(函数、变量、类)
  • 我们就需要通过export暴露模块里面的数据
  • 然后其他地方通过import引入模块就可以使用模块内的数据

模块暴露export:

    //  方式一
    export function a(){
        ...
    }
    // 方式二
    function a(){
        ...
    }
    export { a }

模块导入import:

    import { a, a as alias } from "xxx";
    a();
    alias();

模块默认导出default,一个模块只能用一次
暴露:

    export default a(){

    }

引入(不用花括号):

    import a from "aaa";
    a();

DB库用模块化封装// 省略了,代码比较简单,可以参考这里

ts命名空间
内部模块,主要用于组织代码,避免命名冲突,
个人理解:模块之中再分模块
定义模块、并导出不同命名空间:

    export namespace A{
        interface Animal{
            name: string;
            eat(): void;
        }
        export Class Dog implements Animal{
            name: string;
            constructor(name:string){
                this.name = name;
            }
            eat:void(){
                log(this.name +"在空间A中吃狗粮")
            }
        }
    }
    export namespace B{
        interface Animal{
            name: string;
            eat(): void;
        }
        export Class Dog implements Animal{
            name: string;
            constructor(name:string){
                this.name = name;
            }
            eat:void(){
                log(this.name +"在空间A中吃狗粮")
            }
        }
    }

调用:

    import {A, B} from "xxx";
    var d = new A.Dog("小黑");
    d.eat();// 小黑在空间A中吃狗粮
    
    var dog = new B.Dog("小花");
    dog.eat(); // 小花在空间B中吃狗粮
装饰器

定义:

  • 装饰器是一种特殊类型的声明,他能够被附加到类声明,方法,属性或者参数上,可以修改类的行为。
  • 通俗的将装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。
  • 常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
  • 装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)
  • 装饰器是过去几年中js最大的成就之一,已经是ES7的标准特性之一
类装饰器:普通装饰器
    function logClass(params:any){
        console.log(params);
        // params就是当前类
        params.prototype.apiUrl = "动态扩展的属性";
        params.prototype.run=function(){
            console.log("我是一个run方法");
        }
    }

    @logClass  // 类装饰器,普通装饰器,无法传参,默认吧class传入
    class HttpClient{
        constructor(){

        }
        getData(){

        }
    }

类装饰器:装饰器工厂

作用:

  1. 修改构造函数
  2. 扩展类属性和方法
    定义:
    function logClass(params:string){// params是下方传过来的参数
        return function(target:any){// target相当于是默认传过来的
            log(target);
            log(params);
            target.prototype.apiUrl = params;
        }
    }

    @logClass("https://baidu.com")// 可以传参
    class HttpClient{
        constructor(){

        }
        getData(){

        }
    }

    var http:any = new HttpClient();
    console.log(http.apiUrl);// https://baidu.com

	可以修改构造函数的写法
    function logClass(target:any){
        log(target);
        return class extends target{
            apiUrl:any = "我是修改后的新数据";

            getData(){
                this.apiUrl = this.apiUrl + "----";
                log(this.apiUrl);
            }
        }
    }

    @logClass
    class HttpClient{
        public apiUrl:string | undefined;
        constructor(){
            this.apiUrl = "我是构造函数里面的apiUrl"
        }
        getData(){
            log(this.apiUrl)

    }

    var http= new HttpClient();
    http.getData();
属性装饰器

作用:

  1. 可以给属性赋值
    // 类装饰器
    function logClass(params:string){// params是下方传过来的参数
        return function(target:any){// target相当于是默认传过来的
            log(target);
            log(params);
            target.prototype.apiUrl = params;
        }
    }

    // 属性装饰器
    function logProperty(params:any){
        // 固定写法,参数中,target为类对象,attr为参数名称
        return function(target:any, attr:any){
            log(target);
            log(attr);
            target[attr] = params;
        }
    }

    @logClass("https://baidu.com")// 可以传参
    class HttpClient{

        // 这个属性修饰器的作用就是给url赋值初始值
        @logProperty("http://baidu.com")
        public url:any | undefined;
        constructor(){

        }
        getData(){

        }
    }

    var http:any = new HttpClient();
    console.log(http.apiUrl);// https://baidu.com
方法装饰器

用的是最多的

    function get(params:any){
        return function(target:any, methodName:any, desc:any){
            log(target); // 类属性
            log(methodName); // 方法名字 getData
            log(desc); // 方法的描述,desc.value是方法描述
            target.apiUrl = "xxx"; // 修改雷属性
            target.run=function(){
                log("run");
            }
        }
    }
    class HttpClient{
        public url:any | undefined;
        constructor(){

        }
        @get("https://www.baidu.com")
        getData(){
            log(this.url);
        }
    }

    var http:any = new HttpClient();
    log(http.apiUrl); // https://www.baidu.com‘
    http.run(); // log run

修改当前的方法(主要作用是装饰方法,并把方法的参数给变换类型):

    // 这个方法装饰其主要作用就是把参数都给格式化成string类型
    function get(params:any){
        return function(target:any, methodName:any, desc:any){
            log(target); // 类属性 
            log(methodName); // 方法名字 getData
            log(desc.value); // 方法

            // 想修改下方法,装饰一下,让他们的所有参数变成string类型,并且打印出来
            var oMethod = desc.value;
            desc.value = function(...args:any[]){
                args = args.map((value) => {
                    return String(value);
                })

                // 利用apply进行对象冒充,对getdata进行修改,如果没有apply就相当于是把getData方法给替换掉了

                oMethod.apply(this, args);// this就是指function(...args:any[])这个函数
            }
        }
    }
    class HttpClient{
        public url:any | undefined;
        constructor(){

        }
        @get("https://www.baidu.com")
        getData(...args:any[]){
            log(args);
            log("我是getData方法");
        }
    }
    
    var http:any = new HttpClient();
    http.get(123,"xxx"); 
    
    // 就会先打印["123", "xxx"]后打印 我是getData方法
方法参数装饰器

用的比较少,类装饰器也可以实现这个功能

  • 运行时候当做函数被调用,可以使用参数张诗琪为累的原型增加一些元素数据,传入下列三个参数:
  • 1对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  • 2方法的名字
  • 3参数在函数参数列表中的索引
    function logParams(params:any){
        return function(target:any, methodName:any, paramsIndex:any){
            log(params);// xxxx
            log(target); // 原型对象
            log(methodName);// getData
            log(paramsIndex); // 0
        }
    }
    class HttpClient{
        public url:any | undefined;
        constructor(){
            
        }
        
        getData(@logParams("xxxx") uuid:any){
            log(uuid); // iii
        }
    }
    
    var a = new HttpClient();
    a.getData("iii");
    
    先后输出:
    1. xxxx
    2. 原型对象
    3. getData
    4. 0 
    5. iii
    
装饰器执行顺序

当存在多个装饰器时候:

  • 执行优先级:属性装饰器>方法装饰器>方法参数装饰器>类装饰器
  • 如果有多个同样的装饰器,它会先从后边执行

其他参考资料

  1. 解释原型链,原型对象:点击这里
  2. 解释call()、apply():点击这里
  3. 解释prototype:点击这里
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值