使用babel深入理解es7的decorator
阿新 • • 發佈:2019-02-14
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']
}
}
]