大家好,我是林三心,用最通俗易懂的話講最難的知識點是我的座右銘,基礎是進階的前提是我的初心。
裝飾器是一種以 @ 符號開頭的特殊語法,放在目標代碼的前面用于包裝或擴展代碼功能。JavaScript 的裝飾器語法目前仍處于提案階段,現階段使用的話需要通過 bable 等方式進行編譯之后,才能在瀏覽器正常運行。裝飾器分為兩種:類裝飾器,類成員裝飾器,分別用于裝飾我們的類以及類的成員。
class MyClass { constructor() {}}
比如現在有一個類或者函數 MyClass,它的身上沒有任何的東西,但是我想要給他加一個 log 方法,那我們應該怎么做呢?很多人回想說,直接在它身上加一個 log 方法即可~
class MyClass { constructor() {} log() {}}
但是這么做的話,一個 class 是能做,那如果要給 1000 個 class 加上 log方法呢?那豈不是每一個都得寫~很麻煩,這個時候可以使用 裝飾器 去拓展每一個 class
裝飾器接收的參數是裝飾的目標類,這里的 cls 就是 MyClass
function addConcole(target) { // 拓展原型方法 target.prototype.log = function(msg) { console.log(`[${new Date()} ${msg}`); }; // 拓展靜態屬性 target.myName = '一個類' return target;}@addConcoleclass MyClass { constructor() {}}const myObj = new MyClass();myObj.log('林三心');// [Sat Jul 08 2023 17:31:55 GMT+0800 (中國標準時間) 林三心console.log(MyClass.myName)// 一個類
我們在使用一些 Node 的框架時,在寫接口的時候,我們可能會經常看到這樣的代碼
class Doc { @Get('doc') async findDocById(id) {} @Post('doc') async createDoc(data) {}}
其實這個 @Get 和 @Post ,是框架提供給我們的 類成員裝飾器,是的,類成員也能使用裝飾器,類成員裝飾器接收三個參數:
function Get(path) { return function(target, key, descriptor) { console.log({ target, key, descriptor }) }}
圖片
他的基本實現原理大概是這樣的
function Get(routePath) { return function(target, key, descriptor) { const originalMethod = descriptor.value; // 保存原始方法 descriptor.value = function() { // 在原始方法執行前加入邏輯 console.log('處理 Get 請求,路由路徑: ' + routePath); // 執行原始方法 const result = originalMethod.apply(this, arguments); // 在原始方法執行后加入邏輯 console.log('Get 請求處理完成'); return result; }; return descriptor; };}
上面我們介紹了一下 Nodejs Url 的路由匹配基本原理,但是這是不夠的,因為很多接口還需要權限控制,比如:
這也可以用裝飾器來實現,并且裝飾器是可以疊加的~
class Doc { @Get('doc') @Role('admin') async findDocById(id) {} @Post('doc') @Role('superAdmin') async createDoc(data) {}}
裝飾器疊加的執行順序是 從下往上 的~我們可以看一下下面的例子,發現輸出順序是
function A () { console.log(1)}function B () { console.log(2)}class Doc { @A @B async test() {}}
至于權限控制的裝飾器實現,需要根據不同業務去實現,我這里就粗略實現一下
function Role(permissions) { return function(target, key, descriptor) { const originalMethod = descriptor.value; // 保存原始方法 descriptor.value = function() { // 在原始方法執行前進行權限驗證 const user = getCurrentUser(); // 獲取當前用戶信息 // 檢查用戶是否擁有所需權限 const hasPermission = checkUserPermissions(user, permissions); if (!hasPermission) { // 如果用戶沒有權限,則拋出錯誤或執行其他處理 throw new Error('無權限訪問該接口'); } // 執行原始方法 const result = originalMethod.apply(this, arguments); return result; }; return descriptor; };}
我們想要在執行某個函數的時候,記錄一下
這個時候我們也可以使用裝飾器來完成,非常方便!!!
// 日志裝飾器函數function logDecorator(target, key, descriptor) { const originalMethod = descriptor.value; // 保存原始方法 descriptor.value = function(...args) { console.log(`調用函數:${key}`); console.log(`參數:${JSON.stringify(args)}`); // 執行原始方法 const result = originalMethod.apply(this, args); console.log(`返回值:${result}`); return result; }; return descriptor;}// 示例類class Example { @logDecorator greet(name) { return `Hello, ${name}!`; }}// 測試const example = new Example();example.greet('林三心');
如果我們執行一個方法,獲取返回值需要經過一系列的計算,非常耗時間,那么我們可以判斷入參,第一次時計算完緩存起來,第二次的時候如果還是這個入參,就直接從緩存中去拿,這個操作也可以使用裝飾器去完成
// 緩存裝飾器函數function cacheDecorator(target, key, descriptor) { const cache = {}; // 緩存對象 const originalMethod = descriptor.value; // 保存原始方法 descriptor.value = function(...args) { const cacheKey = JSON.stringify(args); // 生成緩存鍵 if (cacheKey in cache) { console.log('從緩存中獲取結果'); return cache[cacheKey]; // 直接返回緩存結果 } // 執行原始方法 const result = originalMethod.apply(this, args); console.log('將結果緩存起來'); cache[cacheKey] = result; // 緩存結果 return result; }; return descriptor;}// 示例類class Example { @cacheDecorator getValue(key) { console.log('執行函數邏輯'); return key + Math.random(); // 模擬復雜的計算邏輯 }}// 測試const example = new Example();console.log(example.getValue('foo'));console.log(example.getValue('foo')); // 從緩存中獲取結果
對于防抖節流,我們平時可能會這么去做
class C { onClick = debounce(fn, 100)}
但是這么做的話會使這個函數不好拓展,所以使用裝飾器真的很方便
// 防抖裝飾器function debounce(time) { return function (target, key, descriptor) { const oldFunction = descriptor.value; let timer = null; descriptor.value = function () { clearTimeout(timer); timer = setTimeout(() => { oldFunction.apply(this, arguments) }, time); }; return descriptor; }}// 節流裝飾器function throttle(time) { return function (target, key, descriptor) { const oldFunction = descriptor.value; let isLock = false; descriptor.value = function() { if(isLock) { return; } isLock = true; oldFunction.apply(this, arguments); setTimeout(() => { isLock = false; }, time); } return descriptor; }}class C { @debounce(1000) onClick() {} @throttle(1000) onScroll() {}}
本文鏈接:http://www.tebozhan.com/showinfo-26-87263-0.html5分鐘帶你了解【前端裝飾器】,“高大上”的“基礎知識”
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com