ES7 装饰器

2023-11-07 13:52
文章标签 装饰 es7

本文主要是介绍ES7 装饰器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

阅读能解决问题-:
1)装饰器有什么用,主要功能?
2)装饰器?减少引入,减少代码,可以扩展,不需要改原有方法的代码位置
3)放置位置,可以是类、类成员(方法/属性)
4)执行顺序
5)目前项目代码可以加装饰器吗?
6)是不是一定要用类

目录:
1、前言
2、ES7 装饰器
3、应用
4、装饰器只能用于类上吗?为什么不能用于函数?—因为存在函数提升,类不会
5、第三方 core-decorators.js,提供一下装饰器
6、已有项目添加装饰器

1、前言

装饰器是 ES7 中的一个提案,是一种与类(class)相关的语法,用来注释或修改类和类方法。装饰器在 Python 和 Java 等语言中也被大量使用。装饰器是实现 AOP(面向切面)编程的一种重要方式。
装饰器类:

@frozen
class Foo {
@configurable(false)
method() {}}

@frozen 和 @configurable 就是我们说的装饰器。
可以看出是通过@来使用装饰器。一共用了两种:一个用在类上,一个用在方法上。
装饰器属性:这个 @readonly 可以将 count 属性设置为只读。可以看出来,装饰器大大提高了代码的简洁性和可读性。
class Person {
@readonly count = 0;
}

2、ES7 装饰器

2.1 python中的装饰器

def auth(func):def inner(request,*args,**kwargs):v = request.COOKIES.get('user')if not v:return redirect('/login')return func(request, *args,**kwargs)return inner@auth
def index(request):v = request.COOKIES.get("user")return render(request,"index.html",{"current_user":v})

这个 auth 装饰器是通过检查 cookie 来判断用户是否登录的。auth 函数是一个高阶函数,它接收了一个 func 函数作为参数,返回了一个新的 inner 函数。
在 inner 函数中进行 cookie 的检查,由此来判断是跳回登录页面还是继续执行 func 函数。
在所有需要权限验证的函数上,都可以使用这个 auth 装饰器,很简洁明了且无侵入。

2.2 javascript装饰器

JavaScript 中的装饰器和 Python 的装饰器类似,依赖于 Object.defineProperty,一般是用来装饰类、类属性、类方法。
使用装饰器可以做到不直接修改代码,就实现某些功能,做到真正的面向切面编程。这在一定程度上和 Proxy 很相似,但使用起来比 Proxy 会更加简洁。

2.3 类装饰器-接受一个目标类作为参数

1)添加静态属性

const decoratorClass = (targetClass) => {targetClass.test = '123'
}
@decoratorClass
class Test {}
Test.test; // '123'

2)修改原型,给实例添加新属性

const withSpeak = (targetClass) => {const prototype = targetClass.prototype;prototype.speak = function() {console.log('I can speak ', this.language);}
}
@withSpeak
class Student {constructor(language) {this.language = language;}
}
const student1 = new Student('Chinese');
const student2 = new Student('English');
student1.speak(); // I can speak  Chinesestudent2.speak(); // I can speak  Chinese

3)利用高阶函数的属性,给装饰器传参,通过参数来判断对类进行什么处理。

const withLanguage = (language) => (targetClass) => {targetClass.prototype.language = language;
}
@withLanguage('Chinese')
class Student {
}
const student = new Student();
student.language; // 'Chinese'

4)react-redux中,需要将store数据映射到组件中,connect 是一个高阶组件,它接收了两个函数

mapStateToProps 和 mapDispatchToProps 以及一个组件 App,最终返回了一个增强版的组件。
class App extends React.Component {
}
connect(mapStateToProps, mapDispatchToProps)(App)
使用装饰器写法:
@connect(mapStateToProps, mapDispatchToProps)
class App extends React.Component {
}

2.4 类属性装饰器

