[转]用AOP改善javascript代码
有时候,不光要低头写代码,也要学着站在更高的角度,来思考代码怎么写,下面这篇文章,讲的关于代码设计的问题,脑洞大开。
原文: http://www.alloyteam.com/2013/08/yong-aop-gai-shan-javascript-dai-ma/
自己的改进版本: 因为觉得原文作者的这种实现方式,在Function.prototype 上面实现的方式,有点不妥。所以,结合我上面一篇的思考,我做出了如下的修改,
function after(old, callback){ return function(){
var ret = old.apply(this, arguments);
if(ret === false){
return false;
}
callback.apply(this, arguments);
return ret;
}
}
function say(name){
console.log('say ' + name);
return 1;
}
b = after(say, function(name){
console.log('after ' + name);
return 2;
})
实现了一个 after(old, callback)方法, 这个工具方法用来实现 在这些了old()方法体之后,插入我们需要插入的逻辑,(切面) 。但是这种方法也不是很完美,不是很“直观”
类似的,可以实现 before 切面。
运行结果:
------------------------------------------------------------------------------------------
Aop又叫面向切面编程,用过spring的同学肯定对它非常熟悉,而在js中,AOP是一个被严重忽视的技术点,这篇就通过下面这几个小例子,来说说AOP在js中的妙用.
1, 防止window.onload被二次覆盖.
2,无侵入的统计代码.
3, 分离表单请求和校验.
4,给ajax请求动态添加参数.
5,职责链模式.
6, 组合代替继承.
先给出before和after这2个“切面”函数. 顾名思义,就是让一个函数在另一个函数之前或者之后执行,巧妙的是,before或者after都可以和当前的函数公用this和arguments, 这样一来供我们发挥的地方就多着了.
处理window.onload被二次覆盖.
前段时间看到QQ群里有个人问问题,要改写window.onload, 怎么才能不把以前的window.onload函数覆盖掉.
最原始的方案肯定是直接在原来的window.onload里添上你的新代码.
这样的坏处非常明显,需要去改动原有的函数, 是侵入性最强的一种做法.
另外一种稍微好点的方案是用中间变量保存以前的window.onload;
这样一来,多了一个讨厌的中间变量__onload, 来管理它也要花费一些额外的成本.
试想一下这个场景,当人觉得天气冷,出门的时候很自然选择穿上一件貂皮大衣,而不是把自己的皮扯掉换成貂皮. 动态装饰的好处就体现出来了,完全不会侵入之前的函数.
无侵入的统计代码
本身跟逻辑没有任何关联的统计代码要被硬插进函数里, 这点相信很多搞过上报的同学都很不爽. 比如下面这段代码, 用来统计一个创建1000个节点的函数在用户的电脑上要花费多少时间.
用aop的方式,不再需要在函数内部做改动,先定义一个通用的包装器.
只要一行代码,便能给任何函数都加上统计时间的功能.
分离表单请求和校验
我们在提交表单之前经常会做一些校验工作,来确定表单是不是应该正常提交. 最糟糕的写法是把验证的逻辑都放在send函数里面.
而更好的方式是把所有的校验规则用策略模式放到一个集合里,返回false或者true来决定是否通过验证. 这样可以随意的选择和更换校验规则.
这样还有一个缺点,校验和发送请求这2个请求耦合到了一个函数里面, 我们用aop来把它们分离开来, 把validata做成插件化,真正的即插即用. 只需把send函数改成:
过最前面Function.prototype.before的代码不难看出,我们约定,当前一个函数返回false, 就会阻断下一个函数的执行, 所以当validata返回false的时候, 便不再继续执行send. 而因为之前提到的before函数可以和当前函数公用this和arguments, 所以value参数也能顺利的传递到validata函数里.
给ajax请求动态添加参数
第一个例子里window.onload是用的after后置装饰, 这里是用before前置装饰. 在ajax请求之前动态添加一些参数.
我们遇到过很多跨域的请求, jsonp和iframe都是很常用的方式. 之前在我们的项目里,用参数retype=jsonp表示是jsonp请求, retype=iframe表示是iframe请求. 除此之外这2个请求的参数没有任何区别. 那么可以用before把retype参数动态装饰进去.
先定义一个ajax请求的代理函数.
这个函数里面没有逻辑处理和分支语句,它也不关心自己是jsonp请求还是iframe请求. 它只负责发送数据, 是一个单一职责的好函数.
接下来在发送请求前放置一个before装饰器.
开始发送请求:
职责链模式.
职责链模式在js中典型的应用场景是事件冒泡. 将所有子节点和父节点连成一条链,并沿着这条链传递事件,直到有一个节点能够处理它为止. 职责链模式是消除过多的if else语句的神器.
拿最近做的一个需求来举例, 有个文件上传的功能, 提供了控件,html5, flash, 表单上传这4种上传方式. 根据它们的优先级以及浏览器支持情况来判断当前选择哪种上传方式. 在我进行改造之前,它的伪代码大概是这样:
当然实际的代码远不只这么多,其中还包括了各种控件初始化,容错等情况。有天我需要屏蔽掉flash,看起来是很简单的需求,但难度实际跟在心脏旁边拆掉一根毛线血管类似.
如果试试职责链模式呢, 看看事情将变得多简单:
第一步先改写之前的after函数,使得返回一个对象时阻断职责链的传递,而返回null时继续传递请求。
接下来把每种控件的创建方式都包裹在各自的函数中, 确保没有逻辑交叉和相互污染.
最后用职责链把它们串起来:
可以预见,某天我又需要屏蔽掉flash, 那时的我只需要改动这一行代码. 改成:
组合代替继承
很多时候我们在设计程序的时候,会遇到使用组合还是继承的问题. 通常来讲, 使用组合更灵活轻巧. 还是拿之前文件上传来举例.
我定义了一个超类Upload, 衍生出4个子类.
Plugin_Upload, Html5_Upload, Flash_Upload以及Form_Upload.
Plugin_Upload会继承父类,得到Upload的大部分功能, 然后对控件上传的一些特性进行个性定制. 比如其它3种上传方式都是选择文件后便开始上传. 而控件上传在开始上传之前会经过一轮文件扫描.
第一种做法是Plugin_Upload继承Upload, 然后重写它的start_upload方法.
用更轻的组合方式, 可以直接给原来的start_upload函数装饰上扫描功能, 甚至不需要衍生一个额外的子类.
[转]用AOP改善javascript代码的更多相关文章
- 用AOP改善javascript代码
Aop又叫面向切面编程,用过spring的同学肯定对它非常熟悉,而在js中,AOP是一个被严重忽视的技术点,这篇就通过下面这几个小例子,来说说AOP在js中的妙用. 1, 防止window.onloa ...
- (第一章)改善JavaScript,编写高质量代码。
根据<编写高质量代码改善JavaScript程序的188个建议>这本书,来记录我目前所了解的建议方式. 建议1:警惕Unicode乱码 根据ECMA标准规定JavaScript语言可以使用 ...
- 读《编写高质量代码:改善JavaScript程序的188个建议》1
建议3:减少全局变量污染 定义全局变量有3种方式: ❑在任何函数外面直接执行var语句. var f='value'; ❑直接添加一个属性到全局对象上.全局对象是所有全局变量的容器.在Web浏览器中, ...
- 大量javascript代码的项目如何改善可维护性
项目中有点javascript文件,javascript代码行数达到7000多行,维护很费力,主要体现在以下几个方面: 1,方法没有注释,没有注释方法的作用,从上到下罗列,很难知道这个方法应该啥时候调 ...
- 深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点
深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点 2011-12-28 23:00 by 汤姆大叔, 139489 阅读, 119 评论, 收藏, 编辑 才华横溢的 ...
- 高质量JavaScript代码书写基本要点
翻译-高质量JavaScript代码书写基本要点 by zhangxinxu from http://www.zhangxinxu.com本文地址:http://www.zhangxinxu.com/ ...
- 以优美方式编写JavaScript代码
英文原文:CoffeeScript: The beautiful way to write JavaScript 我用 JavaScript 编程很多年了,写了大量的 JavaScript 代码,即便 ...
- [转] 翻译-高质量JavaScript代码书写基本要点 ---张鑫旭
by zhangxinxu from http://www.zhangxinxu.com本文地址:http://www.zhangxinxu.com/wordpress/?p=1173 原文作者:St ...
- 高质量JavaScript代码书写基本要点学习
高质量JavaScript代码书写基本要点学习 可维护的代码意味着: •可读的 •一致的 •可预测的 •看上去就像是同一个人写的 •已记录 最小全局变量(Minimizing Globals) ...
随机推荐
- Linux-RedHat7.2 安装.net core2.0
1.添加dotnet产品Feed sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc sudo sh -c 'ech ...
- iview 的 Carousel 走马灯 焦点图 不能用 建议换/vue-awesome-swiper
https://www.npmjs.com/package/vue-awesome-swiper
- python基础一 day2
内容: 3%%s 输出:3%s 后面的全部转义 结果: 如果是因为执行break语句导致循环提前结束,就不会执行else. 单位换算: 编码方式: ascii unicode u ...
- java分段加载数据,循环和递归两种方式
package org.jimmy.autosearch2019.test; import java.util.ArrayList; public class Test20190328 { priva ...
- 字符数组函数,连接strcat 复制函数strcpy 比较函数strcmp 长度函数 strlen
之前我们学习数据类型的时候,有一个类型 char ,这个类型允许我们在里边放一个字符 char variable1='o'; char variable2='k'; #include <iost ...
- ie8兼容性
ie8下不支持css的nth-child()样式解决方法一:使用jQuery的nth-child()方法例:$(".ability-head-list ul li:nth-child(1) ...
- 01Ping程序的设计
1.Ping程序设计具体设计任务 1.1 实验目的 PING程序是我们使用的比较多的用于测试网络连通性的程序.PING程序基于ICMP,使用ICMP的回送请求和回送应答来工作.由计算机网络课程知道,I ...
- 洛谷 P1708 天然气井 题解
https://www.luogu.org/problemnew/show/P1708 这道题还是比较好的. 读完题目我们先想想如何计算某个天然气井($x_1,y_1$)和中转站($a_1,b_1$) ...
- 天梯赛L1 题解
L1-001 Hello World (5 分) 这道超级简单的题目没有任何输入. 你只需要在一行中输出著名短句“Hello World!”就可以了. AC代码:(直接输出记性) #include & ...
- PXE自动化安装系统
准备(以centos7为例) ①关闭防火墙 ②关闭selinux ③dhcp服务设置为静态IP ④安装软件包 http:充当yum源安装包仓库 tftp-server :在它的工作目录存在引导主机的工 ...