【小試牛刀】Stage-2 裝飾器初探

原文連接:QC-L/blogjavascript

目前 JavaScript 的裝飾器處於提案 Stage-2 階段,且還沒有穩定。所以與全部提案同樣,在將來可能會發生變化。本文爲我的的思考和看法,若有異議歡迎拍磚。html

裝飾器這個概念想必你們並不陌生,筆者最先遇到這個詞是在學習 Java 的時候,主要應用是在 Java 的 Spring 中,其中有個概念叫 AOP(面向切面編程)。java

固然這個概念在 JavaScript 中也已有其餘的應用,好比 TypeScript,MobX 等。社區也有 Redux 相關的解決方案,如 @connect 裝飾器的使用。node

這裏給你們帶來的是有關 Babel 7.1.0 中實現的 @babel/babel-plugin-proposal-decorators 相關應用。git

環境搭建

因爲該提案依賴於 Babel 的提案插件,所以須要搭建一個簡易的 Babel 編譯環境。github

新建目錄,初始化 package.json:npm

$ mkdir test-decorator && cd test-decorator
$ npm init -y
複製代碼

在 package.json 中添加 Babel 相關 package:編程

yarn add -D @babel/cli @babel/core @babel/preset-env
複製代碼

建立 .babelrcjson

touch .babelrc
複製代碼

添加以下配置:segmentfault

{
  "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 類型。

    參數 說明
    kind class 標識,用於說明當前修飾的內容
    elements [Descriptor] 描述符組成的數組,描述類中全部的元素

    其中 elements 中對象與 method 和 fields 相同,後面介紹。

  2. 修飾 class method修飾 class fields

    參數 說明 method fields
    kind 標識,用於說明當前修飾的內容 method field
    descriptor 與 Object.defineProperty 中的 descriptor 相同 [object Object] [object Object]
    key 所修飾的 method 或 fileds 的名稱。能夠是 String,Symbol 或 Private Name method x
    placement 可選的參數爲 "static","prototype" 或者 "own" prototype | static | own prototype | 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 介紹下其包含哪些屬性:

    參數 說明 默認值
    configurable true 當且僅當該屬性的 configurable 爲 true 時,該屬性描述符纔可以被改變,同時該屬性也能從對應的對象上被刪除 false
    enumerable false 當且僅當該屬性的enumerable爲true時,該屬性纔可以出如今對象的枚舉屬性中 false
    value method 該屬性對應的值。能夠是任何有效的 JavaScript 值(數值,對象,函數等) undefined
    writable true 當且僅當該屬性的writable爲true時,value才能被賦值運算符改變。 false
    get undefined 當且僅當該屬性的writable爲true時,value才能被賦值運算符改變。 undefined
    set undefined 一個給屬性提供 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);
  }
}
複製代碼

相關內容推薦

裝飾器應用:

參考資料:

下一篇帶你們動手實現一個裝飾器,敬請期待。

相關文章
相關標籤/搜索