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代码

    /** * @Classname CustBankcardServiceImpl * @Description 计算项目代码量(行数) * @Date 2019/11/04 14:24 * @Crea ...

  2. 2019ICPC徐州自我反省及未来打算

    徐州站结束了有好几天了,然而为了热爱的网络课(qdu-zpj网络课你值得信赖),一直没时间写个博客,今天又来说点心里话 今年的ICPC,就这样都打完了,可惜最终也是没能拿金,不过拿到了块银,也算保底吧 ...

  3. 数据结构实验之图论十:判断给定图是否存在合法拓扑序列(SDUT 2140)

    分析:BFS判断是否有环. #include<bits/stdc++.h> using namespace std; typedef long long ll; int gra[200][ ...

  4. fatal: 'origin' does not appear to be a git repository

    git push时报以下错误: fatal: 'origin' does not appear to be a git repository fatal: Could not read from re ...

  5. ICEM-带肋圆柱

    原视频下载地址:https://yunpan.cn/cqUj6H9un37F2  访问密码 91af

  6. CodeForces - 1183E Subsequences (easy version) (字符串bfs)

    The only difference between the easy and the hard versions is constraints. A subsequence is a string ...

  7. Java 面向对象(八)

    常用类之String String字符串类 什么是字符串?就是把多个字符,串连一起. String字符串的本质 其实是一个char[]数组 /**该值用于字符存储.*/ private final c ...

  8. window.postMessage 跨窗口,跨iframe javascript 通信

    同源通信 执行它们的页面位于具有相同的协议(http/https),端口(80/443),主机(通常为域名) 时,这两个脚本才能相互通信 大多数情况下,网站就是内部的域名,所以是同源通信,可以相互访问 ...

  9. Flutter移动电商实战 --(52)购物车_数据模型建立和Provide修改

    根据json数据生成模型类 {"goodsId":"2171c20d77c340729d5d7ebc2039c08d","goodsName" ...

  10. uboot移植spi驱动

    记录一下在uboot内移植spi驱动的过程 芯片:freescale Mpc8308 uboot版本:u-boot-2009.11-rc1.2 需求:我们需要在uboot下通过spi配置一个时钟芯片( ...