【小试牛刀】Stage-2 装饰器初探

原文链接:QC-L/blog

目前 JavaScript 的装饰器处于提案 Stage-2 阶段,且尚未稳定。因此与所有提案一样,在未来可能会发生变化。本文为个人的思考和见解,如有异议欢迎拍砖。

装饰器这个概念想必大家并不陌生,笔者最早遇到这个词是在学习 Java 的时候,主要应用是在 Java 的 Spring 中,其中有个概念叫 AOP(面向切面编程)。

当然这个概念在 JavaScript 中也已有其他的应用,比如 TypeScript,MobX 等。社区也有 Redux 相关的解决方案,如 @connect 装饰器的使用。

这里给大家带来的是有关 Babel 7.1.0 中实现的 @babel/babel-plugin-proposal-decorators 相关应用。

环境搭建

由于该提案依赖于 Babel 的提案插件,因此需要搭建一个简易的 Babel 编译环境。

新建目录,初始化 package.json:

$ mkdir test-decorator && cd test-decorator
$ npm init -y
复制代码

在 package.json 中添加 Babel 相关 package:

yarn add -D @babel/cli @babel/core @babel/preset-env
复制代码

创建 .babelrc

touch .babelrc
复制代码

添加如下配置:

{
  "presets": ["@babel/preset-env"]
}
复制代码

在目录中添加 src/index.js

class TestDecorator {
  method() {}
}
复制代码

package.json 中添加 build script 命令

{
  "name": "test-decorator",
  "version": "1.0.0",
  "description": "",
  "main": "src/index.js",
  "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1",
+    "build": "babel src -d dist"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.2.3",
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.3.1"
  }
}

复制代码

创建 index.html 文件,并引入 dist/index.js

<!doctype html>
<html>
<head>
  <title>测试</title>
</head>
<body>
  <script src="./dist/index.js"></script>
</body>
</html>
复制代码

执行 yarn build 即可。

装饰器初窥

1.编写一个类装饰器函数

修改 src/index.js:

class TestDecorator {
  method() {}
}
// 类装饰器函数
function decorator(value) {
  return function(classDescriptor) {
    console.log(classDescriptor);
  }
}	
复制代码

2.使用类装饰器

+ @decorator
class TestDecorator {
  method() {}
}
复制代码

3.安装 Babel 插件,并修改 .babelrc 文件:

$ yarn add -D @babel/plugin-proposal-decorators
复制代码
{
   "presets": ["@babel/preset-env"],
+  "plugins": [
+    ["@babel/plugin-proposal-decorators", {
+      "decoratorsBeforeExport": true // 用于标识装饰器所处位置(提案中讨论的点)
+    }]
+  ]
}
复制代码

注:decoratorsBeforeExport 是必需设置的选项,否则会抛出异常。为 true 时,会修饰在 export class 上方;为 false 时,会修饰在 export class 前。

// decoratorsBeforeExport: false
export @decorator class TestDecorator {}

// decoratorsBeforeExport: true
@decorator
export class TestDecorator {}
复制代码

如不设置 decoratorsBeforeExport 异常如下:

Error: [BABEL] /test-decorator/src/index.js: The decorators plugin requires a 'decoratorsBeforeExport' option, whose value must be a boolean. If you want to use the legacy decorators semantics, you can set the 'legacy: true' option. (While processing: "/test-decorator/node_modules/@babel/plugin-proposal-decorators/lib/index.js")
复制代码

4.执行 yarn build,打开 index.html 查看控制台结果。

装饰器参数详解

装饰器修饰的位置不同,所得的参数有所不同。并且有参数的装饰器与无参数的装饰器也有所区别。 装饰器可修饰内容如下:

  • class
  • class method
  • class fields

调用装饰器时,参数结构对比如下:

  1. 修饰 class

    与 TypeScript 以前之前的装饰器的区别在于,声明的装饰器方法的参数,为 Descriptor 类型。

    参数说明
    kindclass标识,用于说明当前修饰的内容
    elements[Descriptor]描述符组成的数组,描述类中所有的元素

    其中 elements 中对象与 method 和 fields 相同,后面介绍。

  2. 修饰 class method修饰 class fields

    参数说明methodfields
    kind标识,用于说明当前修饰的内容methodfield
    descriptor与 Object.defineProperty 中的 descriptor 相同[object Object][object Object]
    key所修饰的 method 或 fileds 的名称。可以是 String,Symbol 或 Private Namemethodx
    placement可选的参数为 "static","prototype" 或者 "own"prototype | static | ownprototype | static | own

    其中关于 key 的描述,在提案中有这么一段。

    For private fields or accessors, the key will be a Private Name--this is similar to a String or Symbol, except that it is invalid to use with property access [] or with operations such as Object.defineProperty. Instead, it can only be used with decorators.

    可能有些小伙伴未了解过 Object.defineProperty,这里针对 descriptor 介绍下其包含哪些属性:

    参数说明默认值
    configurabletrue当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除false
    enumerablefalse当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中false
    valuemethod该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)undefined
    writabletrue当且仅当该属性的writable为true时,value才能被赋值运算符改变。false
    getundefined当且仅当该属性的writable为true时,value才能被赋值运算符改变。undefined
    setundefined一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。undefined

    注意: 如果一个描述符不具有 value, writable, get 和 set 任意一个关键字,那么它将被认为是一个数据描述符。如果一个描述符同时有 (value 或 writable) 和 (get 或 set) 关键字,将会产生一个异常

如想尝试的可以自己打印一下,Demo 地址

示例
@classDecoratorArgs(function () {})
@classDecorator
class TestDecorator {
  @filedsDecoratorArgs('fileds')
  @filedsDecorator
  x = 10 // 注意,如果要在 class 中使用 fileds 需要额外引用 @babel/plugin-proposal-class-properties
  @methodDecoratorArgs(20)
  @methodDecorator
  method() {}
}

// 无参数 class decorator
function classDecorator(classDescriptor) {
  console.log(classDescriptor);
}

// 无参数 fileds decorator
function filedsDecorator(filedsDescriptor) {
  console.log(filedsDescriptor);
}

// 无参数 method decorator
function methodDecorator(elementDescriptor) {
  console.log(elementDescriptor);
}

// 有参数 class decorator
function classDecoratorArgs(args) {
  console.log(args);
  return function(classDescriptor) {
    console.log(classDescriptor);
  }
}

// 有参数 fileds decorator
function filedsDecoratorArgs(args) {
  console.log(args);
  return function(filedsDescriptor) {
    console.log(filedsDescriptor);
  }
}

// 有参数 method decorator
function methodDecoratorArgs(args) {
  console.log(args);
  return function(elementDescriptor) {
    console.log(elementDescriptor);
  }
}
复制代码

相关内容推荐

装饰器应用:

参考资料:

下一篇带大家动手实现一个装饰器,敬请期待。

转载于:https://juejin.im/post/5c52930c5188254d855d6693

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值