大家好,我是林三心,用最通俗易懂的话讲最难的知识点是我的座右铭,基础是进阶的前提是我的初心。
装饰器是一种以 @ 符号开头的特殊语法,放在目标代码的前面用于包装或扩展代码功能。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() {}}
本文链接://www.dmpip.com//www.dmpip.com/showinfo-26-87263-0.html5分钟带你了解【前端装饰器】,“高大上”的“基础知识”
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com