TypeScript学习笔记-入门到实战_E-A-D-G-B-E的博客-程序员ITS301

技术标签: typescript  javascript  

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:点击这里
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/z591391960/article/details/105667767

智能推荐

6.3. Deleting Data_丹心明月的博客-程序员ITS301

6.3. Deleting Data6.3.删除数据So far we have explained how to add data to tables and how to change data. What remains is to discusshow to remove data that is no longer needed. Just as adding data is only possible in whole rows, youcan only remove entire ..

你给需求文档,AI就能帮你开发安卓App_QbitAl的博客-程序员ITS301

丰色 发自 凹非寺量子位 报道 | 公众号 QbitAI用自然语言生成代码不算稀奇,但现在,这项技术涉及的业务范围真是越来越广了。就有一个叫做Text2App的“AI”,你“喂”给它一串文...

Mac安装JDK8_胖墩儿吖的博客-程序员ITS301_mac安装jdk8

Mac上安装JDK1.8版本主要分为以下三个步骤:1、下载JDK1.8的安装包2、安装JDK1.83、配置JDK的系统环境变量1、下载JDK1.8的安装包JDK安装包的下载推荐从Oracle的官方网站下载、如果找不到官网可以百度Java8、主要辨别广告、选择错误的网站、下图所示搜索到的词条就是Oracle的官方网站:笔者下载时候下载网站为:JDK1.8下载地址、选择MacOS x64版本的进行下载、目前需要登录后才可以下载、按照登录即可自动下载、木有账号的可以注册后登录下载:2、安装JDK1.8

csvn_bateng6464的博客-程序员ITS301

需要java-JDKCollabNetSubversionEdge1检查java-JDK是否安装java -version如果没有去官网下载安装在/etc/profile里p配置环境变量在末尾加入JAVA_HOME=/usr/java/jdk1.7.0JRE_HOME=/usr/java/jdk1.7.0/jrePATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/...

put与mput_ftpput命令详解 ftp put命令使用哪个端口?_weixin_39584405的博客-程序员ITS301

put 后面加的是我要上传文件的绝对路径,比如D:\dbsoftware\AIX10gR2sof加绝对路径 get是下载 我考微软认证 都没怎么看linux了 忘了有点 put 加FTP服务器的路径 然后加本地要上传的路径谁能跟我详细说说DOS下的FTP命令的PUT和GET的具体用法谁能跟我详细说说DOS下的FTP命令的PUT和GET的具体用法我现在只知道put是Windows98/2000中有一...

随便推点

Solr 获取searcher实例分析(转)_sofokiller的博客-程序员ITS301

每一个搜索请求都会持有一个searcher的引用,而不是创建一个新的searcher,处理完后会释放掉这个引用。Solr在初始化化时,通过SolrCore核心类要做很多的初始化工作,包过读取solrconfig.xml配置文件里的内容,代码如下:booleanQueryMaxClauseCount(); //设置布尔查询最多个数。initListeners(); //读取配置文

【Linux】目录文件权限的查看和修改_Jaymeng8848的博客-程序员ITS301

命令:chmod 777 scan_record.js格式:chmod 权限数字 文件名r 读权限read 4w 写权限write 2x 操作权限execute 1权限数字对应权限组说明:总共分为4部分【文件或文件夹】【owner权限】【group权限】【others权限】【文件是-,文件夹是d】【r/w/x相加】【r/w/x相加】【r/w/x相加】Linux档案的基本权限就有九个,分别是owner/group/others三种身份各有自己的read/write/execute

MySQL zip下载与安装_ooooo博ooooo的博客-程序员ITS301

一. MySQL下载1. 进入MySQL官网官网地址:https://www.mysql.com/2. 点击DOWNLOADS3. 点击Community(GPL) Downloads4. 找到MySQL Community Server,点击下面的DOWNLOAD,进入下载页5. 到了真正的下载页面,选择平台,选择版本(安装版和免安装版),点击Download6....

<Android> Canvas绘制__Lulixue_的博客-程序员ITS301_android canvas 绘制

文本水平+垂直居中drawText(txt, x, y, paint)其中y代表的是baseline, exactCenterY代表的是绝对的中间Y。private Rect mTextBound = new Rect();mPaint.setTextAlign(Paint.Align.CENTER);mPaint.getTextBounds(mText, 0, mText.length(), mTextBound);canvas.drawText(mText, centerX, center

用TinyImageNet数据集进行图像分类实验,test精度极低_乱搭巴士的博客-程序员ITS301_imagenet tiny

错误原因:TinyImageNet数据集的val验证集不能直接用datasets.ImageFolder导入,直接使用的话精度只有零点几。而且test数据集是没有标注的。错误示例:trainset = datasets.ImageFolder(root=os.path.join(data_dir, data, 'tiny-imagenet-200/train'), transform=transform_train)testset = datasets.ImageFolder(root=os.pat

C语言结构体typedef struct详解_还是羊脂球的博客-程序员ITS301_typedef

1、typedef关键字C 语言提供了 typedef 关键字,您可以使用它来为类型取一个新的名字。下面的实例为单字节数字定义了一个术语 BYTE:typedef unsigned char BYTE;在这个类型定义之后,标识符 BYTE 可作为类型 unsigned char 的缩写,例如:BYTE b1, b2;typedef相当于给unsigned char起...

推荐文章

热门文章

相关标签