JS 装饰器解析

简介: 随着 ES6 和 TypeScript 中类的引入,在某些场景需要在不改变原有类和类属性的基础上扩展些功能,这也是装饰器出现的原因。装饰器简介作为一种可以动态增删功能模块的模式(比如 redux 的中间件机制),装饰器同样具有很强的动态灵活性,只需在类或类属性之前加上 @方法名 就完成了相应的类或类方法功能的变化。

img_b34359ee107ac92c5e9ebd492a07b64c.jpe

随着 ES6 和 TypeScript 中类的引入,在某些场景需要在不改变原有类和类属性的基础上扩展些功能,这也是装饰器出现的原因。

装饰器简介

作为一种可以动态增删功能模块的模式(比如 redux 的中间件机制),装饰器同样具有很强的动态灵活性,只需在类或类属性之前加上 @方法名 就完成了相应的类或类方法功能的变化。

不过装饰器模式仍处于第 2 阶段提案中,使用它之前需要使用 babel 模块 transform-decorators-legacy 编译成 ES5 或 ES6。

在 TypeScript 的 lib.es5.d.ts 中,定义了 4 种不同装饰器的接口,其中装饰类以及装饰类方法的接口定义如下所示:

declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;

下面对这两种情况进行解析。

作用于类的装饰器

当装饰的对象是类时,我们操作的就是这个类本身

@log
class MyClass { }

function log(target) { // 这个 target 在这里就是 MyClass 这个类
   target.prototype.logger = () => `${target.name} 被调用`
}

const test = new MyClass()
test.logger() // MyClass 被调用

由于装饰器是表达式,我们也可以在装饰器后面再添加提个参数:

@log('hi')
class MyClass { }

function log(text) {
  return function(target) {
    target.prototype.logger = () => `${text}${target.name} 被调用`
  }
}

const test = new MyClass()
test.logger() // hello,MyClass 被调用

在使用 redux 中,我们最常使用 react-redux 的写法如下:

@connect(mapStateToProps, mapDispatchToProps)
export default class MyComponent extends React.Component {}

经过上述分析,我们知道了上述写法等价于下面这种写法:

class MyComponent extends React.Component {}
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)

作用于类方法的装饰器

与装饰类不同,对类方法的装饰本质是操作其描述符。可以把此时的装饰器理解成是 Object.defineProperty(obj, prop, descriptor) 的语法糖,看如下代码:

class C {
  @readonly(false)
  method() { console.log('cat') }
}

function readonly(value) {
  return function (target, key, descriptor) { // 此处 target 为 C.prototype; key 为 method;
    // 原 descriptor 为:{ value: f, enumarable: false, writable: true, configurable: true }
    descriptor.writable = value
    return descriptor
  }
}

const c = new C()
c.method = () => console.log('dog')

c.method() // cat

可以看到装饰器函数接收的三个参数与 Object.defineProperty 是完全一样的,具体实现可以看 babel 转化后的代码,主要实现如下所示:

var C = (function() {
  class C {
    method() { console.log('cat') }
  }

  var temp
  temp = readonly(false)(C.prototype, 'method',
    temp = Object.getOwnPropertyDescriptor(C.prototype, 'method')) || temp // 通过 Object.getOwnPropertyDescriptor 获取到描述符传入到装饰器函数中

  if (temp) Object.defineProperty(C.prototype, 'method', temp)
  return C
})()

再将再来看看如果有多个装饰器作用于同一个方法上呢?

class C {
  @readonly(false)
  @log
  method() { }
}

经 babel 转化后的代码如下:

desc = [readonly(false), log]
    .slice()
    .reverse()
    .reduce(function(desc, decorator) {
      return decorator(target, property, desc) || desc;
    }, desc);

可以清晰地看出,经过 reverse 倒序后,装饰器方法会至里向外执行。

相关链接

javascript-decorators

Javascript 中的装饰器

JS 装饰器(Decorator)场景实战

修饰器

Babel

作者:牧云云
出处:http://www.cnblogs.com/MuYunyun/"
本文版权归作者和博客园所有,欢迎转载,转载请标明出处。
如果您觉得本篇博文对您有所收获,请点击右下角的 [推荐],谢谢!

目录
相关文章
|
2天前
|
JavaScript 前端开发 UED
深入解析JavaScript原生操作DOM技术
【4月更文挑战第22天】本文深入探讨JavaScript原生DOM操作技术,包括使用`getElement*`方法和CSS选择器获取元素,借助`createElement`与`appendChild`动态创建及插入元素,修改元素内容、属性和样式,以及删除元素。通过掌握这些技术,开发者能实现页面动态交互,但应注意避免过度操作DOM以优化性能和用户体验。
|
2天前
|
前端开发 JavaScript 编译器
深入解析JavaScript中的异步编程:Promises与async/await的使用与原理
【4月更文挑战第22天】本文深入解析JavaScript异步编程,重点讨论Promises和async/await。Promises用于管理异步操作,有pending、fulfilled和rejected三种状态。通过.then()和.catch()处理结果,但可能导致回调地狱。async/await是ES2017的语法糖,使异步编程更直观,类似同步代码,通过事件循环和微任务队列实现。两者各有优势,适用于不同场景,能有效提升代码可读性和维护性。
|
1月前
|
缓存 数据库 Python
Python装饰器:深入解析与应用
Python装饰器是函数,接收函数作为参数并返回新函数,用于在不修改原函数的情况下添加额外功能。它们基于Python的函数一等公民特性,允许在执行前后插入逻辑。应用场景包括日志记录、性能分析、权限校验、缓存和事务管理。学习装饰器能提升代码质量和维护性。
|
1月前
|
测试技术 Python
Python中的装饰器应用及原理解析
Python中的装饰器是一种强大的功能,它可以用于在不修改原始函数代码的情况下,动态地增加功能。本文将深入探讨Python装饰器的应用场景和原理,帮助读者更好地理解和运用这一重要特性。
|
1月前
|
自然语言处理 JavaScript 前端开发
Vue.js 深度解析:模板编译原理与过程
Vue.js 深度解析:模板编译原理与过程
|
1月前
|
缓存 JavaScript 网络架构
Vue.js 进阶技巧:keep-alive 缓存组件解析
Vue.js 进阶技巧:keep-alive 缓存组件解析
|
1月前
|
JavaScript 前端开发 API
Vue.js 深度解析:nextTick 原理与应用
Vue.js 深度解析:nextTick 原理与应用
|
1月前
|
JavaScript 前端开发 IDE
JavaScript 中的 structuredClone():详尽解析
您是否知道,现在 JavaScript 中有一种原生的方式可以深拷贝对象?
|
1月前
|
缓存 测试技术 Python
Python中的装饰器应用及原理解析
【2月更文挑战第10天】在Python编程中,装饰器是一种强大的工具,能够在不改变原有函数代码的情况下,为函数添加额外的功能。本文将深入探讨Python中装饰器的应用场景以及其背后的原理,帮助读者更好地理解和运用这一特性。
|
2月前
|
Python
Python中的装饰器应用及原理解析
在Python编程中,装饰器是一种强大的工具,它可以对函数进行扩展和修改而不需改变函数本身的代码。本文将深入探讨Python中装饰器的原理和应用,以及如何利用装饰器实现日志记录、性能分析等功能。

推荐镜像

更多