JavaScript裝飾器

一、裝飾器簡介

:官方ES6入門叫修飾器,本文以裝飾器贅述。
通常在 Java 項?中?到如下圖所示的代碼,下圖中涉及“@PostMapping”和“@ResponseBody" 在Java中通常解釋為”注解“。通過這種編程?式,可以很?便地將?些邏輯封裝起來,如:
Controller 的請求?法(get、post 等)和路由,參數(shù)的序列化等等。
如果不使?注解,那么上述這些邏輯就需要開發(fā)者??去實現(xiàn)了。

Java類

裝飾器是一種用于修改類的語法,它可以在不修改原始代碼的情況下增強其功能。
裝飾器可以實現(xiàn)AOP(面向切面編程)的功能
可見裝飾器存在以下優(yōu)點:

  • 減少冗余代碼
  • 提高代碼擴展性

二、JavaScript中的裝飾器

2.1 裝飾器演練

2.1.1 裝飾器瀏覽器環(huán)境下如何運行

由于@ 符號后面的標識符被解析為一個裝飾器。然而,裝飾器目前還是處于實驗階段,不在所有的 JavaScript 環(huán)境中都被支持,此時需要babel轉(zhuǎn)換。

npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties

創(chuàng)建一個.babelrc文件

{
    "presets": ["@babel/preset-env"],
    "plugins": [
      ["@babel/plugin-proposal-decorators", { "legacy": true }],
      ["@babel/plugin-proposal-class-properties", { "loose": true }]
    ]
  }

運行下列指令打包輸出轉(zhuǎn)義后的代碼,在html文件中引入

npx babel test.js --out-file dist.js

2.1.2 簡單演示

@test
class TestClass {
  constructor() {
    console.log('test ok')
  }
}

function test(t) {
  t.isTest = true
}
console.log(TestClass.isTest) // true

此時按照上述引入后,可以直接打印出結(jié)果。
上述代碼中'@test' 就是一個裝飾器,修改testClass類,在它上添加了靜態(tài)屬性test。
因此裝飾器基本行為如下:

@decorator
class A {} 
-------------------
// 等同于
class A {} 
A = decorator(A) || A

裝飾器也可以接受參數(shù)(在包裝一層函數(shù))或者在原型對象上操作

@test(1)
class TestClass {
  constructor() {}
}

@test('加參數(shù)ok')
class BooClass {
  constructor() {}
}


function test(t) {
  return function(target) {
     target.isTest = t
  }
}
console.log(TestClass.isTest) // true
console.log(BooClass.isTest) // true
@test(1)
class TestClass {
  constructor() {}
}

function test(t) {
  return function(target) {
     target.prototype.isTest = t
  }
}
let a = new TestClass()
console.log(a.isTest) // 1

2.2 裝飾器使用場景

2.2.1 類場景使用

1)類方法裝飾器

屬性裝飾器表達式會在運行時當作函數(shù)被調(diào)用,傳入下列3個參數(shù):

  • 對于靜態(tài)成員來說是類的構(gòu)造函數(shù),對于實例成員是類的原型對象
  • 成員的名字
  • 成員的描述對象

注:屬性描述符不會做為參數(shù)傳入屬性裝飾器,這與TypeScript是如何初始化屬性裝飾器的有關(guān)。

function readonly(target, name, descriptor) {
  // descriptor值類似Object.prototype原型
  // {
  //     value: specifiedFunction,
  //   enumerable: false,
  //   configurable: true,
  //   writable: true
  // }
  descriptor.writable = false;
  return descriptor;
}
class Person {
  @readonly
  name() {
      console.log("~~~");
  }
}
const p = new Person();
p.name() // ~~~
p.name = () => {
  console.log('111')
} // dist.js:43 Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Person>'

將當前類設(shè)置為只讀,等同于Object.defineProperty使用。類似包含可枚舉,凍結(jié)等

2)類屬性裝飾器

屬性裝飾器表達式會在運行時當作函數(shù)被調(diào)用,傳入下列2個參數(shù):

  • 對于靜態(tài)成員來說是類的構(gòu)造函數(shù),對于實例成員是類的原型對象
  • 成員的名字

注:屬性描述符不會做為參數(shù)傳入屬性裝飾器,這與TypeScript是如何初始化屬性裝飾器的有關(guān)

class MyTestableClass {
  @testable
  name;
  constructor(name) {
      this.name = name;
  }
}

function testable(target, name) {
  console.log(arguments)
}
類屬性裝飾器

3)復合裝飾器

注:一個方法有多個修飾器,該方法會先從外到內(nèi)進入修飾器,接著由內(nèi)向外執(zhí)行?!居悬c類似棧,先進后出】

function dec(id) {
  console.log('進入前:',id)
  // 若不寫這段,會報錯
  // Uncaught TypeError: decorator is not a function
  return (target, property, descriptor) => {
    console.log('進入后:', id)
  }
}

class Example {
  @dec(1)
  @dec(2)
  method() {}
}

// 進入前: 1
// 進入前: 2
// 進入后: 2
// 進入后: 1

2.2.3 無法用于函數(shù)

無法用于函數(shù),是因為函數(shù)存在函數(shù)提升

let count = 0
const add = () => {
  count++
}

@add
const foo = () => {}
console.log(foo.add())

實際操作時,發(fā)現(xiàn)babel轉(zhuǎn)義或者vscode強限制住裝飾器只能支持


無法運用于函數(shù)

2.3 應用場景

關(guān)于裝飾器可以便于我們封裝一些工具場景,比如埋點、日志、發(fā)布等自動化場景。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容