JS階段――アンダースコアソース(1)

10612 ワード

目次
説明
undersscoreは、JavaScriptのツールライブラリとして機能式プログラミングに多くの方法を提供していますので、ソースを読むのは関数式プログラミングを理解するのに適しています.筆者が読んだのは1.8.3版で、コードとコメントはすべて手書きで書いています.また、インターネットの資料も参考にしました.そして、es 6のletを使ってvarに代わって、読み終わったらカプセル化されたjsファイルをgithubに置きます.
_.VERION='1.8.3';
直ちに関数を実行します
undersscoreの最外層は即座に関数を実行して、すべての内容は関数の内部に置いて、利用する思想は閉じます.(function ( ) {...}) ()は独立した作用領域を形成しています.このような利点は全体を汚染しなくてもいいし、他の要因が内部関数に与える影響も防げます.
グローバル変数宣言
 let root = (
                (typeof self == 'object') &&
                // self  window    ,               
                (self.self === self) &&
                //     self,  self       , window    
                (self)
                //        ,       window,   window  root,  self window
             ) ||
             (
                 (typeof global == 'object') &&
                 // global  node          
                 (global.global === global) &&
                 //     gloabl,  global       
                 (global)
                 //        ,       global,   global  root
            ) ||
            (this);
            //          ,    this,         window       ,   global  node   
このセグメントコードは主に環境のグローバル名前空間を確認し、変数rootに値を与えます.注意に値するのは、ネット上の多くのソースコードの分析は
var root=this
しかし、1.8.3バージョンでgithubのソースコードを調べました.一番上のパッケージ方式であることが分かりました.
1、前方互換厳格モードで、厳格モードで直接にthisを使うとundefinedが得られます.これはecma 262第5版の中で、new以外のオブジェクトのthisをグローバルの名前空間に向けて誤魔化さないように、undefinedに設定したからです.2、WebWorkカーをサポートするために、WebWorkカーでselfが使えますが、windowは使えません.
もう一つの注意すべき点は:
(typeof self='object')
ここで使うのは==ではなく、==です.と==の違いは不完全などで、一つは全部で、==を使う利点は転義できます.グローバル名前空間は必ずしも完全に等価が対象とは限らないので、これはブラウザの実現と関係があります.
名前の衝突を避けるlet previousUnderscore = root._; // _ previousUnderscore previous Underscoreは、字面から「以前のundersscore」と理解しています.最初はこの文の意味が分かりませんでしたが、最初から最後まで一つのところにprevious Underscoreが使われました.
_.noConflict = function() {
    root._ = previousUnderscore;
    return this;
  };
//   noConflict      
衝突が発生した場合は、例えば
var under scorecache=u.noConflick()
undersscoreの名前を再定義します.
_.(初期化)
    let _ = function (obj) {
        if (obj instanceof _){
            return obj;
        }
        //   obj   ·_·     ,     obj
        if (!(this instanceof _)) {
            return new _(obj);
        }
        //     `_`     
        //    new   ,       
        this._wrapped = obj;
        //  obj   this._wrapped  
    };
実は、コア関数_は、実は構造関数であり、new呼び出しのない構造関数をサポートしています.関数スタイルのコードを使うと大きな影響はありませんが、オブジェクトスタイル向けのコードを使うと、ここではユーザーがnewを呼び出す手間が省けます.
nodeサービス中:
if (typeof exports != 'undefined' && !exports.nodeType) {
    if (typeof module != 'undefined' && !module.nodeType && module.exports) {
      exports = module.exports = _;
    }
    exports._ = _;
  } else {
    root._ = _;
  }
これはNode.jsにおける共通モジュールの実装方法であり、exportが存在するかどうかを判断することによって、局所変数_を決定する.exportsに割り当てられています.ちなみにAMD規格、CMD規格とUMD規格、Underscore.jsはAMDをサポートしています.ソースの末尾に定義があります.ここで簡単に述べます.
AMDJ
define(['underscore'], function (_) {
    //todo
            });
cmd:Common Module Definition/draft、CMDモジュール定義仕様
var _ = require('underscore');
module.exports = _;
ちなみに、nodeTypeを使ってexportsとmoduleを確保するのはHTMLの要素ではない.
原型を保存
let ArrayProto = Array.prototype,
    ObjProto = Object.prototype,
    SymbolProto = ((typeof(Symbol)) !== ("undefined")) ? (Symbol.prototype) : (null);
    //     ,     ,      
let push = ArrayProto.push,
    slice = ArrayProto.slice,
    // slice  ,             
    toString = ObjProto.toString,
    hasOwnProperty = ObjProto.hasOwnProperty;
    //     ,     ,      
    //                (      )
let nativeIsArray = Array.isArray,
    nativeKeys = Object.keys,
    //                       
    nativeCreate = Object.create;
//     create       ,       create        
//ES5    ,       , underscore      
ちなみに、var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null; 2009年のES 5は6種類の言語タイプを規定しています.Null Udefined Number Boolean String ObjectはES 5/タイプとES 5/タイプの変換とテストを参照してください.新しく登場したES 6は6種類の原始タイプを含みます.Null Unidefined Number Boolean StringとSymbol、もう一つのObjectがあります.JavaScriptのデータタイプとデータ構造に詳しいです.新しく追加されたSymbolはすでに提出されています.具体的な概念はここではもう復唱しません.Symbolを参考にしてください.ES 6の普及のおかげで、クライアントブラウザも多くSymbolをサポートしています.例えば、Firefox v 36+とChrome v 38+など、ES 6サポート状況を具体的に参照してください.ES 6について深く知りたいなら、ES 6 In Depthという文章とES 6草案を見てもいいです.
オブジェクト作成の特殊処理
let Ctor = function () {};  //          
let baseCraate = function (prototype) {
        if (!_.isObject(prototype)) {
            return {};
        }
        //         ,       
        if (nativeCreate) {
            return nativeCreate(prototype);
        }
        //             ,              。
        //             
        Ctor.prototype = prototype;
        //               
        let result = new Ctor();
        //      
        Ctor.prototype = null;
        //   Ctor        
        return result;
        //     
    };