类属性装饰器可以用在类的属性、方法、get/set 函数中,一般会接收三个参数:

  1. target:被修饰的类
  2. name:类成员的名字
  3. descriptor:属性描述符,对象会将这个参数传给 Object.defineProperty
    使用类属性装饰器可以做到很多有意思的事情,比如 readonly 的例子:
function readonly(target, name, descriptor) {descriptor.writable = false;return descriptor;
}
class Person {@readonly name = 'person'
}
const person = new Person();
person.name = 'tom'; 
// 还可以用来统计一个函数的执行时间,以便于后期做一些性能优化。
function time(target, name, descriptor) {const func = descriptor.value;if (typeof func === 'function') {descriptor.value = function(...args) {console.time();const results = func.apply(this, args);console.timeEnd();return results;}}
}
class Person {@timesay() {console.log('hello')}
}
const person = new Person();
person.say();

2.5 装饰器组合

如果想要多个装饰器,可以叠加使用

class Person {@time@logsay() {}
}

在这里插入图片描述

3、应用

1)基本使用:

给一个类添加log或console,但如果这个方法所有类都要加,那得一个一个写,很麻烦。这时候可以用装饰器去拓展每一个class:

function addConcole(target) {// 拓展原型方法target.prototype.log = function(msg) {console.log(`[${new Date()} ${msg}`);};// 拓展静态属性target.myName = '一个类'return target;
}
@addConcole
class MyClass {constructor() {}
}const myObj = new MyClass();
myObj.log('林三心');
// [Sat Jul 08 2023 17:31:55 GMT+0800 (中国标准时间) 林三心
console.log(MyClass.myName)
// 一个类

拓展原型方法,拓展静态属性

2)Node路由请求Url(类成员装饰器)

在使用一些 Node 的框架时,在写接口的时候,我们可能会经常看到这样的代码

  • 当我们请求路径是 GET doc 时会匹配到findDocById
  • 当我们请求路径是 POST doc 时会匹配到createDoc
class Doc {@Get('doc')async findDocById(id) {}@Post('doc')async createDoc(data) {}
}

其实这个 @Get 和 @Post ,是框架提供给我们的 类成员装饰器,是的,类成员也能使用装饰器,类成员装饰器接收三个参数:

  • target 是目标类的原型对象
  • key 表示目标类成员的键名
  • descriptor 是一个属性描述符对象,它包含目标类成员的属性特性(例如 value、writable 等)
function Get(path) {return function(target, key, descriptor) {console.log({target,key,descriptor})}
}

在这里插入图片描述

3)接口权限控制(类成员装饰器叠加)

  • GET doc 接口只能 管理员 才能访问
  • POST doc 接口只能 超级管理员 才能访问
function authenticated(target, key, descriptor) {const originalMethod = descriptor.value;descriptor.value = function(...args) {if (isAuthenticated()) {originalMethod.apply(this, args);} else {console.log('Unauthorized access!');}};return descriptor;
}
class Doc {@Get('doc')@authenticated('admin')async findDocById(id) {}@Post('doc')@authenticated('superAdmin')async createDoc(data) {}
}

多个装饰器叠加执行顺序:从下往上, 离class定义最近的先执行。

4)记录日志的装饰器:

  • 函数调用时间
  • 函数调用参数
// 日志装饰器函数
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 {@logDecoratorgreet(name) {return `Hello, ${name}!`;}
}// 测试
const example = new Example();
example.greet('林三心');

5)缓存装饰器

// 缓存装饰器函数
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 {@cacheDecoratorgetValue(key) {console.log('执行函数逻辑');return key + Math.random(); // 模拟复杂的计算逻辑}
}// 测试
const example = new Example();
console.log(example.getValue('foo'));
console.log(example.getValue('foo')); // 从缓存中获取结果

6)防抖节流

@debounce(500)
submit() {}@throttle(200)
handleScroll() {}

7)错误处理装饰器

跟日志装饰器一样,错误其实也是日志的一部分,错误日志非常重要,因为 Nodejs 的线上报错,大部分都需要通过查日志来进行定位,所以我们也可以封装一个错误的处理装饰器~

