ECMAScript Decorators---装饰器
Decorators是什么
- Decorators可以改变类方法和类实例字段的属性和行为,使我们可以灵活地使用更简单的语法动态实现这些内容,是非侵入式的。---举例,你给手机添加一个外壳罢了,并不影响手机原有的通话、充电等功能
应用场景
- Decorators的经典的应用是AOP编程,比如“日志系统”,日志系统的作用是记录系统的行为操作,它在不影响原有系统的功能的基础上增加记录环节
- 更加抽象的理解,可以理解为给数据流做一层filter,因此 AOP 的典型应用包括安全检查、缓存、调试、持久化等等。
原理
- Decorators的本质是利用了ES5的Object.defineProperty属性,这三个参数其实是和Object.defineProperty参数一致的,因此不能更改
- object 必需。要在其上添加或修改属性的对象 这可能是一个本机JavaScript对象(即用户定义的对象或内置对象)或 DOM 对象。
- propertyname必需。一个包含属性名称的字符串
- descriptor 必需。 属性描述符。它可以针对数据属性或访问器属性。
- 举例说明
var myObj = { myPropOne: 1, myPropTwo: 2};// modify property descriptorObject.defineProperty( myObj, 'myPropOne', { writable: false, // 是否允许该属性值更改 enumerable: false, // 是否允许key被枚举,话句话说for in 或者Object.keys() 不会输出key configurable: false// 目标属性是否可以被删除或是否可以再次修改特性} );复制代码
应用举例
- 类方法 @readonly
class User { constructor( firstname, lastName ) { this.firstname = firstname; this.lastName = lastName; } @readonly getFullName() { return this.firstname + ' ' + this.lastName; }}// create instancelet user = new User( 'John', 'Doe' );console.log( user.getFullName() );// 某天我不小心重写了这个方法User.prototype.getFullName = function() { return 'HACKED!';}// 输出 HACKED! 与预期不符,怎么避免此类情况发生// 方法1 这是最好的解决方案么?修饰器登场Object.defineProperty( User.prototype, 'getFullName', { writable: false});// 将此方法添加到修饰方法getFullName上function readonly( target, property, descriptor ) { descriptor.writable = false; return descriptor;}复制代码
- 类方法 @log日志打印
function log( logMessage ) { // return decorator function return function ( target, property, descriptor ) { // save original value, which is method (function) let originalMethod = descriptor.value; // replace method implementation descriptor.value = function( ...args ) { console.log( '[LOG]', logMessage ); // here, call original method // `this` points to the instance return originalMethod.call( this, ...args ); }; return descriptor; }}class User { constructor( firstname, lastName ) { this.firstname = firstname; this.lastName = lastName; } @log('calling getFullName method on User class') getFullName() { return this.firstname + ' ' + this.lastName; }}var user = new User( 'John', 'Doe' );console.log( user.getFullName() );复制代码
- 类的属性 大小写转换
// 解释:descriptor.initializer函数由Babel内部使用来创建对象属性的属性描述符的值function toCase( CASE = 'lower' ) { return function ( target, name, descriptor ) { let initValue = descriptor.initializer(); descriptor.initializer = function(){ return ( CASE == 'lower' ) ? initValue.toLowerCase() : initValue.toUpperCase(); } return descriptor; }}class User { @toCase( 'upper' ) firstName = 'default_first_name'; lastName = 'default_last_name'; constructor( firstName, lastName ) { if( firstName ) this.firstName = firstName; if( lastName ) this.lastName = lastName; } getFullName() { return this.firstName + ' ' + this.lastName; }}console.log( new User() );复制代码
- 类装饰器
function withLoginStatus( UserRef ) { return class extends UserRef { constructor( ...args ) { super( ...args ); this.isLoggedIn = false; } setLoggedIn() { this.isLoggedIn = true; } }}@withLoginStatusclass User { constructor( firstName, lastName ) { this.firstName = firstName; this.lastName = lastName; }}let user = new User( 'John', 'Doe' );console.log( 'Before ===> ', user );// set logged inuser.setLoggedIn();console.log( 'After ===> ', user );复制代码
babel 装换
1、创建项目文件2、命令行进入该项目目录 npm init 3、npm install babel-core babel-plugin-transform-decorators4、安装 npm install babel-plugin-transform-decorators-legacy --save-dev5、.babelrc添加{ "plugins": [ "transform-decorators-legacy" ]}复制代码
参考链接