decorator 装饰器

许多面向对象都有decorator(装饰器)函数,比如python中也可以用decorator函数来强化代码,decorator相当于一个高阶函数,接收一个函数,返回一个被装饰后的函数。

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

2.1 使用decorator的前期配置

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

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

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

就可以了;

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

由于目前浏览器和node暂时不支持装饰器,所以我们可以配置一个webpack来使用装饰器

全局安装

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

启动配置 创建一个webpack.dev.js

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 方法 )

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

配置.babelrc

{
"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。 下面我们采用一个钢铁侠的例子来展开

@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
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

es6 装饰器decorator的使用 +webpack4.0配置的更多相关文章

  1. ES6装饰器Decorator基本用法

    1. 基本形式 @decorator class A {} // 等同于 class A {} A = decorator(A); 装饰器在javascript中仅仅可以修饰类和属性,不能修饰函数.装 ...

  2. python 装饰器(decorator)

    装饰器(decorator) 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 装饰器(decorator)是一种高级Python语 ...

  3. Python的程序结构[8] -> 装饰器/Decorator -> 装饰器浅析

    装饰器 / Decorator 目录 关于闭包 装饰器的本质 语法糖 装饰器传入参数 1 关于闭包 / About Closure 装饰器其本质是一个闭包函数,为此首先理解闭包的含义. 闭包(Clos ...

  4. python函数编程-装饰器decorator

    函数是个对象,并且可以赋值给一个变量,通过变量也能调用该函数: >>> def now(): ... print('2017-12-28') ... >>> l = ...

  5. python语法32[装饰器decorator](转)

    一 装饰器decorator decorator设计模式允许动态地对现有的对象或函数包装以至于修改现有的职责和行为,简单地讲用来动态地扩展现有的功能.其实也就是其他语言中的AOP的概念,将对象或函数的 ...

  6. Python_高阶函数、装饰器(decorator)

    一.变量: Python支持多种数据类型,在计算机内部,可以把任何数据都看成一个“对象”,而变量就是在程序中用来指向这些数据对象的,对变量赋值就是把数据和变量给关联起来. 对变量赋值x = y是把变量 ...

  7. python 语法之 装饰器decorator

    装饰器 decorator 或者称为包装器,是对函数的一种包装. 它能使函数的功能得到扩充,而同时不用修改函数本身的代码. 它能够增加函数执行前.执行后的行为,而不需对调用函数的代码做任何改变. 下面 ...

  8. 【Angular专题】 (3)装饰器decorator,一块语法糖

    目录 一. Decorator装饰器 二. Typescript中的装饰器 2.1 类装饰器 2.2 方法装饰器 2.3 访问器装饰器 2.4 属性装饰器 2.5 参数装饰器 三. 用ES5代码模拟装 ...

  9. koa2使用es7 的装饰器decorator

    本文主要讲述我在做项目中使用装饰器(decorator)来动态加载koa-router的路由的一个基础架构. 目前JavaScript 对decorator 是不支持,但是可以用babel 来编译 既 ...

随机推荐

  1. 学到了林海峰,武沛齐讲的Day23-完

    10月11号生了儿子,很高心..不好的是孩子住院了,14号出院,晚上外公去世了,15号赶回老家.....20号回贵阳,21号回公司办事....我要坚定的学习下去...以前几乎是卡在这里就学不下去了.加 ...

  2. Spring动态代理及Spring Bean的生命周期

    数组添加值 public class DiTest { /** * 数组 */ private String [] arrays; /** * List:集合 */ private List<I ...

  3. Atcoder Rating System

    来翻译一下官方文档,但是建议看英文原文,本文可能会出现一些错误,只是为了方便自己查阅用的. 对于你的每一场rated比赛,会有一个Performance值\(X_i\),你的rating是\(X_i- ...

  4. wepy代码知识点

    index-page <style lang="less"> .index-nood-modal { width: 100vw; height: 100vh; posi ...

  5. laravel-china 镜像停止服务

    php 的很多开发都会用到composer.然后国内的镜像又慢,很多人会选择用laravel-china的镜像. 之前一直用的很好.今天突然发现不能composer update.出现报错.WTF!! ...

  6. ELK系列(7) - 测试环境下Logstash异常退出:block in multi_receive_encoded

    问题与分析 在本地测试无误后将ELK部署到了测试环境,结果第二天发现Logstash挂掉了,一开始以为是自动部署之类导致的问题.之后几天时间里Logstash总是会down掉,查看了下日志文件,发现报 ...

  7. xiugai grub

    https://wiki.gentoo.org/wiki/Flicker_Free_Boot#Getting_the_custom_version_of_grub

  8. elasticsearch配置jdk

    编辑bin/elasticsearch 可以看到elasticsearch使用环境变量JAVA_HOME中配置的jdk:if [ -x "$JAVA_HOME/bin/java" ...

  9. rpm包和deb分别是什么?

    一.RMP 是 LINUX 下的一种软件的可执行程序,你只要安装它就可以了.这种软件安装包通常是一个RPM包(Redhat Linux Packet Manager,就是Redhat的包管理器),后缀 ...

  10. socket_timeout

    https://github.com/pika/pika/blob/03542ef616a2a849e8bfb0845427f50e741ea0c6/docs/examples/using_urlpa ...