原文連接: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
與 TypeScript 之前以前的裝飾器的區別在於,聲明的裝飾器方法的參數,爲 Descriptor 類型。
參數 | 值 | 說明 |
---|---|---|
kind | class | 標識,用於說明當前修飾的內容 |
elements | [Descriptor] | 描述符組成的數組,描述類中全部的元素 |
其中 elements 中對象與 method 和 fields 相同,後面介紹。
修飾 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 asObject.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);
}
}
複製代碼
裝飾器應用:
參考資料:
下一篇帶你們動手實現一個裝飾器,敬請期待。