underscoreアーキテクチャ解析

47114 ワード

使用方法JQと同様に、underscoreもグローバルオブジェクトを占有している.しかし、underscoreはユーザーに2つの使用方法を提供しています.
_.unique();		//              
_().unique();	//                 

スキーマ#スキーマ#
われわれは浅いところから深いところまで,一歩一歩完備している.
初期アーキテクチャ
まず、2つの呼び出し方式をサポートします.
(function (root) {
  // _        
  var _ = function () {
    if(!(this instanceof _)){
      return new _();
    }
  }

  _.unique = function(){
    console.log(1);
  }

  _.prototype.unique = function(){
    console.log(1);
  }

  //      
  root._ = _;
})(this);

コメント:
  • 構造関数のreturn new _()および_.prototype.fnは、第2の方法のためにサービスする
  • である.
  • 単純な構造関数による静的メソッド呼び出しは、functionで宣言された関数を直接呼び出すのと同じ
  • である.
    以上のコードにより、両方の使用方法が正常に使用されることを保証できます.
    しかし、このように書くことは新しい問題を引き起こした--同じ方法は静的方法の中で1回書くべきで、また原型チェーンの上で1回書くべきで、このようにするのはとても愚かで、中の方法が百個以上ある時全体の枠組みはとても肥大化します
    私たちは混入でこの問題を解決することができます.
    かき混ぜる
    まずmixinという方法の論理を見てみましょう
    _.mixin = function(obj){
        _.each(arr, callback(){
        	xxx
        })
    }
    

    解析:
  • mixinは、混入対象
  • を示すパラメータを受信する.
  • eachは、入力されたコールバック関数を用いて、入力配列の各項目を動作
  • する.
  • 混入この方法では、eachの第1のパラメータarrは、混入対象の静的方法の名前であるべきであり、第2のパラメータは、_のプロトタイプチェーンに具体的な静的方法を混入動作
  • である.
  • eachこの方法は他の用途に外部から呼び出すこともできるので、第2点と第3点は全く同じものではない
  • .
    (function (root) {
        var _ = function () {
            if(!(this instanceof _)){
                return new _();
            }
        }
    
        _.unique = function(){
            console.log(1);
        }
    
        // _.prototype.unique = function(){
        //   console.log(1);
        // }
    
        //            
        _.each = function(arr, cb){
            for (var i = 0; i < arr.length; i++) {
                cb(arr[i]);
            }
        }
    
        //                             
        _.functions = function(obj){
            var resultArr = [];
            for( var key in obj ){
                resultArr.push(key);
            }
            return resultArr;
        }
    
    	//                        _      
        _.mixin = function(obj){
            _.each(_.functions(obj), function (key) {
                var fn = obj[key];
                _.prototype[key] = function () {
                    fn();
                };
            });
        }
    
        _.mixin(_); //                   
        //      
        root._ = _;
    })(this);
    

    今までの問題は解決しました.の
    データ・ソースの追加
    以上のコードは2つの使用方法を走らせることに成功しただけですが、私たちはデータを入力して処理していません.
    通常、開発ではユーザーがデータを処理しなければこのフレームワークを使用できません.
    var arr = [1,2,3,4,5,5,6];
    
    _.unique(arr);
    _(arr).unique();
    

    1つ目は、メソッドを使用してコンストラクション関数の静的メソッドを呼び出し、直接関数にパラメータを設定すればよい.
    2つ目の使用方法は、_のインスタンスを生成するため、インスタンス化時にデータソースを格納するための属性を追加する必要があることに注意してください.
    そして混入時にプロトタイプチェーンに対応する方法をバインドする
    (function (root) {
      var _ = function (val) {
        if(!(this instanceof _)){
          return new _(val);
        }
        this.val = val;
      }
    
    
      //      (      )
      _.unique = function(arr, cb){
        var resultArr = [];
        var item;
        for (var i = 0; i < arr.length; i++) {
          item = cb ? cb(arr[i]) : arr[i];
          if( resultArr.indexOf(item) === -1 ){
            resultArr.push(item);
          }
        }
        return resultArr;
      }
    
      //                    
      _.each = function(arr, cb){
        for (var i = 0; i < arr.length; i++) {
          cb(arr[i]);
        }
      }
    
      //                             
      _.functions = function(obj){
        var resultArr = [];
        for( var key in obj ){
          resultArr.push(key);
        }
        return resultArr;
      }
    
      //   
      _.mixin = function(obj){
        _.each(_.functions(obj), function (key) {
          var fn = obj[key];
            
          //                          
          _.prototype[key] = function () {
            //       ,           ,            
            var args = [this.val];  //           
            Array.prototype.push.apply(args, arguments);  //                
            return fn.apply(this, args);
          };
        });
      }
    
      //                   
      _.mixin(_);
      //      
      root._ = _;
    })(this);
    

    実際の効果は以下の通りです.
    var arr = [1,2,3,4,5,5,6];
    
    _.unique(arr);		// [1, 2, 3, 4, 5, 6]
    _(arr).unique();	// [1, 2, 3, 4, 5, 6]
    
    var arr = [1,2,3,4,5,5,6,'a','A'];
    function toLowerCase(item){
        return typeof item === 'string' ? item.toLowerCase() : item;
    }
    
    console.log(_.unique(arr, toLowerCase)); // [1, 2, 3, 4, 5, 6, "a"]
    console.log(_(arr).unique(toLowerCase)); // [1, 2, 3, 4, 5, 6, "a"]
    

    素晴らしい!
    しかし問題はまた来て、今まで私達は1回の関数を使うことしかできなくてチェーン式の呼び出しを行うことができません
    チェーンコール
    チェーン呼び出しを行う場合は、中間局を使用してチェーン呼び出しを行うかどうかを決定することができます.もしそうであれば、後で他の関数を呼び出し続け、そうでなければ結果を直接返します.
    (function (root) {
      var _ = function (val) {
        if(!(this instanceof _)){
          return new _(val);
        }
        this.val = val;
      }
      /*      */
      //                 
      //              ,            
      var useChain = function(instance, val){
        if(instance._chain){
          instance.val = val;
          return instance;
        }
        return val;
      }
    
      /*      (      ) */
      //                ,                
      _.chain = function(val){
        var instance = _(val);
        instance._chain = true;
        return instance;
      }
    
      //     
      _.unique = function(arr, cb){
        var resultArr = [];
        var item;
        for (var i = 0; i < arr.length; i++) {
          item = cb ? cb(arr[i]) : arr[i];
          if( resultArr.indexOf(item) === -1 ){
            resultArr.push(item);
          }
        }
        return resultArr;
      }
    
      //       
      _.max = function(arr){
        var result = arr[0] ? arr[0] : false;
        for (var i = 1; i < arr.length; i++) {
          if( arr[i] > result ) result = arr[i];
        }
        return result;
      }
    
      //                    
      _.each = function(arr, cb){
        for (var i = 0; i < arr.length; i++) {
          cb(arr[i]);
        }
      }
    
      //                             
      _.functions = function(obj){
        var resultArr = [];
        for( var key in obj ){
          resultArr.push(key);
        }
        return resultArr;
      }
    
      //   
      _.mixin = function(obj){
        _.each(_.functions(obj), function (key) {
          var fn = obj[key];
          //                          
          _.prototype[key] = function () {
            //       ,           ,            
            var args = [this.val];  //           
            Array.prototype.push.apply(args, arguments);  //               
            return useChain(this, fn.apply(this, args));
          };
        });
      }
    
      //                   
      _.mixin(_);
      //      
      root._ = _;
    })(this);
    

    解析:
  • このコードは、従来のプライベートメソッドuseChainと静的メソッドchain
  • とを比較して追加された.
  • ここでのmixinの関数に注意して、戻る時にuseChainを使って包装して、useChainの作用は現在のインスタンスがチェーン呼び出しを行うかどうかを判断するので、もしそうならば1つのunderscoreのインスタンスを返して、さもなくば直接現在の値を
  • に返します.
  • ここでの静的方法chainは、現在のインスタンスがチェーン呼び出しを行うかどうかをマークするために使用される
  • .
    実際の効果は以下の通りです.
    var arr = [1, 2, 3, 4, 5, 5, 6, 'a', 'A'];
    
    function toLowerCase(item) {
      return typeof item === 'string' ? item.toLowerCase() : item;
    }
    
    console.log(_.unique(arr, toLowerCase));
    // [1, 2, 3, 4, 5, 6, "a"]
    console.log(_.chain(arr).unique(toLowerCase).max().val);
    // 6
    console.log(_(arr).chain().unique(toLowerCase));
    // {val: Array(7), _chain: true}
    

    以上のように初期モデルを構築した後、後で何か新しい方法が必要なら直接静的方法を追加すればいい!
    もちろんunderscoreは上記だけでなく、興味のある方は公式サイトでご覧ください~~
    質問があればコメントエリアや私信メッセージを歓迎します~
    もし本文があなたに役に立つなら、いいねをつけてから行きましょう.♪(・ω・)ノ