function errorHandler(target, key, descriptor) {const originalMethod = descriptor.value;descriptor.value = function (...args) {try {originalMethod.apply(this, args);} catch (error) {console.error(`Error occurred in ${key}:`, error);}};return descriptor;
}
// 使用:
class Common {@log()commonRequest(url, params) {return request(url, params)}
}const common = new Common()
common.commonRequest('http://xxx.com', { name: 'l' })
Error occurred in commonRequest: Request Error

8)计时装饰器

function timing(target, key, descriptor) {const originalMethod = descriptor.value;descriptor.value = function(...args) {const start = performance.now();const result = originalMethod.apply(this, args);const end = performance.now();console.log(`Execution time of ${key}: ${end - start} milliseconds`);return result;};return descriptor;
}class Common {@timing()commonRequest(url, params) {return request(url, params)}
}
const common = new Common()
common.commonRequest()
Execution time of commonRequest: 20 milliseconds

9)参数检验装饰器

在没有ts类型监测的时候,可以用这个:

function validateArgs(...types) {return function (target, key, descriptor) {const originalMethod = descriptor.value;descriptor.value = function (...args) {for (let i = 0; i < types.length; i++) {const type = types[i];const arg = args[i];if (typeof arg !== type) {throw new Error(`Invalid argument type at index ${i}`);}}originalMethod.apply(this, args);};return descriptor;};
}
class Common {@validateArgs(['string', 'object'])commonRequest(url, params) {return request(url, params)}
}
const common = new Common()
common.commonRequest(123, 123) // 报错

4、装饰器只能用于类上吗?为什么不能用于函数?—因为存在函数提升,类不会

var counter = 0;var add = function () {counter++;
};@add
function foo() {}

👆 想在执行函数的时候couter+1,但其实counter等于0,因为函数提升后是这样的:

@add
function foo() {
}
var counter;
var add;
counter = 0;
add = function () {counter++;
};

5、第三方 core-decorators.js,提供一下装饰器

@autobind:使得方法中的this对象,绑定原始对象
@readonly:使得属性或方法不可写。
@override:检查子类的方法,是否正确覆盖了父类的同名方法,如果不正确会报错。
@deprecate (别名@deprecated):在控制台显示一条警告,表示该方法将废除。

6、目前已有项目上添加装饰器:

确保项目使用了 Babel 或 TypeScript 这样的工具来编译js 代码,因为装饰器是 ES7 的一个提案,需要转译为 ES5 以在现有浏览器中运行。

1)安装依赖:
对于babel需要添加一下依赖:

npm install --save-dev @babel/plugin-proposal-decorators 
# 或 
yarn add --dev @babel/plugin-proposal-decorators

对于 TypeScript,无需额外安装插件,装饰器已经内置在 TypeScript 中。
2)配置

对于 Babel,在 .babelrc 文件中添加以下配置:
{"plugins": [["@babel/plugin-proposal-decorators", { "legacy": true }]]
}

对于 TypeScript,确保 tsconfig.json 文件中有以下配置:

{"compilerOptions": {"experimentalDecorators": true,"emitDecoratorMetadata": true}
}

参考+其他学习资料:
1、ES7-装饰器Decorator详解 https://zoeice.com/es-decorator/
2、提案地址:JavaScript Decorators。
3、 5分钟带你了解【前端装饰器】,“高大上”的“基础知识” (qq.com)
4、 分享能提高开发效率,提高代码质量的八个前端装饰器函数~ (qq.com)

这篇关于ES7 装饰器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/364060

相关文章

Python中的属性装饰器:解锁更优雅的编程之道

引言 在Python的世界里,装饰器是一个强大的工具,它允许我们以一种非侵入性的方式修改函数或方法的行为。而当我们谈论“属性装饰器”时,则是在探讨如何使用装饰器来增强类中属性的功能。这不仅让我们的代码更加简洁、易读,同时也提供了强大的功能扩展能力。本文将带你深入了解属性装饰器的核心概念,并通过一系列实例展示其在不同场景下的应用,从基础到进阶,再到实际项目的实战经验分享,帮助你解锁Python编程

