TS简介
Ts微软开发,包含ES6、包含ES5

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

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

创建tsconfig.json文件 tsc --init 生成配置文件
tsconfig.json配置文件中,修改outDir配置项,取消注释然后修改为.js
vscode中,点击上方栏位run task,选择ts监听
完成
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(value:T):T{
return value;//传入什么返回什么
}
// 这样调用
getData(123123);
getData("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{
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(){

    }
}


类装饰器:装饰器工厂
作用:

修改构造函数
扩展类属性和方法
定义:
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();


属性装饰器
作用:

可以给属性赋值
// 类装饰器
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


装饰器执行顺序
当存在多个装饰器时候:

执行优先级:属性装饰器>方法装饰器>方法参数装饰器>类装饰器
如果有多个同样的装饰器,它会先从后边执行
其他参考资料
call()、apply()


欢迎欢迎~热烈欢迎~