看遍世界之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

相关文章
相关标签/搜索