Js設計モードについて簡単に話します

15760 ワード

ポリシーモード
一連のアルゴリズムを定義して、それらを一つずつカプセル化して、それらを相互に置き換えることができます.目的はアルゴリズムの使用をアルゴリズムの実現と分離することである.
ポリシーモードに基づく1つのプログラムは少なくとも2つの部分から構成される.
  • のポリシークラスのセットは、特定のアルゴリズムをパッケージ化し、具体的な計算プロセスを担当する.
  • 環境類Contectは、Contectはお客様の要求を受け、その後、あるポリシークラスに依頼します.
  •         validator = {
                validate: function (value, type) {
                    switch (type) {
                        case 'isNonEmpty ':
                            {
                                return true; // NonEmpty     
                            }
                        case 'isNumber ':
                            {
                                return true; // Number     
                                break;
                            }
                        case 'isAlphaNum ':
                            {
                                return true; // AlphaNum     
                            }
                        default:
                            {
                                return true;
                            }
                    }
                }
            };
            //    
            alert(validator.validate("123", "isNonEmpty"));
    var validator = {
    
        //                  ,       
        types: {},
    
        //             
        messages: [],
    
        //            
        config: {},
    
        //          
        //        key => value 
        validate: function (data) {
    
            var i, msg, type, checker, result_ok;
    
            //          
            this.messages = [];
    
            for (i in data) {
                if (data.hasOwnProperty(i)) {
    
                    type = this.config[i];  //   key            
                    checker = this.types[type]; //           
    
                    if (!type) {
                        continue; //          ,    
                    }
                    if (!checker) { //           ,    
                        throw {
                            name: "ValidationError",
                            message: "No handler to validate type " + type
                        };
                    }
    
                    result_ok = checker.validate(data[i]); //                
                    if (!result_ok) {
                        msg = "Invalid value for *" + i + "*, " + checker.instructions;
                        this.messages.push(msg);
                    }
                }
            }
            return this.hasErrors();
        },
    
        // helper
        hasErrors: function () {
            return this.messages.length !== 0;
        }
    };
    
    
    
    
    
    //            
    validator.types.isNonEmpty = {
        validate: function (value) {
            return value !== "";
        },
        instructions: "        "
    };
    
    //            
    validator.types.isNumber = {
        validate: function (value) {
            return !isNaN(value);
        },
        instructions: "            ,  :1, 3.14 or 2010"
    };
    
    //                
    validator.types.isAlphaNum = {
        validate: function (value) {
            return !/[^a-z0-9]/i.test(value);
        },
        instructions: "             ,        "
    };
    
    
    
    var data = {
        first_name: "Tom",
        last_name: "Xu",
        age: "unknown",
        username: "TomXu"
    };
    //                 
    validator.config = {
        first_name: 'isNonEmpty',
        age: 'isNumber',
        username: 'isAlphaNum'
    };
    
    
    validator.validate(data);
    
    if (validator.hasErrors()) {
        console.log(validator.messages.join("
    ")); }
    戦略モードにより、条件分岐文の大きな部分が消去されました.アルゴリズムを独立したstrategyにカプセル化し,それらをスイッチしやすく,理解しやすく,拡張しやすいようにした.同時に、アルゴリズムは、他の場所に多重化されてもよく、それによって、多くのコピー貼り付け作業が回避される.
    プロキシモード
    プロキシモードは、このオブジェクトへのアクセスを制御するためのプロキシをオブジェクトに提供する.
    仮想エージェントは、私たちが最もよく使うプロキシモードであり、いくつかのオーバーヘッドが大きいオブジェクトを、本当にこのオブジェクトを使用する必要があるときに作成するまで遅延します.
    仮想エージェントは、画像のプリロードを実現する.
    var addImg = (function(){
        var img = document.createElement('img');
        document.body.appendChild(img);
        return { 
            setSrc: function(src){
                img.src = src;
            }
        }
    })();
    var proxyAddImg = (function(){
        var img = new Image();
        img.onload = function(){
            addImg.setSrc(this.src);
        }
        return { 
            setSrc: function(src){
                addImg.setSrc('loading.gif'); 
                img.src = src;
            }
        }
    })();
    proxyAddImg.setSrc('demo.png'); 
    仮想エージェントのHttpマージ要求は、プロキシ関数を介して、一時的な要求を収集し、最後に要求を統合してサーバに送信することができます.
    var proxySynData = (function(){
        var cache = [], //           
            timer; //   
        return function(ID){
            if(!timer){ //         
                timer = setTimeout(function(){
                    synData(cache.join()); //        
                    cache.length = 0; //    
                    clearTimeout(timer); //     
                    timer = null; //      
                }, 2000);
            }
            cache.push(ID); //    
        }
    })();
    var list = document.getElementsByTagName('input');
    for(var i = 0, item; item = list[i]; i++){
        item.onclick = function(){
            if(this.checked){
                proxySynData(this.id);
            }
        };
    }
    キャッシュエージェントは、非常によく理解され、いくつかのオーバーヘッドの大きな演算結果をキャッシュすることができる.もしあなたが2回目の関数を実行した時、同じパラメータを伝達したら、キャッシュの結果をそのまま使用します.演算量が大きいなら、これは小さくない最適化です.
    var add = function(){
        var sum = 0;
        for(var i = 0, l = arguments.length; i < l; i++){
            sum += arguments[i];
        }
        return sum;
    };
    var proxyAdd = (function(){
        var cache = {}; //           
        return function(){
            var args = Array.prototype.join.call(arguments);//               “ ”
            if(cache.hasOwnProperty(args)){//   args in cache
                console.log('      ');
                return cache[args];//         “ ”
            }
            console.log('    ');
            return cache[args] = add.apply(this,arguments);//               
        }
    })();
    console.log(proxyAdd(1,2,3,4,5)); //15
    console.log(proxyAdd(1,2,3,4,5)); //15
    console.log(proxyAdd(1,2,3,4,5)); //15
    観察者モード
    観察者モードは、オブジェクト間の1対以上の依存関係を定義するリリース・購読モードと呼ばれ、オブジェクトの状態が変化すると、その依存するすべてのオブジェクトが通知されます.
    イベントオブジェクトを定義します.以下の機能があります.
  • 傍受イベント(公衆号購読)
  • トリガイベント(公開日)
  • イベントを削除する(パブリックナンバーを取る)
  • // subscription.js
    var CLEARED = null;
    var nullListeners = {
      notify: function notify() {}
    };
    
    function createListenerCollection() {
      // the current/next pattern is copied from redux's createStore code.
      // TODO: refactor+expose that code to be reusable here?
      var current = [];
      var next = [];
    
      return {
        clear: function clear() {
          next = CLEARED;
          current = CLEARED;
        },
        notify: function notify() {
          var listeners = current = next;
          for (var i = 0; i < listeners.length; i++) {
            listeners[i]();
          }
        },
        get: function get() {
          return next;
        },
        subscribe: function subscribe(listener) {
          var isSubscribed = true;
          if (next === current) next = current.slice();
          next.push(listener);
    
          return function unsubscribe() {
            if (!isSubscribed || current === CLEARED) return;
            isSubscribed = false;
    
            if (next === current) next = current.slice();
            next.splice(next.indexOf(listener), 1);
          };
        }
      };
    }
    
    var Subscription = function () {
      function Subscription(store, parentSub, onStateChange) {
        _classCallCheck(this, Subscription);
    
        this.store = store;
        this.parentSub = parentSub;
        this.onStateChange = onStateChange;
        this.unsubscribe = null;
        this.listeners = nullListeners;
      }
    
      Subscription.prototype.addNestedSub = function addNestedSub(listener) {
        this.trySubscribe();
        return this.listeners.subscribe(listener);
      };
    
      Subscription.prototype.notifyNestedSubs = function notifyNestedSubs() {
        this.listeners.notify();
      };
    
      Subscription.prototype.isSubscribed = function isSubscribed() {
        return Boolean(this.unsubscribe);
      };
    
      Subscription.prototype.trySubscribe = function trySubscribe() {
        if (!this.unsubscribe) {
          this.unsubscribe = this.parentSub ? this.parentSub.addNestedSub(this.onStateChange) : this.store.subscribe(this.onStateChange);
    
          this.listeners = createListenerCollection();
        }
      };
    
      Subscription.prototype.tryUnsubscribe = function tryUnsubscribe() {
        if (this.unsubscribe) {
          this.unsubscribe();
          this.unsubscribe = null;
          this.listeners.clear();
          this.listeners = nullListeners;
        }
      };
    
      return Subscription;
    }();
    Reduxは観察者モードを採用しています.
    createStore(root Reducer,initial State,appyMiddleware)
    戻り値:(1)dispatch(action):actionの配布に用いられ、store内のstate(2)subscribe(listener):listenerを登録し、store内のstateが変更された後、このlistenerを実行します.unsubscrib()メソッドを返して、現在のlistenerをログアウトします.(3)get State():storeの中のstateを読み取る(4)replacceReducer():reducerを差し替えて、state修正の論理を変える
    したがって、store内部メンテナンスリスト配列は、store.subscribeで登録されたすべてのlistenerを記憶するために使用されます.store treeを更新すると、配列内のlistenerを順次実行します.
      function subscribe(listener) {
        if (typeof listener !== 'function') {
          throw new Error('Expected listener to be a function.');
        }
    
        var isSubscribed = true;
    
        ensureCanMutateNextListeners();
        nextListeners.push(listener);
         
        return function unsubscribe() {
          if (!isSubscribed) {
            return;
          }
    
          isSubscribed = false;
    
          ensureCanMutateNextListeners();
          var index = nextListeners.indexOf(listener);
          nextListeners.splice(index, 1);
        };
      }
    dispatch方法は主に二つのことを完成します.1、actionクエリreducerでstateを変更する方法によって、store tree 2、store treeを更新してから、listenerの応答関数を順次実行します.
      function dispatch(action) {
        if (!(0, _isPlainObject2['default'])(action)) {
          throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.');
        }
    
        if (typeof action.type === 'undefined') {
          throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?');
        }
    
        if (isDispatching) {
          throw new Error('Reducers may not dispatch actions.');
        }
    
        try {
          isDispatching = true;
          currentState = currentReducer(currentState, action);
        } finally {
          isDispatching = false;
        }
        //    ,  listener,      
        var listeners = currentListeners = nextListeners;
        for (var i = 0; i < listeners.length; i++) {
          var listener = listeners[i];
          listener();
        }
    
        return action;
      }
    reduxでは、connect(connect)方法で、reactのUIコンポーネントをreduxの状態、イベントに関連付ける.
        var Connect = function (_Component) {
          _inherits(Connect, _Component);
          /*
          *      ,        ,   this.store,  this.onStateChange.bind(this)
          */
          function Connect(props, context) {
            _classCallCheck(this, Connect);
    
            var _this = _possibleConstructorReturn(this, _Component.call(this, props, context));
    
            _this.version = version;
            _this.state = {};
            _this.renderCount = 0;
            _this.store = props[storeKey] || context[storeKey];
            _this.propsMode = Boolean(props[storeKey]);
            _this.setWrappedInstance = _this.setWrappedInstance.bind(_this);
    
            (0, _invariant2.default)(_this.store, 'Could not find "' + storeKey + '" in either the context or props of ' + ('"' + displayName + '". Either wrap the root component in a , ') + ('or explicitly pass "' + storeKey + '" as a prop to "' + displayName + '".'));
    
            _this.initSelector();
            _this.initSubscription();
            return _this;
          }
    
          ···
          //   store.subscribe(listener)      , store       , store     ,    view
          Connect.prototype.componentDidMount = function componentDidMount() {
            if (!shouldHandleStateChanges) return;
            this.subscription.trySubscribe(); // //    this.store.subscribe(this.onStateChange);
            this.selector.run(this.props);
            if (this.selector.shouldComponentUpdate) this.forceUpdate();
          };
           ···
    
          Connect.prototype.componentWillUnmount = function componentWillUnmount() {
            if (this.subscription) this.subscription.tryUnsubscribe(); //     
            this.subscription = null;
            this.notifyNestedSubs = noop;
            this.store = null;
            this.selector.run = noop;
            this.selector.shouldComponentUpdate = false;
          };
            ···
          //       
          Connect.prototype.initSubscription = function initSubscription() {
            if (!shouldHandleStateChanges) return;
    
            var parentSub = (this.propsMode ? this.props : this.context)[subscriptionKey];
            this.subscription = new _Subscription2.default(this.store, parentSub, this.onStateChange.bind(this));  //    Subscription.js   , store      listener---this.onStateChange.bind(this)
    
            this.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription);
          };
    
          Connect.prototype.onStateChange = function onStateChange() {
            this.selector.run(this.props);
    
            if (!this.selector.shouldComponentUpdate) {
              this.notifyNestedSubs();
            } else {
              this.componentDidUpdate = this.notifyNestedSubsOnComponentDidUpdate;
              this.setState(dummyState);
            }
          };
    
        ···
    
          return Connect;
        }(_react.Component);
    
    装飾者モード
    オブジェクト自体を変更しない上で、プログラムの実行中にオブジェクトに動的に追加する追加のタスクがあります.
    //  window  onload  
    window.onload = function() {
        alert(1)
    }
    
    var _onload = window.onload || function(){}  //      ,         ,          ,               
    
    window.onload = function() {
        _onload()
        alert(2)
    }
    
    //     this     
    var getId = document.getElementById; //    ,this  window
    document.getElementById = function(ID){
        console.log(1);
        return getId(ID);
    }
    document.getElementById('demo'); //this    document
    
    //     document     this  getId
    AOP装飾関数
    AOP(Asppect Oriented Prograamming)面向けプログラミング
    コア業務の論理に関係のない機能を引き出す
    更に「動的編入」方式で業務ロジックモジュールを組み込む
    //     
    Function.prototype.before = function(beforeFunc){
        var that = this; //        
        return function(){ //                  
            beforeFunc.apply(this, arguments); //      
            return that.apply(this, arguments); //                
        }
    }
    
    document.getElementById = document.getElementById.before(function() {
        alet(1)
    })
    //      
    class Home extends Component {
        //....
    }
    
    //             ,  props        react-redux   connect()  
    export default connect(state => ({todos: state.todos}))(Home);
    
    //            ,       
    @connect(state => ({ todos: state.todos }))
    class Home extends React.Component {
        //....
    }
    アダプタモード
    アダプターモードは2つの非互換インターフェースの間のブリッジとして機能しています.
    広東省の地図をレンダリングしたページを作成していると仮定します.現在、第三者の資源から広東省のすべての都市及びそれらに対応するIDを取得しました.
    var getGuangdongCity = function () {
        var GuangdongCity = [
           {
              name:'shenzhen',
              id  : '11'
           },
           {
             name:'guangzhou',
             id:12
           }
      ];
      return GuangdongCity;
    };
    
    var render  = function (fn) {
        console.log('starting render Guangdong map');
        document.write(JSON.stringify(fn()));
    };
    /* var GuangdongCity = {
    //                      shenzhen:11,
    //                      guangzhou:12,
    //                      zhuhai:13
    //                     };
    */
    var addressAdapter = function (oldAddressfn) {
    var address = {},
        oldAddress = oldAddressfn();
        for(var i = 0 , c; c = oldAddress[i++];){
            address[c.name] = c.id;  //                           
        }
        return function () {
            return address;
        }
    };
    render(addressAdapter(getGuangdongCity));
    
    アダプターモードを使用すると、パラメータタイプの多少の不一致による問題を解決できます.
    reduxはreactに合うように、mapStateToProps()という関数を持って、stateをPropsの外部状態に変えます.これで外部からまたコンポーネントに戻ることができます.