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. Java中static用来计数

    搓搓的代码 import java.util.ArrayList; import java.util.Iterator; class Student{ private static int num; ...

  2. MongoDB 如何保证 oplog 顺序?

    MongoDB 复制集里,主备节点间通过 oplog 来同步数据,Priamry 上写入数据时,会记录一条oplog,Secondary 从 Primary 节点拉取 oplog并重放,以保证最终存储 ...

  3. Linux swap的创建与配置

    在Linux下,swap的作用类似Windows系统下的“虚拟内存”.当物理内存不足时,拿出部分硬盘空间当SWAP分区(虚拟成内存)使用,从而解决内存容量不足的情况.Linux下的swap有两种实现形 ...

  4. Android Studio如何删除一个Module

    当你想在Android Studio中删除某个module时,大家习惯性的做法都是选中要删除的module,右键去找delete.但是在Android  Studio中你选中module,右键会发现没 ...

  5. centos6下安装docker

    安装docker对内核版本的要求很高,需要内核3.10以上. 一.docker卸载 查看内核版本: 如果不升级内核到3.10安装docker,后面会有很多奇怪的问题,像我就是拉取不到镜像. 以下我是r ...

  6. [Shell]CVE-2017-8464漏洞复现

    0x01 漏洞原理 Windows系统通过解析 .LNK 后缀文件时,是使用二进制来解析的,而当恶意的二进制代码被系统识别执行的时候就可以实现远程代码执行,由于是在explorer.exe进程中运行的 ...

  7. Dp优化之决策单调栈优化

    证明:g(i) ≤ g(j)   (i ≤ j) 令 d=g(i) , k<d , 设cut = x表示 f(i) = f(x) + w[x,i]    ( x < i ) 构造一个式子: ...

  8. fidder监控请求响应时间和请求IP

    1.增加监控请求的详情时间 在CustomRules.js的class Handlers中增加  //添加请求的响应时间 public static BindUIColumn("Time T ...

  9. 报错:使用java api连接redis集群时报错 READONLY You can't write against a read only slave.

    报错: READONLY You can’t write against a read only slave. 报错原因: 因为连接的是从节点,从节点只有读的权限,没有写的权限 解决方案: 进入red ...

  10. docker tcp配置

    1. Ubuntu Docker deamon监听tcp端口设置 https://www.jianshu.com/p/e278b0e44e1b 2. Centos https://www.cnblog ...