es7提出了decorator的语法,让我们能写出更优雅更好维护的代码,装饰器的本质是对现有的功能进行包装,可以用来加钩子或者增强功能等,js从语法层面支持这种写法,让我们可以保持代码解耦。
比如我们有一个函数
funciton update() {
console.log('update db')
}
我们想在执行这个函数时打日志,我们可能会这样改写
funciton update() {
log('log')
console.log('update db')
}
或者用高阶函数装饰update
decoratorUpdate = funciton( ) {
log('log')
update();
}
这样会让我们的代码被业务无关的代码侵入,增加了耦合度。
在es7里我们可以这样写
funciton log(className, propName, descriptor) {
var value = descriptor.value;
descriptor.value = funciton() {
console.log('update db');
value();
}
}
class Curd{
@log
update() {
}
}
这样我们的代码里,业务相关的代码和无关的代码分离。更加清晰。
下面我们使用babel看一下es7的decorator本质是什么。
1.首先我们安装babel。
npm install babel-loader babal-core babel-preset-es2015 babel-plugin-transform-decorators-legacy
写某个文件夹下新建.babelrc文件。
{
"presets": [
// 把es6转成es5
'babel-preset-es2015'
],
// 把装饰器语法转成es5
"plugins": ['transform-decorators-legacy']
}
然后,新建index.js
/*
target 类的prototype
name 类prototype上的属性
descriptor 描述符
*/
function readonly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
// descriptor为数据描述符
function log(target, name, descriptor) {
// 保存原来的值
var value = descriptor.value;
// 加钩子
descriptor.value = function() {
console.log('log');
// 调用原来的值
value();
}
return descriptor;
}
// descriptor为存取描述符
function get(target, name, descriptor) {
var value = descriptor.get;
descriptor.get = function() {
console.log('log');
console.log(value())
}
return descriptor
}
function decoratorFactory(type) {
switch(type) {
case 1: return function(target, name, descriptor) {
var value = descriptor.value;
descriptor.value = function() {
console.log('you will say hello =>');
console.log(value())
}
return descriptor;
}
case 2: return function(target, name, descriptor) {
var value = descriptor.value;
descriptor.value = function() {
console.log('you will say world =>');
console.log(value())
}
return descriptor;
}
}
}
function classDecorator(className) {
className.flag = true;
return className;
}
@classDecorator
class a {
@readonly
name() {
return 1;
}
@log
update() {
console.log('update db');
};
@get
get say() {
return 1
}
// @后面跟的值是一个函数,所以我们可以计算一个表达式,返回一个函数
@(false ? decoratorFactory(1) : decoratorFactory(2))
hello() {
console.log('hello')
}
@decoratorFactory(2)
world() {
console.log('world')
}
@decoratorFactory(1)
@decoratorFactory(2)
double() {
console.log('double')
}
}
//a.prototype.name = 1 // throw error
console.log(new a().update())
console.log(new a().hello())
console.log(new a().world())
console.log(new a().double())
console.log(new a().say)
在当我文件夹下执行babel index.js -o index-babel.js得到
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _dec, _dec2, _dec3, _dec4, _class, _desc, _value, _class2;
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object['ke' + 'ys'](descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object['define' + 'Property'](target, property, desc);
desc = null;
}
return desc;
}
/*
target 类的prototype
name 类prototype上的属性
descriptor 描述符
*/
function readonly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
// descriptor为数据描述符
function log(target, name, descriptor) {
// 保存原来的值
var value = descriptor.value;
// 加钩子
descriptor.value = function () {
console.log('log');
// 调用原来的值
value();
};
return descriptor;
}
// descriptor为存取描述符
function get(target, name, descriptor) {
var value = descriptor.get;
descriptor.get = function () {
console.log('log');
console.log(value());
};
return descriptor;
}
function decoratorFactory(type) {
switch (type) {
case 1:
return function (target, name, descriptor) {
var value = descriptor.value;
descriptor.value = function () {
console.log('you will say hello =>');
console.log(value());
};
return descriptor;
};
case 2:
return function (target, name, descriptor) {
var value = descriptor.value;
descriptor.value = function () {
console.log('you will say world =>');
console.log(value());
};
return descriptor;
};
}
}
function classDecorator(className) {
className.flag = true;
return className;
}
var a = (_dec = false ? decoratorFactory(1) : decoratorFactory(2), _dec2 = decoratorFactory(2), _dec3 = decoratorFactory(1), _dec4 = decoratorFactory(2), classDecorator(_class = (_class2 = function () {
function a() {
_classCallCheck(this, a);
}
_createClass(a, [{
key: 'name',
value: function name() {
return 1;
}
}, {
key: 'update',
value: function update() {
console.log('update db');
}
}, {
key: 'hello',
value: function hello() {
console.log('hello');
}
}, {
key: 'world',
value: function world() {
console.log('world');
}
}, {
key: 'double',
value: function double() {
console.log('double');
}
}, {
key: 'say',
get: function get() {
return 1;
}
// @后面跟的值是一个函数,所以我们可以计算一个表达式,返回一个函数
}]);
return a;
}(), (_applyDecoratedDescriptor(_class2.prototype, 'name', [readonly], Object.getOwnPropertyDescriptor(_class2.prototype, 'name'), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, 'update', [log], Object.getOwnPropertyDescriptor(_class2.prototype, 'update'), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, 'say', [get], Object.getOwnPropertyDescriptor(_class2.prototype, 'say'), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, 'hello', [_dec], Object.getOwnPropertyDescriptor(_class2.prototype, 'hello'), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, 'world', [_dec2], Object.getOwnPropertyDescriptor(_class2.prototype, 'world'), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, 'double', [_dec3, _dec4], Object.getOwnPropertyDescriptor(_class2.prototype, 'double'), _class2.prototype)), _class2)) || _class);
//a.prototype.name = 1 // throw error
console.log(new a().update());
console.log(new a().hello());
console.log(new a().world());
console.log(new a().double());
console.log(new a().say);
done。
2 在webpack中使用decorator
加配置
loaders: [
{
test: /\.js|jsx$/, //是一个正则,代表js或者jsx后缀的文件要使用下面的loader
loader: "babel-loader",
query: {
// 把es6转成es5
presets: ['es2015'],
// 把decorator转成es5
plugins: ['transform-decorators-legacy']
}
}
]