JavaScript装潢者形式

本文是《JavaScript设想形式与开辟实践》的进修笔记,例子来源于书中,关于设想形式的意见,引荐看看本书作者的发起

什么是装潢者形式?

给对象动态增添职责的体式格局成为装潢者形式。

装潢者形式能够在不转变对象本身的基础上,在运行递次时期给对象动态地增加职责。这是一种轻巧天真的做法,装潢者是一种“即付即用”的体式格局,比方天冷了就多穿一件外衣。

装潢函数

想要为函数增加一些功用,最简朴粗犷的体式格局就是直接改写该函数,然则这是最差的要领,直接违反了开放——关闭准绳。

var a = function(){
    alert(1)
}
// 改成
var a = function(){
    alert(1)
    alert(2)
}

许多时刻我们不想碰原函数,或许原函数是其他同事编写的,甚至在一个陈旧的项目中,这个函数的源代码被隐藏在一个我们不肯触碰的阴晦角落里。如今须要不转变源代码的状况下,给函数增添功用。

我们经由过程保留原援用的体式格局改写某个函数。

var a = function(){
    alert(1)
}
var _a = a
a = function(){
    _a()
    alert(2)
}
a()

这是现实开辟中很罕见的一个做法,比方我们想给 window 绑定 onload 事宜,然则又不肯定这个事宜是否是已被其他人绑定过,为了防止覆蓋掉之前的 window.onload 函数中的行动,先保留 window.onload,把它放入新的 window.onload。

window.onload = function(){
    alert(1)
}

var _onload = window.onload || function(){}

window.onload = funtion(){
    _onload()
    alert(2)
}

如许的代码是相符关闭——开放准绳,我们在增添新功用的时刻确切没有修正本来的代码,然则这类体式格局存在两个题目:

  1. 必需保护 _onload 这个中心变量,虽然看起来不起眼,然则假如函数装潢链较长,或许须要装潢的函数变多,这些中心变量的数目也会越来越多。
  2. 实在还遇到了 this 被挟制的题目,在 window.onload 的例子中没有这个懊恼,由于挪用 _onload 的时刻 this 也指向 window,跟挪用 window.onload 的时刻一样。

用 AOP 装潢函数

AOP(Aspect Oriented Programming)面向切面编程的重要作用是:把一些跟中心营业逻辑无关的功用抽离出来,这些跟营业逻辑无关的功用一般包括日记统计、平安掌握、非常处置惩罚等。把这些功用抽离出来今后,再经由过程“动态织入”的体式格局掺入营业逻辑模块中。如许的优点起首是能够坚持营业逻辑模块的纯洁和高内聚性,其次是能够很方便地复用日记统计等功用模块。

起首给出 Function.prototype.before 和 Function.prototype.after 要领:

Function.prototype.before = function(beforefn){
    // 保留原函数的援用
    var _self = this
    // 返回包括原函数和新函数的“代办函数”
    return function(){
        // 实行新函数,保证this不被挟制,
        // 新函数接收的参数也会被一成不变地传入原函数,
        // 新函数在原函数之前实行
        beforefn.apply(this,arguments)
        return _self.apply(this.arguments)
    }
}

Function.prototype.after = function(afterfn){
    var _self = this
    return function(){
        var ret = _self.apply(this,arguments)
        afterfn.apply(this,arguments)
        return ret
    }
}

“代办函数”只是构造上像代办罢了,并不负担代办的职责(比方掌握对象的接见),它的事情是把要求离别转发给新增加的函数和原函数,且担任保证它们的实行递次。

再回到 window.onload 的例子中,用 Function.prototype.after 来增添新事宜:

window.onload = function(){
    alert(1)
}

window.onload = (window.onload || function(){}).after(function(){
    alert(2)
}).after(function(){
    alert(3)
})

AOP 的运用实例

(1)数据统计上报

<html>
    <button tag="login" id="button">点击翻开登录浮层</button>
    <script>
        var showLogin = function(){
        console.log( '翻开登录浮层' )
        log( this.getAttribute( 'tag' ) )
    }
        var log = function( tag ){
            console.log( '上报标签为: ' + tag )
    }
    document.getElementById( 'button' ).onclick = showLogin
    </script>
</html>

showLogin 函数既要担任翻开浮层,又要担任数据上报,两个功用耦合在一个函数里,运用 AOP 星散:

<html>
<button tag="login" id="button">点击翻开登录浮层</button>
<script>
    Function.prototype.after = function( afterfn ){
        var __self = this;
        return function(){
        var ret = __self.apply( this, arguments )
        afterfn.apply( this, arguments )
        return ret
        }
    }
    var showLogin = function(){
        console.log( '翻开登录浮层' )
    }
    var log = function(){
        console.log( '上报标签为: ' + this.getAttribute( 'tag' ) )
    }

    showLogin = showLogin.after( log ); // 翻开登录浮层以后上报数据
    document.getElementById( 'button' ).onclick = showLogin;
</script>
</html>

(2) 插件式表单考证

<html>
<body>
    用户名:<input id="username" type="text"/>
    暗码: <input id="password" type="password"/>
    <input id="submitBtn" type="button" value="提交"></button>
</body>
<script>
    var username = document.getElementById( 'username' ),
    password = document.getElementById( 'password' ),
    submitBtn = document.getElementById( 'submitBtn' );
    var formSubmit = function(){
        if ( username.value === '' ){
            return alert ( '用户名不能为空' );
        }
        if ( password.value === '' ){
            return alert ( '暗码不能为空' );
        }
        var param = {
            username: username.value,
            password: password.value
        }
        ajax( 'http:// xxx.com/login', param ); // ajax 详细完成略
    }

    submitBtn.onclick = function(){
        formSubmit();
    }
</script>
</html>

formatSubmit 函数负担了两个职责,除了提交ajax要求,还要考证用户输入的合法性。我们把校验输入的逻辑放到validata函数中,并约定当validata函数返回false的时刻示意校验未经由过程。

var validata = function(){
    if ( username.value === '' ){
        alert ( '用户名不能为空' );
        return false;
    }
    if ( password.value === '' ){
        alert ( '暗码不能为空' );
        return false;
    }
}

var formSubmit = function(){
    if ( validata() === false ){ // 校验未经由过程
    return;
    }
    var param = {
        username: username.value,
        password: password.value
    }
    ajax( 'http:// xxx.com/login', param );
}

submitBtn.onclick = function(){
    formSubmit();
}

运用AOP优化代码

Function.prototype.before = function( beforefn ){
    var __self = this;
    return function(){
    if ( beforefn.apply( this, arguments ) === false ){
    // beforefn 返回false 的状况直接return,不再实行背面的原函数
        return;
    }
    return __self.apply( this, arguments );
    }
}

var validata = function(){
    if ( username.value === '' ){
        alert ( '用户名不能为空' );
        return false;
    }
    if ( password.value === '' ){
        alert ( '暗码不能为空' );
        return false;
    }
}
var formSubmit = function(){
    var param = {
        username: username.value,
        password: password.value
    }
    ajax( 'http:// xxx.com/login', param );
}

formSubmit = formSubmit.before( validata );
    submitBtn.onclick = function(){
        formSubmit();
}
    原文作者:白芷
    原文地址: https://segmentfault.com/a/1190000018059243
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