找回密码
 会员注册
查看: 26|回复: 0

前端装饰器模式快闪

[复制链接]

5

主题

0

回帖

16

积分

新手上路

积分
16
发表于 2024-9-19 20:38:42 | 显示全部楼层 |阅读模式
前端装饰器模式快闪「福利」✿✿ヽ(°▽°)ノ✿:文章最后有抽奖,转转纪念T恤一件或转转随机手办一个,走过路过不要错过哦狸猫换太子北宋真宗时,有李妃和刘妃两位小主都有了龙种,谁生了儿子,谁就能豪横。谁曾想刘妃不是个善茬,把剥了皮的狸猫换了李妃刚生完的孩子。天可怜见,执行任务的宫女人性未泯,将孩子送往八贤王处抚养,这才引出后来,包拯陈州放粮救李妃,仁宗认母家团圆。那位说了,好好的谈装饰器模式这怎么说上故事了。各位看官不要急,这前端装饰器模式的实现核心其实就在这五个字上,天空飘来五个字,那都不是事,串了串了……核心其实就在这五个字上——狸猫换太子。装饰器模式咱们闲言少叙,言归正传。装饰器模式是设计模式的一种,是为已有功能动态的添加更多功能的一种方式。重点体现了设计模式六大原则之中的单一职责原则和开闭原则。单一职责很好理解,就是专心,就是一个函数只做一件事情。开闭原则是指要对扩展开放,对修改关闭。说人话,就是在原有功能不变的前提下要加功能、加需求,不是去动写好的函数,而是想办法扩展。这个想出来的办法就是装饰器模式。最简单的实现现在我们先来个最简单的实现。假设有如下一个场景,定义一个启动函数,执行就会打印"start"。let start = function () {    console.log('start')}start();好了,现在产品来了,他说启动之前,需要加入一段自检功能。最快的方法当然就是在start函数里添加一段检查代码。但是这就违反了设计模式的单一职责原则和开闭原则。这时候就是装饰器模式大展身手的好时候了。let start = function () {    console.log('start')}const _start = start;start = function(){    console.log('check');    _start()}start();对于原函数,我们没有做任何的修改,因此不必担心原有功能受到影响。首先将原函数对象地址赋值给新的变量,然后重写原函数变量。重写的函数首先加入自检功能,然后执行上面备份到新变量的原函数。这样就实现了一个最简单的装饰器模式。你品,你细品,有没有狸猫换太子的味道。装饰器模式与代理模式我想到了这里,你应该对于装饰器模式有所掌握了。所以我们趁热打铁,来认识一下装饰器模式的兄弟代理模式。代理模式也是经典设计模式的一种,和装饰器模式的共同点是都不改变原有功能。代理模式决定了能不能访问到原有功能,是一种控制。装饰器模式决定了你能访问到的是加了其他哪些功能的原有功能。如果非要类比的话,那么代理有点像防火墙,装饰器有点像你买的快递的各种快递外包装。在ES6中提供了一个新的标准内置对象Proxy,就是代理模式的实现。你可能已经知道了在Vue3.0中将会通过Proxy来替换原本的Object.defineProperty来实现数据响应式。语法let p = new roxy(target, handler);target是用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。handler是一个对象,其属性是当执行一个操作时定义代理的行为的函数。Proxy 支持13种拦截操作,几乎涵盖了对象函数操作的方方面面,完全够开一个新的专题,所以这里就先不做扩展。装饰器模式与面向切面编程面向切面编程和面向对象编程一样,是一种编程的思想,是对面向对象编程的一种补充。在后端开发使用的Java框架spring的特性中有一项特性就是AOP,面向切面编程。所以到底什么是切面,如果是后端开发理解,还需要引入好多其他术语概念,但是我们前端有koa啊,有洋葱模型呀。洋葱模型就是一个切面。我的函数方法就是洋葱的核心,我想在方法调用前后做一些事情,比如打日志、算时间等等,这时候就将我们的函数方法包裹起来,再加一层,这就是面向切面编程。思考的是程序执行的位置。面向对象编程可以让我们方便抽象和管理我们的代码,而面向切面编程的重点在于解耦和复用。而装饰器就是实现面向切面编程的一种方式。源码级别的实现function.before和function.after在webpack和babel还没有大行其道之前,比较经典的AOP实现方式就是function.before和function.after,同样我们也可以用他们来实现装饰器模式。Function.prototype.before = function(fn){    var _this = this;       // 用来保存调用这个函数的引用,如func_1调用此函数,则_this指向func_1    return function(){      // 返回一个函数,这个函数包含原函数和新函数,原函数指的是func_1,新函数指的是fn        fn.apply(this,arguments);   // 执行新函数        return _this.apply(this,arguments);     // 执行原函数    }}Function.prototype.after = function(fn){    var _this = this;    return function(){        var r = _this.apply(this,arguments); // 先执行原函数,也就是func_1        fn.apply(this,arguments);   // 再执行新函数        return r;    }}var func_1 = function () {   console.log("2")}func_1 = func_1.before(function () {   console.log("1");}).after(function () {   console.log("3");} )func_1();   // 输出1、2、3Object.defineProperty()后来,我们有了webpack和babel。Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性。/*** param obj 当前操作对象* param prop 需要定义的对象属性名称* param desc 属性描述符*/Object.defineProperty(obj, prop, desc)属性描述符包括数据描述符:value,writable;存取描述符:get,set;描述符:configurable,enumerable;这个方法可不得了,存取描述符get,set是vue框架的基础,而数据描述符就是Babel实现装饰器的基础。Babel将我们的@函数名最终转换为Object["define" + "Property"](target, property, desc);由此可见,装饰方法本质上还是使用Object.defineProperty()来实现的。function before(target, key, descriptor) {  const fn = descriptor.value;  return {    ...descriptor,    value() {      console.log('before')      return fn.apply(this, arguments);    }  }}function after(target, key, descriptor) {  const fn = descriptor.value;  return {    ...descriptor,    value() {      let result = fn.apply(this, arguments);      console.log('after');      return result;    }  }}class Test {  @after  @before  func(){    console.log('func')  }}const test = new Test();test.func();经过Babel编译后运行,可以输出:beforefuncafter工程化的实现此处以webpack打包项目为例,如果在一个webpack打包项目中你还没有使用装饰器,那么你的项目在开发效率和代码重构上就还有提升空间。如果你使用babel6,安装并配置babel插件babel-plugin-transform-decorators-legacy,如果使用babel7,安装并配置官方插件@babel/plugin-proposal-decorators。完成配置后,可以将项目中无关业务的功能尝试使用装饰器的方式实现,如登陆条件判断,防抖,节流,埋点统计等。优秀装饰器的样子core-decorators是一个装饰器第三方模块,提供了一些常见的装饰器,通过学习它,我们可以更好地理解装饰器。它的github地址是https://github.com/jayphelps/core-decorators,目前已经有4.2K的赞。文末福利
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-26 23:55 , Processed in 0.784011 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表