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这个类的行为,为它加上了武器属性weapon。transform函数的参数target是IronMan类本身。

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)

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

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. spring boot, 容器启动后执行某操作

    常有在spring容器启动后执行某些操作的需求,现做了一个demo的实现,做一下记录,也希望可以给需要的同学提供参考. 1.spring启动后,以新线程执行后续需要的操作,所以执行类实现Runnabl ...

  2. springboot启动时执行任务CommandLineRunner

    # SpringBoot中CommandLineRunner的作用> 平常开发中有可能需要实现在项目启动后执行的功能,SpringBoot提供的一种简单的实现方案就是添加一个model并实现Co ...

  3. Other-Website-Contents.md

    title: 本站目录 categories: Other sticky: 10 toc: true keywords: 机器学习基础 深度学习基础 人工智能数学知识 机器学习入门 date: 999 ...

  4. 3、spark Wordcount

    一.用Java开发wordcount程序 1.开发环境JDK1.6 1.1 配置maven环境 1.2 如何进行本地测试 1.3 如何使用spark-submit提交到spark集群进行执行(spar ...

  5. 基于Shiro的登录功能 设计思路

    认证流程 Shiro的认证流程可以看作是个“有窗户黑盒”, 整个流程都有框架控制,对外的入口只有subject.login(token);,这代表“黑盒” 流程中的每一个组件,都可以使用Spring ...

  6. Ubuntu 源 (ros)

    deb http://archive.ubuntu.com/ubuntu/ trusty main restricted universe multiverse deb http://archive. ...

  7. Beyond compare4密钥

    w4G-in5u3SH75RoB3VZIX8htiZgw4ELilwvPcHAIQWfwfXv5n0IHDp5hv1BM3+H1XygMtiE0-JBgacjE9tz33sIh542EmsGs1yg6 ...

  8. 前端代码规范-CSS

    CSS规范 一.命名规范BEM(Block Element Modifier) 1.Block name -- 实体名称中的单词之间用连字符分隔(-) HTML <div class=" ...

  9. docker容器里面执行top报“TERM environment variable not set.”

    解决: [hadoop@master ~]$ docker exec -ti 6eca7d27a988 /bin/bashroot@6eca7d27a988:/# topTERM environmen ...

  10. easyUI的datagrid表格的使用

    实现easyUI表格的里面数据的增删改查功能.SQL使用Oracle和mybatis. 话不多说,直接上代码. 首先是前段部分的. var session = GetSession(); var pa ...