学学AOP之装潢者形式

装潢者,英文名叫decorator. 所谓的”装潢”,从字面能够很轻易的理解出,就是给 土肥圆,化个妆,华美的转身为白富美,但本体照样土肥圆。

说人话.
咳咳~

在js内里一切都是对象,而且函数就是一等对象。 在一般的Object中,我们能够很轻易的增添属性或许其他要领,固然函数也不破例。 然则,如许做的结果就是,我们会不停的转变本体,就像把凤姐送去做整形手术一样。 固然,结果有好有坏,或许凤姐就是下一个angelababy,或许… 所以,为了我们代码的贞洁度(就算你是丑小鸭), 我们能够不动刀子的情况下,让你变得又白又美。

援用装潢

这个是装潢的初级阶段,就是抹点粉罢了。 在js中,我们叫做援用装潢。

talk is cheap, show u code

//我们给jimmy函数分外增添其他的功用
var jimmy = function(){
    console.log("jimmy");
}
var _jimmy = jimmy;
jimmy = function(){
    _jimmy();
    console.log("I love HuaHua");
}
jimmy();

这个的运用场景重要就是在多人协作和框架设想内里。比方,李冰岩已运用了onload函数,然则,小舟又想运用onload函数。 如许会形成一个题目,假如小舟直接修改的话,他须要看的是李冰岩写的蜜汁代码,而且还要防备不会引发毛病。这无疑是很难题的,所以在这里,能够运用援用装潢,来给onload在增添一层。

//这是小李的蜜汁代码
var xiaoLi = function(){
    console.log("蜜汁代码");
}
window.onload = xiaoLi;  //小李已绑定好onload函数了
//接下来小舟须要修改onload代码
var fn = window.onload;
var xiaoZhou = function(){
    fn();
    conosle.log("整齐代码");
}
window.onload = function(){  //依据onload的特征,直接覆蓋掉小李的code
    xiaoZhou();
}

所以援用装潢的用途照样蛮大的。
大你妹啊~~
啊。。。。
(另一Me) 我们来剖析一下,上面谁人援用情势有什么弊病(优点已都晓得了).
起首,我们运用援用情势的时刻,必定会增添一个过剩的援用对象,比方上文的”fn”.
而且跟着你顺序链的增添,中心对象一定会和你节点一致数量的。固然,起名我就不说了,症结是,一大堆无用的代码放在那边,觉得很不爽的。 所以,这里引入AOP的装潢情势.

AOP装潢

亲热,熟习,圆满。 我们见过AOP应当不止一次了,在职责链情势运用过,在迭代器情势运用过等等。运用这么屡次,彷佛还没有对AOP做一些基础诠释呢?所以,这里给人人咻咻.
AOP中文名叫面向切面编程。 先说一下这个名词,“面向”这词应当不必诠释,症结”切面”是什么鬼。 假如人人做过sangwich,应当就晓得,起首我们买来一块面包,须要将面包切开,然后在切面上面加上一些flavoring,比方蔬菜,火腿,培根之类的。 恩,对照js顺序来讲,一个顺序链就相当于你买回来的面包,flavoring就是你想加的功用函数,如何将函数准确的安排在顺序链中适宜的位置,这就是AOP做的事变。
起首,再次将两个动态函数咻咻:

Function.prototype.after = function(fn){
    var _this = this;
    return function(){
        var res = _this.apply(this,arguments);
        fn.apply(this,arguments);
        return res;
    }
}
Function.prototype.before = function(fn){
    var _this = this;
    return function(){
        fn.apply(this,arguments);  
        return    _this.apply(this,arguments);
    }
}

这两个函数的组合构成了js中AOP情势的英华.而AOP最常常使用的就是讲与营业逻辑无关的功用动态织入到主顺序中。

talk is cheap , show u code

举个栗子吧: 运用AOP盘算顺序运转事宜