Object.creatのブラウザ間の互換性を処理するために、undersscoreは特殊な処理を行った.プロトタイプは直接的には実用化できませんので、まず空のオブジェクトを作成して、そのプロトタイプを私たちが実際化したいプロトタイプに指して、最後にそのオブジェクトの例に戻ります.
コールバック処理
これを見る前に、void 0とはどういう意味ですか?引用文の由来はJavaScriptにあります.もしundefinedかどうかを判断したいなら、私たちは通常こう書きます.
if(a === undefined){}
しかし、JavaScriptのundefinedは信頼できません.このような関数を書いてみます.
function test(a) {
  var undefined = 1;
  console.log(undefined); // => 1
  if(a===undefined) {
    // ...
  }
}
識別子undefinedは本当に「未定義」を反映していないので、他の手段でこの意味を取得します.幸い、JavaScriptはまた、指定された表現に対して値を求め、信頼されたundefinedに戻るvoid演算子を提供しています.
void expression
最も一般的な使い方は以下の演算でundefinedが得られ、式が0の場合の演算オーバヘッドが最小となります.
void 0;
// or
void(0);
underscoreでは、undefinedを取得する必要があるところは、void 0によって代替されました.もちろん、曲線救国の方式は一つだけではないです.私達はjqueryを包むすぐ実行関数を見ました.
(function(window,undefined) {
    // ...
})(window)
この関数では、私たちは第二のパラメータ(形参の名前はundefined)を渡さないと、第二のパラメータの値は「未定義」に伝達されます.したがって、このようにして、関数の作用領域のすべてのundefinedは信頼されたundefinedであります.
    // underscore    
    //   this  (context  )  argCount            、    
    let optimizeCb = function (func, context, argCount) {
        if (context === void 0) {
            return func;
        }
        // void 0  undefined,                   

        switch (argCount) {
            //      argCount,       argCount,       null,  3,        
            // 1      ,        
            case 1: return function (value) {
                return func.call(context, value);
            };
            //    2      ,         2      
            // 3      ,      、          
            // _.each、_.map
            case 3:return function (value, index, collection) {
                return func.call(context, value, index, collection)
            };
            // 4      ,      、   、          
            //_.reduce、_reduceRight
            case 4:return function (accumulator, value, index, collection) {
                return func.call(context, accumulator, value, index, collection);
            };
        }
        //              ,    apply      
        return function () {
            return func.apply(context, arguments)
        }
実は上のswitch-case文で直接に下記のreturn文を実行しなくてもいいです.同じ効果ではないです.callはappyよりずっと速いです.コールにはこれらのステップがありません.https://segmentfault.com/q/1010000007894513 http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.4.3http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.4.4
次に、セット反復に対するコールバック処理である.
    var builtinIteratee;
    //           
    var cb = function(value, context, argCount) {
        if (_.iteratee !== builtinIteratee) {return _.iteratee(value, context);}
        //           ,        
        if (value == null) {return _.identity;}
        //     value,         
        if (_.isFunction(value)) {return optimizeCb(value, context, argCount);}
        //       ,        
        if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);
        //       ,        
        return _.property(value);
        //      ,          
    };
    //       ,    argCount     cb  。        。
    _.iteratee = builtinIteratee = function(value, context) {
        return cb(value, context, Infinity);
    };
残りの処理
//        。
//       ,              ,                
    let shallowProperty = function(key) {
        return function(obj) {
            return obj == null ? void 0 : obj[key];
        };
    };

//     。
//    ES6 rest  。                 。
       let restArgs = function(func, startIndex) {
        startIndex = startIndex == null ? func.length - 1 : +startIndex;
        // startIndex null ,            1,              ,      startIndex
        //       rest     
        return function() {
            //     ,         
            let length = Math.max(arguments.length - startIndex, 0),
                //        length     
                rest = Array(length),
                index = 0;
            //      2   : func(a,b,*rest)
            //   : func(1,2,3,4,5);       :func.call(this, 1,2, [3,4,5]);
            for (; index < length; index++) {
                rest[index] = arguments[index + startIndex];
            }
            //   rest    ,        ,       , rest          ,       
            switch (startIndex) {
                case 0: return func.call(this, rest);
                //      0,              
                case 1: return func.call(this, arguments[0], rest);
                //                  ,          
                case 2: return func.call(this, arguments[0], arguments[1], rest);
                //       、                        
            }
            //           ,       apply
            let args = Array(startIndex + 1);
            //        
            for (index = 0; index < startIndex; index++) {
                args[index] = arguments[index];
            }
            //        
            args[startIndex] = rest;
            return func.apply(this, args);
        };
    };

//        。
//                  
    let MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
    //      +∞
    let getLength = shallowProperty('length');
    //   length     
    let isArrayLike = function(collection) {
        let length = getLength(collection);
        return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
    };