python+selenium2学习笔记unittest-04装饰器skip用法

在运行测试用例时,有时需跳过或判断用例时,可以用装饰器来实现 主要的几个方法就是下面的这几种 import unittestclass test(unittest.TestCase):def setUp(self):pass@unittest.skip('跳过')def test_01(self):print("直接跳过")@unittest.skipIf(3>2,'当条件为TRUE跳过')

python内置装饰器@staticmethod,@classmethod

2.@staticmethod,@classmethod 有了@property装饰器的了解,这两个装饰器的原理是差不多的。@staticmethod返回的是一个staticmethod类对象,而@classmethod返回的是一个classmethod类对象。他们都是调用的是各自的__init__()构造函数。 一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。 而使用@stat

Python中的装饰器及其应用场景

Python中的装饰器(Decorators)是一个非常强大且优雅的特性,它允许你在不修改原有函数或类定义的情况下,给函数或类增加新的功能。装饰器本质上是一个函数,它接收一个函数(或类)作为参数,并返回一个新的函数(或类),这个新函数(或类)会包含原函数(或类)的所有功能,并在其基础上增加额外的功能。装饰器的这种特性使得代码的重用性、可读性和可维护性都得到了极大的提升。 一、装饰器的基本概念

七、装饰器模式

装饰器模式(Decorator Pattern)是一种结构型设计模式,允许在不改变对象自身的情况下,动态地向对象添加新功能。它通过将功能附加到对象的方式来增强其行为,提供了一种灵活的替代方案来使用子类扩展功能。 主要组成部分: 抽象构件(Component): 定义一个接口或抽象类,声明了要装饰的对象的基本功能。 具体构件(ConcreteComponent): 实现抽象构件的具体类,是

java设计模式day03--(结构型模式:代理模式、适配器模式、装饰者模式、桥接模式、外观模式、组合模式、享元模式)

5,结构型模式 结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。 由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: 代理模式 适配器模式 装饰者模式 桥接模式 外观模式 组合模式

js装饰器简单理解

装饰器(Decorator)是一种与类(class)相关的语法,用来注释或修改类和类方法。 装饰器是一种函数,写成@函数名。它可以放在类和类方法的定义前面。如下: @frozenclass Foo {@configurable(false)@enumerable(true)method() {}@throttle(500)expensiveMethod() {}} 上面代

61、Python之函数高级:为函数添加方法,实现属性可变的装饰器

引言 今天文章的标题,初读起来可能有些拗口,什么叫“为函数添加方法”?但是,如果真正对“Python函数也是对象”这个理念有清晰的理解的话,其实,也是不难理解的,本质上就是给一个对象新增一个自定义方法。通过这样做,我们就可以实现在运行过程中,对装饰器的属性进行动态修改了。 本文的主要内容有: 1、函数对象添加自定义方法 2、属性可变的动态装饰器 函数对象添加自定义方法 其实,这一点

JAVA学习笔记之​流,序列化,Socket和装饰者模型

http://toutiao.com/a6329568040976204033/?tt_from=mobile_qq&utm_campaign=client_share&app=explore_article&utm_source=mobile_qq&iid=5215874113&utm_medium=toutiao_ios JAVA学习笔记之​流,序列化,Socket和装饰者模型 御景

58、Python之函数高级:不定参数的函数,写出更加通用的装饰器

引言 上一篇文章中,我们见到引入了Python中的装饰器,通过一个简单的案例实现了一个初步的装饰器,但是,这个装饰器其实是有些缺陷。这一篇文章中,我们对上一篇文章中的装饰器进行一个优化升级,从而写出更加通用的装饰器。 本文的主要内容有: 1、简陋装饰器的缺陷 2、关于函数参数的更加通用的写法 3、更加规范通用的装饰器实现方式 简陋装饰器的缺陷 我们之所以需要使用装饰器,很多时候就