//纯手写盘算函数运转事宜
function factorial(n) {  //最基础的阶乘盘算
  if (n === 1) return 1;
  return n * factorial(n - 1);
}
function calTime(n){
    var start = new Date().getMilliseconds();
    factorial(n);
    console.log(new Date().getMilliseconds() - start+"ms");
}
calTime(1000);

能够得出消耗的时候,然则,假如另有其他的函数须要测试,那末这么做的意义并没有很大的代价。我们运用AOP举行重构。

function factorial(n) {  //最基础的阶乘盘算
  if (n === 1) return 1;
  return n * factorial(n - 1);
}
var calTime = (function(){
    var start;
    return    function(){
        if(!start){  //给最先时候赋值
            start = new Date().getMilliseconds();
        }else{
            console.log(new Date().getMilliseconds()-start+"ms");
            start = undefined;
        }
    }
})();
var calcu = factorial.before(calTime).after(calTime)(200);

如许很好的将计时功用从营业逻辑中提取出来,而且看着真的很有angelababy的滋味诶.
运用AOP的时刻须要注重一点就是,before&after实行完后,返回的结果都是第一个函数的内容。

var result = function(){
    return 1;
}.before(function(){
    return 2;
}).after(function(){
    return 3;
});
console.log(result());  //1

我们大抵的了解了AOP的用法和理论,能够针关于开首所说的例子举行重构.

window.onload = function(){
    console.log("小李的蜜汁代码");
}
var fn = window.onload;
fn.before(function(){
    console.log("整齐代码");
});
window.onload = fn;

看起来,比上面谁人栗子清楚许多,而且运用before和after也非常利于代码的浏览。

实例解说AOP装潢

上面谁人例子,只能算是AOP装潢情势的一个不起眼的角落。 AOP援用的场景在js中,或许说在任何一门言语中都是大放色泽的。 在js中,”细粒度”对象是顺序中复用性最高的对象,能把对象用细粒度的情势示意,那末AOP无疑是最好的挑选。
在写营业逻辑的时刻,我们最大的题目就是推断逻辑,运用大批的if语句,而这都能够经由思索奇妙化解。比方,我在写购置课程的时刻就会碰到一些逻辑。 只有当课程数量符合请求的时刻,购置的结果才有用.
按一般的营业逻辑编写

var buy = function(){
    if(confirm()){  //考证购置信息是不是正当
        http.buyCourse('xxx');  //提议请求
    }
}
var confirm = function(){
    console.log("考证购置数量");
}
document.querySelector(".buy").addEventListener("click",function(){
    buy();
},false);

运用AOP装潢情势重构后

var buy = function(){
    http.buyCourse("xxx"); //给背景提议请求,考证
}
var confirm = function(){
    console.log("考证购置数量");
}
var buy = buy.before(confirm);
document.querySelector(".buy").addEventListener("click",function(){
    buy();
},false);

美美代码的 满满即视感!!!
不够,老板,再来份糖炒栗子~
好嘞~
这里我们只是猎取函数的结果,那我们想直接干涉干与通报的参数,能够吗?
固然能够。
我们研究一下,before的内部组织(after是一样的)

Function.prototype.before = function(fn){
    var _this = this;
    return function(){
        fn.apply(this,arguments);  
        return    _this.apply(this,arguments);
    }
}

这里,因为arguments是援用范例,假如fn转变了arguments,则会反应到_this.apply的arguments也会发作转变。 而这个运用场景就是,给ajax的地点增添上你须要的参数。
在现实项目中,一最先的接口,就是一个普一般通的地点,发请求,然后猎取参数。

http.ajax(url,type).then(...)

想如许的运用,然则某天,你的leader提高了请求品级,将地点背面都加上一个token参数,或许说一个口令的md5或sha1的盘算值,我想,这尼玛工作量应当不小。
固然,我们能够直接将url举行通报。

