面向切面编程(Aspect-oriented programming,AOP)是一种编程范式。做后端 Java web 的同学,特别是用过 Spring 的同学肯定对它非常熟悉。AOP 是 Spring 框架里面其中一个重要概念。可是在 Javascript 中,AOP 是一个经常被忽视的技术点。
场景
假设你现在有一个牛逼的日历弹窗,有一天,老板让你统计一下每天这个弹窗里面某个按钮的点击数,于是你在弹窗里做了埋点;
过了一个星期,老板说用户反馈这个弹窗好慢,各种卡顿。你想看一下某个函数的平均执行时间,于是你又在弹窗里加上了性能统计代码。
时间久了,你会发现你的业务逻辑里包含了大量的和业务无关的东西,即使是一些你已经封装过的函数。
那么 AOP 就是为了解决这类问题而存在的。
关注点分离
分离业务代码和数据统计代码(非业务代码),无论在什么语言中,都是AOP的经典应用之一。从核心关注点中分离出横切关注点,是 AOP 的核心概念。
在前端的常见需求中,有以下一些业务可以使用 AOP 将其从核心关注点中分离出来
- Node.js 日志log
- 埋点、数据上报
- 性能分析、统计函数执行时间
- 给ajax请求动态添加参数、动态改变函数参数
- 分离表单请求和验证
- 防抖与节流
装饰器(Decorator)
提到 AOP 就要说到装饰器模式,AOP 经常会和装饰器模式混为一谈。
在ES6之前,要使用装饰器模式,通常通过Function.prototype.before做前置装饰,和Function.prototype.after做后置装饰(见《Javascript设计模式和开发实践》)。
Javascript 引入的 Decorator ,和 Java 的注解在语法上很类似,不过在语义上没有一丁点关系。Decorator 提案提供了对 Javascript 的类和类里的方法进行装饰的能力。(尽管只是在编译时运行的函数语法糖)
埋点数据上报
因为在使用 React 的实际开发中有大量基于 Class 的 Component,所以我这里用 React 来举例。
比如现在页面中有一个button,点击这个button会弹出一个弹窗,与此同时要进行数据上报,来统计有多少用户点击了这个登录button。
import React, { Component } from 'react'; import send from './send'; class Dialog extends Component { constructor(props) { super(props); } @send showDialog(content) { // do things } render() { return ( <button onClick={() => this.showDialog('show dialog')}>showDialog</button> ) } } export default Dialog;
上面代码引用了@send装饰器,他会修改这个 Class 上的原型方法,下面是@send装饰器的实现
export default function send(target, name, descriptor) { let oldValue = descriptor.value; descriptor.value = function () { console.log(`before calling ${name} with`, arguments); return oldValue.apply(this, arguments); }; return descriptor; }
在按钮点击后执行showDialog前,可以执行我们想要的切面操作,我们可以将埋点,数据上报相关代码封装在这个装饰器里面来实现 AOP。
前置装饰和后置装饰
上面的send这个装饰器其实是一个前置装饰器,我们可以将它再封装一下使它可以前置执行任意函数。
function before(beforeFn = function () { }) { return function (target, name, descriptor) { let oldValue = descriptor.value; descriptor.value = function () { beforeFn.apply(this, arguments); return oldValue.apply(this, arguments); }; return descriptor; } }
这样我们就可以使用@before装饰器在一个原型方法前切入任意的非业务代码。
function beforeLog() { console.log(`before calling ${name} with`, arguments); } class Dialog { ... @before(beforeLog) showDialog(content) { // do things } ... }
和@before装饰器类似,可以实现一个@after后置装饰器,只是函数的执行顺序不一样。
function after(afterFn = function () { }) { return function (target, name, descriptor) { let oldValue = descriptor.value; descriptor.value = function () { let ret = oldValue.apply(this, arguments); afterFn.apply(this, arguments); return ret; }; return descriptor; } }
性能分析
有时候我们想统计一段代码在用户侧的执行时间,但是又不想将打点代码嵌入到业务代码中,同样可以利用装饰器来做 AOP。
function measure(target, name, descriptor) { let oldValue = descriptor.value; descriptor.value = function () { let ret = oldValue.apply(this, arguments); performance.mark("startWork"); afterFn.apply(this, arguments); performance.mark("endWork"); performance.measure("work", "startWork", "endWork"); performance .getEntries() .map(entry => JSON.stringify(entry, null, 2)) .forEach(json => console.log(json)); return ret; }; return descriptor; }
在要统计执行时间的类方法前面加上@measure就行了,这样做性能统计的代码就不会侵入到业务代码中。
class Dialog { ... @measure showDialog(content) { // do things } ... }
小结
面向切面编程的重点就是将核心关注面分离出横切关注面,前端可以用 AOP 优雅的来组织数据上报、性能分析、统计函数的执行时间、动态改变函数参数、插件式的表单验证等代码。
以上所述是小编给大家介绍的Javascript面向切面编程详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!
RTX 5090要首发 性能要翻倍!三星展示GDDR7显存
三星在GTC上展示了专为下一代游戏GPU设计的GDDR7内存。
首次推出的GDDR7内存模块密度为16GB,每个模块容量为2GB。其速度预设为32 Gbps(PAM3),但也可以降至28 Gbps,以提高产量和初始阶段的整体性能和成本效益。
据三星表示,GDDR7内存的能效将提高20%,同时工作电压仅为1.1V,低于标准的1.2V。通过采用更新的封装材料和优化的电路设计,使得在高速运行时的发热量降低,GDDR7的热阻比GDDR6降低了70%。
更新动态
- 雨林唱片《赏》新曲+精选集SACD版[ISO][2.3G]
- 罗大佑与OK男女合唱团.1995-再会吧!素兰【音乐工厂】【WAV+CUE】
- 草蜢.1993-宝贝对不起(国)【宝丽金】【WAV+CUE】
- 杨培安.2009-抒·情(EP)【擎天娱乐】【WAV+CUE】
- 周慧敏《EndlessDream》[WAV+CUE]
- 彭芳《纯色角3》2007[WAV+CUE]
- 江志丰2008-今生为你[豪记][WAV+CUE]
- 罗大佑1994《恋曲2000》音乐工厂[WAV+CUE][1G]
- 群星《一首歌一个故事》赵英俊某些作品重唱企划[FLAC分轨][1G]
- 群星《网易云英文歌曲播放量TOP100》[MP3][1G]
- 方大同.2024-梦想家TheDreamer【赋音乐】【FLAC分轨】
- 李慧珍.2007-爱死了【华谊兄弟】【WAV+CUE】
- 王大文.2019-国际太空站【环球】【FLAC分轨】
- 群星《2022超好听的十倍音质网络歌曲(163)》U盘音乐[WAV分轨][1.1G]
- 童丽《啼笑姻缘》头版限量编号24K金碟[低速原抓WAV+CUE][1.1G]