看遍世界之underscore源碼分析百大玩法

爲了提高下本身的技術水平,開始研究us中的方法,the first one,哈哈css

按照underscore 1.8.3版原本進行研究數組

先從集合方法開始研究bash

在us中集合的方法有25個,其中有each,map,reduce,find,filter,max,size等, 具體能夠參照文檔閉包

※ 今天分析第一個_.eachapp

使用:
      _.each(list, iteratee, context)
     iteratee爲迭代函數,函數裏具備三個參數
傳入數組
 _.each([1,2,3], alert);    // 會分別alert彈出1,2,3
傳入對象
_.each({
        site: 'so.com',
        from: 'weibo',
        url: 'xxx.com/',
    }, console.log);        
// 這裏會調用console.log直接打印出對象的值
// so.com site {site: "so.com", from: "weibo", url: "xxx.com/"}
// weibo from {site: "so.com", from: "weibo", url: "xxx.com/"}
// xxx.com/ url {site: "so.com", from: "weibo", url: "xxx.com/"}
複製代碼

☆: list爲數組時,三個參數分別爲item數組的每一項,index數組的索引和arr數組自己。函數

☆:list爲對象時,三個參數分別爲value對象的值,key對象的key和obj對象自己ui

大體介紹了一下,那就廢話很少說,開始寫吧this

// 首先是個閉包環境,防止形成全局污染
(function() {
   // _實際上是個構造函數,支持無new調用
   // 而後將傳入的參數賦給this._wrapped屬性(暫時還不清楚這個屬性的做用)
   var _ = function (obj) {
        // 若是obj是_的實例直接返回obj
        if (obj instanceof _) {
            return obj;
        }
        // 若是不是_的實例,那就new一個
        if (!(this instanceof _)) {
            return new _(obj);
        }
        // 將obj賦值給this._wrapped屬性
        this._wrapped = obj;

        return obj;
    }

    // 寫一些ES5原生方法放在這
    var nativeKeys = Object.keys;

    // 在_.each裏調用
    // optimizeCb函數來返回一個回調函數
    var optimizeCb = function (fn, context) {
        // 若是沒有傳context,就直接返回fn
        if (context === void 0) {
            return fn;
        }

        return function () {
            // 返回一個函數指向context
            return fn.apply(context, arguments);
        }
    };

    // 在getLength時調用
    // 傳入key值
    var property = function (key) {
        return function (obj) {
            return obj !== null && obj[key];
        }
    }

    // 在_.isArrayLike調用
    // 經過一個閉包的形勢去獲取length屬性
    var getLength = property('length');

    // 在_.isArrayLike調用
    // js裏Length的最大值
    var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;

    // 在_.each裏調用
    // 數組或者類數組都有length屬性
    // 類型是number
    // 而且不能大於js裏的最大值
    _.isArrayLike = function (obj) {
        var length = getLength(obj);
        return typeof length === 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
    };

    // 在_.each調用
    // 遍歷對象的key以數組的形勢返回
    _.keys = function (obj) {
        // 若是傳入的obj不是對象,就直接返回一個[]
        // 那麼接下來繼續寫個_.isObject
        if (!_.isObject(obj)) return [];

        // 若是支持ES5原生方法直接用,別客氣
        if (nativeKeys) return nativeKeys(obj);

        // 不支持的話,那就手動寫吧
        var keys = [];
        for (var key in obj) {
            // 判斷是否是私有屬性,若是不是私有的會帶上一堆原型上的,這可不行
            if (_.has(obj, key)) keys.push(key);
        }

        return keys;
    };

    // 在_.keys調用
    // hasOwnProperty.call會把指向放到obj上,防止hasOwnProperty被寫在對象屬性上修改掉
    _.has = function (obj, key) {
        return obj !== null && hasOwnProperty.call(obj, key);
    };

    // 在_.keys調用
    // 這裏的對象還要算上function
    _.isObject = function (obj) {
        var type = typeof obj;
        return type === 'object' || type === 'function' && !!obj;   // !!obj 判斷數組用的   !![] true
    };

    // 源碼在148行
    // each方法直接掛載在_的prototype上
    _.each = _.forEach = function (obj, iteratee, context) {
        // 經過context獲得iteratee迭代函數
        iteratee = optimizeCb(iteratee, context);

        var i, len;

        //each其實就是循環兩種類型,一種是數組(類數組),另外一種就是對象
        // 用_.isArrayLike去判斷是否是數組或類數組
        if (_.isArrayLike(obj)) {
            // 遍歷數組
            // 執行iteratee函數,分別傳入item, index, arr
            for (i = 0, len = obj.length; i < len; i++) {
                iteratee(obj[i], i, obj);
            }
        } else {
            var keys = _.keys(obj); // 拿到obj的key值以數組的形勢返回,等同Object.keys
            // 接下來遍歷對象
            for (i = 0, len = keys.length; i < len; i++) {
                iteratee(obj[keys[i]], keys[i], obj);   // value, key, obj
            }
        }
        // 返回obj,供鏈式調用
        return obj;
    };

    // 掛載在window對象上,這樣其實很很差,經過export導出更合理
    window._ = _;
}.call(this));
複製代碼

總結一下吧: 遍歷其實就是兩種類型,先要判斷兩種類型,而後根據傳入的iteratee迭代函數再遍歷便可,寫的很差請多多包涵!url

相關文章
相關標籤/搜索