var http = {
    ajax1(url){
        url += param.getToken();
        sendAjax(url);
    },
    ajax2(url){
        ...
    }
    ...
}

而且,万一哪天你的leader说,哎,如许做安全性照样不太高,要不加两个token殽杂一下。
啊~啊~啊~啊~(殽杂你妹啊,过不过年啦)
假如你继承这么写,我置信,年终奖是有的,然则,春运火车票你预计是摸不着了。
所以能够运用AOP举行动态织入。要晓得,参数,我AOP也是能够动的。

function dealUrl(url){
    url+=param.getToken();
}
http.ajax = http.ajax.before(dealUrl);
http.ajax("www.example.com");   //此时的Url = www.example.com?token=23jkfd3kjfdksjfkjds

而且,这个处置惩罚url函数,我也是能够扔到恣意一个js文件内里复用的耶.
棒!!!
我AOP能够动你要的参数,而且,我还能够把我的结果给你是咻咻,假如我不让你实行,你永久也不会实行,哈哈哈哈~~~~
对不起,,上面那段是我意淫AOP说的。。。 实在AOP能够算是全能的设置东西,比方表单考证吧。 我们常常会把表单考证和表单结果发送耦合在一起。
像如许

var sendRes = function(){
    if(user.userName === ""){
        alert("用户名不能为空~");
        return;
    }else if(user.password === ""){
        alert("暗码不能为空~");
        return;
    }
    http.sendUser("xxx");  //考证胜利发送用户信息
}

一个函数内里同时含有了两个职责,一个考证一个发送用户信息。 所以我们如今的重要目标就是解耦。
追念一下,之前表单考证我们运用战略情势,解耦了考证,这里我们再次运用。

var sendRes = function(){
    var detect = new Detect();  //战略者情势
    detect.add(user.userName,["notEmpty"]);
    detect.add(user.password,["notEmpty"]);
    var notPass = detect.getResult();
    if(notPass){  //假如没经由过程
        console.log(notPass);
        return;
    }
    http.sendUser("xxx"); //考证胜利发送用户信息
}

能够运用上面谁人考证,然则结果是,考证和战略情势照样在一起。我们再运用AOP举行解耦。起首我们得重构一下before函数

Function.prototype.before = function(fn){
    var _this = this;
    return function(){
        var res = fn.apply(this,arguments);  //值为Boolean,示意是不是继承向下通报
        if(res==="next"){  //假如返回不成立,则继承向下通报
            return    _this.apply(this,arguments);
        }
        return res;
    }
}

看到这里,有些同砚应当邃晓是怎样一回事了。没错,就是依据before内里考证的结果推断是不是实行下个发送请求的功用函数。
固然,假如不想污染原型,你也能够自定义一个函数。

var before = function(beforeFn,fn){
    return function(){
        var res = beforeFn.apply(this,arguments);
        if(res==="next"){
            return fn.apply(this,arguments);
        }
    }
}

如许写也是能够的。
我们先按原型体式格局写,如许直观一点

var sendRes = function(){
    http.sendUser("xxx"); //考证胜利发送用户信息
}
sendRes = sendRes.before(function(){
    var detect = new Detect();  //战略者情势
    detect.add(user.userName,["notEmpty"]);
    detect.add(user.password,["notEmpty"]);
    var notPass = detect.getResult();
    if(notPass){  //假如没经由过程
        console.log(notPass);
    }
    return "next";
});

能够看出,考证那部份已完整和发送用户信息的功用函数完整给解耦了。 如许不仅提高了函数的重用性,而且也让你的代码朝着“细粒度”方向大步行进.

辩证装潢者情势

实在,装潢者情势和职责链情势的情势是完整一样的,所以,他们的弊病也是相似的。链造的太长,关于机能来讲就是一次rape.所以,照样那句话,不要为了情势而情势,没有全能的情势。

    原文作者:villainhr
    原文地址: https://segmentfault.com/a/1190000004351323
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