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 descriptor
Object.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 instance
let 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;
}
}
}
@withLoginStatus
class User {
constructor( firstName, lastName ) {
this.firstName = firstName;
this.lastName = lastName;
}
}
let user = new User( 'John', 'Doe' );
console.log( 'Before ===> ', user );
// set logged in
user.setLoggedIn();
console.log( 'After ===> ', user );
复制代码
babel 装换
1、创建项目文件
2、命令行进入该项目目录 npm init
3、npm install babel-core babel-plugin-transform-decorators
4、安装 npm install babel-plugin-transform-decorators-legacy --save-dev
5、.babelrc添加
{
"plugins": [
"transform-decorators-legacy"
]
}
复制代码
参考链接