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

栏目: JavaScript · 发布时间: 5年前

内容简介:注: javascript中也有在vscode里打开设置=>用户设置里面加入(tips:打开设置后也可以直接点击右上角的'{}'进行用户设置)就可以了;

许多面向对象都有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的目录结构是这样的

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

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) // true
复制代码

上面代码中,@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"
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

代码整洁之道:程序员的职业素养

代码整洁之道:程序员的职业素养

罗伯特·C.马丁 (Robert C.Martin) / 余晟、章显洲 / 人民邮电出版社 / 2016-9-1 / 49.00元

1. 汇聚编程大师40余年编程生涯的心得体会 2. 阐释软件工艺中的原理、技术、工具和实践 3. 助力专业软件开发人员具备令人敬佩的职业素养 成功的程序员在以往的工作和生活中都曾经历过大大小小的不确定性,承受过永无休止的压力。他们之所以能够成功,是因为拥有一个共同点,都深切关注创建软件所需的各项实践。他们将软件开发视为一种需要精雕细琢加以修炼的技艺,他们以专业人士的标准要求自己,......一起来看看 《代码整洁之道:程序员的职业素养》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具