设为首页 加入收藏

TOP

Javascript装饰器的妙用(四)
2018-07-13 06:06:56 】 浏览:320
Tags:Javascript 装饰 妙用
tor === A, target === A.prototype) // true, true
}


函数tag与method会在class A定义的时候执行。


@Decorator 在 Class 中的使用


该装饰器会在class定义前调用,如果函数有返回值,则会认为是一个新的构造函数来替代之前的构造函数。


函数接收一个参数:
1.constructor 之前的构造函数


我们可以针对原有的构造函数进行一些改造:


新增一些属性


如果想要新增一些属性之类的,有两种方案可以选择:
1.创建一个新的class继承自原有class,并添加属性
2.针对当前class进行修改


后者的适用范围更窄一些,更接近mixin的处理方式。


@name
class Person {
  sayHi() {
    console.log(`My name is: ${this.name}`)
  }
}


// 创建一个继承自Person的匿名类
// 直接返回并替换原有的构造函数
function name(constructor) {
  return class extends constructor {
    name = 'Niko'
  }
}


new Person().sayHi()


修改原有属性的描述符


@seal
class Person {
  sayHi() {}
}


function seal(constructor) {
  let descriptor = Object.getOwnPropertyDescriptor(constructor.prototype, 'sayHi')
  Object.defineProperty(constructor.prototype, 'sayHi', {
    ...descriptor,
    writable: false
  })
}


Person.prototype.sayHi = 1 // 无效


使用闭包来增强装饰器的功能



在TS文档中被称为装饰器工厂


因为@符号后边跟的是一个函数的引用,所以对于mixin的实现,我们可以很轻易的使用闭包来实现:


class A { say() { return 1 } }
class B { hi() { return 2 } }


@mixin(A, B)
class C { }


function mixin(...args) {
  // 调用函数返回装饰器实际应用的函数
  return function(constructor) {
    for (let arg of args) {
      for (let key of Object.getOwnPropertyNames(arg.prototype)) {
        if (key === 'constructor') continue // 跳过构造函数
        Object.defineProperty(constructor.prototype, key, Object.getOwnPropertyDescriptor(arg.prototype, key))
      }
    }
  }
}


let c = new C()
console.log(c.say(), c.hi()) // 1, 2


多个装饰器的应用


装饰器是可以同时应用多个的(不然也就失去了最初的意义)。
用法如下:


@decorator1
@decorator2
class { }


执行的顺序为decorator2 -> decorator1,离class定义最近的先执行。
可以想像成函数嵌套的形式:


decorator1(decorator2(class {}))


@Decorator 在 Class 成员中的使用


类成员上的 @Decorator 应该是应用最为广泛的一处了,函数,属性,get、set访问器,这几处都可以认为是类成员。
在TS文档中被分为了Method Decorator、Accessor Decorator和Property Decorator,实际上如出一辙。


关于这类装饰器,会接收如下三个参数:
1.如果装饰器挂载于静态成员上,则会返回构造函数,如果挂载于实例成员上则会返回类的原型
2.装饰器挂载的成员名称
3.成员的描述符,也就是Object.getOwnPropertyDescriptor的返回值



Property Decorator不会返回第三个参数,但是可以自己手动获取
前提是静态成员,而非实例成员,因为装饰器都是运行在类创建时,而实例成员是在实例化一个类的时候才会执行的,所以没有办法获取对应的descriptor


静态成员与实例成员在返回值上的区别


可以稍微明确一下,静态成员与实例成员的区别:


class Model {
  // 实例成员
  method1 () {}
  method2 = () => {}


  // 静态成员
  static method3 () {}
  static method4 = () => {}
}


method1和method2是实例成员,method1存在于prototype之上,而method2只在实例化对象以后才有。
作为静态成员的method3和method4,两者的区别在于是否可枚举描述符的设置,所以可以简单地认为,上述代码转换为ES5版本后是这样子的:


function Model () {
  // 成员仅在实例化时赋值
  this.method2 = function () {}
}


// 成员被定义在原型链上
Object.defineProperty(Model.prototype, 'method1', {
  value: function () {},
  writable: true,
  enumerable: false,  // 设置不可被枚举
  configurable: true
})


// 成员被定义在构造函数上,且是默认的可被枚举
Model.method4 = function () {}


// 成员被定义在构造函数上
Object.defineProperty(Model, 'method3', {
  value: function () {},
  writable: true,
  enumerable: false,  // 设置不可被枚举
  configurable: true
})


可以看出,只有method2是在实例化时才赋值的,一个不存在的属性是不会有descriptor的,所以这就是为什么TS在针对Property Decorator不传递第三个参数的原因,至于为什么静态成员也没有传递descriptor,目前没有找到合理的解释,但是如果明确的要使用,是可以手动获取的。


就像上述的示例,我们针对四个成员都添加了装饰器以后,method1和method2第一个参数就是Model.prototype,而method3和method4的第一个参数就是Model。


class Model {
  // 实例成员
  @instance
  method1 () {}
 

首页 上一页 1 2 3 4 5 6 7 下一页 尾页 4/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇u-boot-1.1.6第1阶段分析之make s.. 下一篇Python 字节码介绍

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目