实现第一个集合方法each

每天打算研究一个方法,用以提升下自己的技术水平,the first one,哈哈

使用:
      _.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对象本身

大致介绍了一下,那就废话不多说,开始写吧

// 1首先是个闭包环境,防止造成全局污染
(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迭代函数再遍历即可,写的不好请多多包涵!

你可能感兴趣的:(实现第一个集合方法each)