es6 装饰器decorator的使用 +webpack4.0配置

decorator 装饰器

许多面向对象都有decorator(装饰器)函数,好比python中也能够用decorator函数来强化代码,decorator至关于一个高阶函数,接收一个函数,返回一个被装饰后的函数。javascript

注: javascript中也有decorator相关的提案,只是目前node以及各浏览器中均不支持。只能经过安装babel插件来转换代码,插件名叫这个:transform-decorators-legacy。也有在线试用](babeljs.io/repl/),安装好transform-decorators-legacy以后,就能看到转义后的代码了前端

2.1 使用decorator的前期配置

1.vscode里面去除装饰器报错的方法

在vscode里打开设置=>用户设置里面加入(tips:打开设置后也能够直接点击右上角的'{}'进行用户设置)java

"javascript.implicitProjectConfig.experimentalDecorators": true 复制代码

就能够了;node

2.搭建一个简单的webpack 来使用装饰器

因为目前浏览器和node暂时不支持装饰器,因此咱们能够配置一个webpack来使用装饰器python

全局安装webpack

cnpm install webpack webpack-cli webpack-dev-server -g
复制代码

启动配置 建立一个webpack.dev.jsgit

var path = require('path') module.exports = { mode: "development", entry: { main: "./test.js" }, output: { path: path.resolve(__dirname, "./dist"), filename: "test.js" }, module: { rules: [ //webpack 4.X写法 { test: /.js$/, use: ['babel-loader'] } ] } } 复制代码

下载依赖(webpack4.x 方法 )github

npm install -D babel-loader @babel/core @babel/preset-env
复制代码

配置.babelrcweb

{
    "presets": [ "@babel/preset-env" ], "plugins": [ [ "@babel/plugin-proposal-decorators", { "legacy": true } ], ] } 复制代码

建立好webpack的目录结构是这样的

 

 

package.json的配置

{
  "name": "decorator", "version": "1.0.0", "description": "", "main": "test.js", "scripts": { "build": "webpack --config=webpack.dev.js", "start": "node ./dist/bound.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@babel/core": "^7.4.4", "@babel/plugin-proposal-class-properties": "^7.4.4", "@babel/plugin-proposal-decorators": "^7.4.4", "@babel/preset-env": "^7.4.4", "babel-loader": "^8.0.5", "babel-plugin-transform-decorators-legacy": "^1.3.5", "babel-preset-env": "^1.7.0", "core-decorators": "^0.20.0" } } 复制代码

2.2 开始使用decorator

1.类的修饰

许多面向对象的语言都有修饰器(Decorator)函数,用来修改类的行为。目前,有一个提案将这项功能,引入了 ECMAScript。 下面咱们采用一个钢铁侠的例子来展开npm

@transform
class IronMan { // ... } function transform(target) { target.weapon = laser } console.log(IronMan.weapon) // laser 复制代码

上面代码中,@transform就是一个修饰器。它修改了IronMan这个类的行为,为它加上了武器属性weapontransform函数的参数targetIronMan类自己。

2.结合redux库使用

实际开发中,React 与 Redux 库结合使用时,经常须要写成下面这样。

class MyReactComponent extends React.Component {} export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent); 复制代码

有了装饰器,就能够改写上面的代码。

@connect(mapStateToProps, mapDispatchToProps)
export default class MyReactComponent extends React.Component {} 复制代码

3.方法的修饰

修饰器不只能够修饰类,还能够修饰类的属性。

class Person { @readonly name() { return `${this.first} ${this.last}` } } 复制代码

上面代码中,修饰器readonly用来修饰“类”的name方法。

修饰器函数readonly一共能够接受三个参数。

function readonly(target, name, descriptor){ // descriptor对象原来的值以下 // { // value: specifiedFunction, // enumerable: false, // configurable: true, // writable: true // }; descriptor.writable = false; return descriptor; } readonly(Person.prototype, 'name', descriptor); // 相似于 Object.defineProperty(Person.prototype, 'name', descriptor); 复制代码

修饰器第一个参数是类的原型对象,上例是Person.prototype,修饰器的本意是要“修饰”类的实例,可是这个时候实例还没生成,因此只能去修饰原型(这不一样于类的修饰,那种状况时target参数指的是类自己);第二个参数是所要修饰的属性名,第三个参数是该属性的描述对象。

4.装饰器不能用于修饰函数

本来做者设计的时候 是可使用这种方式的 好比修饰一个函数这么写

@RunOnce
function expensiveOperation() { return 1 + 1; } //语法糖以后的效果是这样的 var expensiveOperation = RunOnce(function expensiveOperation() { return 1 + 1; }); 复制代码

这意味着装饰器能够用于任何任务,可是做者认为这样可能有点复杂 而且还有一个潜在的问题 装饰器和跟随变量一块提高 使得装饰器语法函数过早执行而致使由于位置的缘由产生的一些错误 好比:

var readOnly = require("some-decorator"); // 是否提高求值? // 若是是这样的话 `readOnly` 那么就是未定义 @readOnly function foo() { } 复制代码

总而言之,做者不但愿产生这样的复杂性,因此去除了修饰函数,详情能够参考这篇做者参与讨论的帖子

5.应用

至于decorator的应用场景在哪里?应该大部分AOP的场景均可以用,例如日志系统。 这里就手动来实现一个简单的日志系统。

const log = (type) => { return (target, name, descriptor) => { let method = descriptor.value ; // 具体三个方法 descriptor.value = (...args) => { console.log(`${type}`); let result ; try { result = method.apply(target,args); console.log(`${type} ${result} 成功`) } catch (error) { console.log(`${type} ${result} 失败`) } return result ; } } } class Man { @log('正在洗漱') wash() { return "洗漱"; } @log('正在吃饭') eat() { return "吃饭"; } @log('正在跑步') run() { return "跑步"; } } let m = new Man() ; m.wash(); m.eat(); m.run(); 复制代码

6.core-decorators.js

core-decorators.js是一个第三方模块,提供了几个常见的修饰器,经过它能够更好地理解修饰器。

(1)@readonly

readonly修饰器使得属性或方法不可写。

import { readonly } from 'core-decorators'; class Meal { @readonly entree (){ console.log(111) }; } var dinner = new Meal(); dinner.entree = 'salmon'; // Cannot assign to read only property 'entree' of [object Object] 复制代码

(2)@override

override修饰器检查子类的方法,是否正确覆盖了父类的同名方法,若是不正确会报错。

import { override } from 'core-decorators'; class Parent { speak(first, second) {} } class Child extends Parent { @override speak() {} // SyntaxError: Child#speak() does not properly override Parent#speak(first, second) } // or class Child extends Parent { @override speaks() {} // SyntaxError: No descriptor matching Child#speaks() was found on the prototype chain. // // Did you mean "speak"? } 复制代码

(3)@deprecate (别名@deprecated)

deprecatedeprecated修饰器在控制台显示一条警告,表示该方法将废除。

import { deprecate } from 'core-decorators'; class Person { @deprecate facepalm() {} @deprecate('We stopped facepalming') facepalmHard() {} @deprecate('We stopped facepalming', { url: 'http://knowyourmeme.com/memes/facepalm' }) facepalmHarder() {} } let person = new Person(); person.facepalm(); // DEPRECATION Person#facepalm: This function will be removed in future versions. person.facepalmHard(); // DEPRECATION Person#facepalmHard: We stopped facepalming person.facepalmHarder(); // DEPRECATION Person#facepalmHarder: We stopped facepalming // // See http://knowyourmeme.com/memes/facepalm for more details. // 复制代码

(4)@suppressWarnings

suppressWarnings修饰器抑制deprecated修饰器致使的console.warn()调用。可是,异步代码发出的调用除外。

import { suppressWarnings } from 'core-decorators'; class Person { @deprecated facepalm() {} @suppressWarnings facepalmWithoutWarning() { this.facepalm(); } } let person = new Person(); person.facepalmWithoutWarning(); // no warning is logged 复制代码

7.Mixin

在修饰器的基础上,能够实现Mixin模式。所谓Mixin模式,就是对象继承的一种替代方案,中文译为“混入”(mix in),意为在一个对象之中混入另一个对象的方法。

const Foo = { foo() { console.log('foo') } }; class MyClass {} Object.assign(MyClass.prototype, Foo); let obj = new MyClass(); obj.foo() // 'foo' 复制代码

可使用装饰器改写为

function mixins(...list) { return function (target) { Object.assign(target.prototype, ...list); }; } const Foo = { foo() { console.log('foo') } }; @mixins(Foo) class MyClass {} let obj = new MyClass(); obj.foo() // "foo" 复制代码

这中方法会改写Myclass的 prototype,因此也可使用继承的方法混入

let MyMixin = (superclass) => class extends superclass { foo() { console.log('foo from MyMixin'); } }; class MyClass extends MyMixin(MyBaseClass) { /* ... */ } let c = new MyClass(); c.foo(); // "foo from MyMixin" 
做者:前端小菜鸟qwq 连接:https://juejin.im/post/5cdc1bbce51d45379a164342 来源:掘金 著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。
相关文章
相关标签/搜索