5分でマスターできるJavaScript装飾者モードとAOP


デコレーションモードとは?
私たちが写真を撮ったら、友達の輪を送るつもりです.たくさんの仲間が写真にフィルターをかけることを選びます.同じ写真、異なるフィルターを組み合わせると違った体験ができます.ここでは実際に装飾者モードを適用しました.フィルターで写真を飾りました.オブジェクト(写真)を変更せずに、動的に機能を追加します.
注意したいのは、JavaScriptの言語動態の特性によって、ある対象を簡単に変えることができます.しかし、私たちはできるだけ直接関数を書き換えないようにします.コードの維持性、拡張性が悪くなり、他の業務も汚染されます.
何がAOPですか
食事の前には手を洗い、食後には口をすすぐことも珍しくないと思います.このスローガンは実はAOPの生活の中の例です.食事という動作は接点に相当します.私達はこの接点の前に他の手を洗うような動作を挿入することができます.
AOP(Asppect-Oriented Prograamming):面に向かってプログラミングするのは、OOPに対する補完です.AOPを利用して、業務ロジックの各部分を隔離してもいいし、業務に関係ない機能を隔離して、例えばログオン、異常処理などをしてもいいです.それによって、業務ロジックの各部分間の結合度が低くなり、業務に関係ない機能の多重性が向上し、オープンの効率が向上します.
JavaScriptでは、装飾者モードによってAOPを実現できますが、両者は次元の概念ではありません.AOPはプログラミングのモデルで、装飾者は設計モードです.
ES 3下装飾者の実現
装飾者モードとAOPの概念を理解した後、ES 3に対応できるコードを書いて装飾者モードを実現します.
//    
var takePhoto =function(){
    console.log('   ');
}
//    aop   
var after=function( fn, afterfn ){ 
    return function(){
        let res = fn.apply( this, arguments ); 
        afterfn.apply( this, arguments );
        return res;
    }
}
//     
var addFilter=function(){
    console.log('   ');
}
//           
takePhoto=after(takePhoto,addFilter);

takePhoto();
これにより、写真とフィルタの抜け方を実現しました.これからは自動的に機能をアップロードする必要があれば、aop関数afterによって追加することもできます.
ES 5下装飾者の実現
ES 5に導入されました.Object.definePropertyオブジェクトに属性を追加するのに便利です.
let takePhoto = function () {
    console.log('   ');
}
//   takePhoto      after
Object.defineProperty(takePhoto, 'after', {
    writable: true,
    value: function () {
        console.log('   ');
    },
});
//   takePhoto      before
Object.defineProperty(takePhoto, 'before', {
    writable: true,
    value: function () {
        console.log('    ');
    },
});
//     
let aop = function (fn) {
    return function () {
        fn.before()
        fn()
        fn.after()
    }
}

takePhoto = aop(takePhoto)
takePhoto()
プロトタイプチェーンとクラスに基づく装飾者が実現します.
JavaScriptでは、関数もクラスも自分の原型を持っています.プロトタイプチェーンを通じて、私たちも簡単に動態的に拡張できます.以下はプロトタイプチェーンに基づいた書き方です.
class Test {
    takePhoto() {
        console.log('  ');
    }
}
// after AOP
function after(target, action, fn) {
    let old = target.prototype[action];
    if (old) {
        target.prototype[action] = function () {
            let self = this;
            fn.bind(self);
            fn(handle);
        }
    }
}
//   AOP        
after(Test, 'takePhoto', () => {
    console.log('    ');
});

let t = new Test();
t.takePhoto();
ES 7修飾器を使って装飾者を実現します.
ES 7に@decocorator修飾器の提案を導入し、阮一峰の文章を参照してください.修飾器はクラスの挙動を変更する関数です.現在Babelトランスコーダはサポートされています.注意修飾器は、装飾類またはクラスの属性、方法のみです.三者の具体的な違いはMDN Object.definePropertyを参照してください.Type Scriptの実現はまた違っています.Type Script Decorator.
次に、修飾器によって方法の装飾を実現します.
function after(target, key, desc) {
    const { value } = desc;
    desc.value = function (...args) {
        let res = value.apply(this, args);
        console.log('   ')
        return res;
    }
    return desc;
}

class Test{
    @after
    takePhoto(){
        console.log('  ')
    }
}

let t = new Test()
t.takePhoto()
修飾器を使うコードは非常に簡潔で明瞭であることがわかる.
シーン:性能報告
装飾者モードは、多くのシーンに適用されてもよく、典型的なシーンは、ある非同期要求時の性能データを記録して報告することである.
function report(target, key, desc) {
    const { value } = desc;
    desc.value = async function (...args) {
        let start = Date.now();
        let res = await value.apply(this, args);
        let millis = Date.now()-start;
        //     
        return res;
    }
    return desc;
}

class Test{
    @report
    getData(url){
    // fetch   
    }
}

let t = new Test()
t.getData()
このように@report修飾されたコードを使用すると要求された時間が報告されます.拡張または変更report関数は、業務コードに影響を与えず、逆も同様である.
シーン:異常処理
既存のコードに対して簡単な異常処理ができます.
function handleError(target, key, desc) {
    const { value } = desc;
    desc.value = async function (...args) {
        let res;
        try{
            res = await value.apply(this, args);
        }catch(err){
            //     
            logger.error(err)
        }
        return res;
    }
    return desc;
}

class Test{
    @handleError
    getData(url){
        // fetch   
    }
}

let t = new Test()
t.getData()
以上の二つの例を通して、修飾器の定義は簡単ですが、機能は非常に強いです.
結び目
私たちは高次関数、プロトタイプチェーン、Object.defineProperty@Decoratorそれぞれによって装飾者モードを実現しました.次に振り返ってみます.
  • 装飾者モードは、業務コードに業務関連機能以外の機能を付加するのに適しています.
  • 装飾者モードは無痛で他人のコードを拡張するのに適しています(他の人の項目を引き継ぐ必要がありますよね)
  • 一部の友達は装飾者モードとvueのmixinメカニズムが似ていると思うかもしれませんが、実は彼らは全部「開放-閉鎖原則」と「単一職責原則」の体現です.
    コードjsbin住所を添付します.
  • ES 3
  • ES 5
  • プロトタイプ
  • ES 7