zoukankan      html  css  js  c++  java
  • underscore源码解析(集合)

    underscore就是一些函数的集合,大概分为6部分,分别是基础函数,集合,数组,函数,对象,实用功能.

    在实现每一个功能的时候,基本上先自己实现,再对比。

    (function(){

    }.call(this));
    基本框架长这样,就一个函数的自调用,用window作为调用对象,防止污染全局。

    //把window对象先存起来备用吧。
    var root = this;
    //弄个window的属性_;
    var previousUnderscore = root._;
    //存一些原型
    var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
    //存一些原型的方法
    var
    push = ArrayProto.push,
    slice = ArrayProto.slice,
    toString = ObjProto.toString,
    hasOwnProperty = ObjProto.hasOwnProperty;

    var
    nativeIsArray = Array.isArray,
    nativeKeys = Object.keys,
    nativeBind = FuncProto.bind,
    nativeCreate = Object.create;
    //弄个备用的构造函数
    var Ctor = function(){};

    //构造函数_,obj是_实例的话,直接返回obj.若是直接调用构造函数而不是通过new操作符的话,会调用第二个if.执行new _(obj).
    //另外,这样的话obj就不是实例了,所以弄个实例属性_wrapped把obj存起来备用。
    var _ = function(obj) {
    if (obj instanceof _) return obj;
    if (!(this instanceof _)) return new _(obj);
    this._wrapped = obj;
    };
    //这个是什么nodejs接口的,不懂。过了,嘿嘿
      if (typeof exports !== 'undefined') {
        if (typeof module !== 'undefined' && module.exports) {
          exports = module.exports = _;
        }
        exports._ = _;
      } else {
        root._ = _;
      }
    //这个是关于函数优化的,参数函数,调用EC,参数数量。
    //只传了一个参数的话,context==null.这里应该是可以用context === void 0或者context==null,一个意思。直接返回传入的函数
    //没传第三个参数的话,默认就是传三个参数给调用的函数,一般是用在forEach,filter这些数组方法,function(element,index,array);
    //第三个参数是1或2的话,感觉没什么特别的,直接调用吧.是4的话就是reduce函数之类的。
    var optimizeCb = function(func, context, argCount) {
    if (context === void 0) return func;
    switch (argCount == null ? 3 : argCount) {
    case 1: return function(value) {
    return func.call(context, value);
    };
    case 2: return function(value, other) {
    return func.call(context, value, other);
    };
    case 3: return function(value, index, collection) {
    return func.call(context, value, index, collection);
    };
    case 4: return function(accumulator, value, index, collection) {
    return func.call(context, accumulator, value, index, collection);
    };
    }
    return function() {
    return func.apply(context, arguments);
    };
    };


    //没传参数的话,直接返回一个函数,设置a,调用这个函数a,返回传给a的参数
    //value是函数的话,直接调用上面的函数优化公式,
    //对象的话,返回是否对象匹配的函数。
    //返回一个property函数,这个函数调用传一个对象,value是键,返回值

    var cb = function(value, context, argCount) {
    if (value == null) return _.identity;
    if (_.isFunction(value)) return optimizeCb(value, context, argCount);
    if (_.isObject(value)) return _.matcher(value);
    return _.property(value);
    };

    _.identity = function(value) {
    return value;
    };
    _.property = function(key) {
    return function(obj) {
    return obj == null ? void 0 : obj[key];
    };
    };
    //源码中没有用到。返回一个迭代函数吧。
    _.iteratee = function(value, context) {
    return cb(value, context, Infinity);
    };
    //创建一个对象合并器.keyfunc是一个对象键数组函数,返回对象所有键。
    //undefinedOnly么是定义是不是只有往目标对象上添加属性而不管属性是否存在。一般为
    //true。也就是当对象不存在这个属性的时候才添加。
    //当返回的函数只有一个参数时或者直接调用,直接返回那个参数。
    //否则把对象的参数一个个遍历出来,添加到新对象,再返回新对象

    //放个参考的
    //https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

    var createAssigner = function(keysFunc, undefinedOnly) {
    return function(obj) {
    var length = arguments.length;
    if (length < 2 || obj == null) return obj;
    for (var index = 1; index < length; index++) {
    var source = arguments[index],
    keys = keysFunc(source),
    l = keys.length;
    for (var i = 0; i < l; i++) {
    var key = keys[i];
    if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
    }
    }
    return obj;
    };
    };

    //创建一个对象,原型是参数prototype
    //参数不是对象的话,直接返回
    //有原生的Object.create方法的话直接用
    //利用Ctor构造函数创建对象原型。
    var baseCreate = function(prototype) {
    if (!_.isObject(prototype)) return {};
    if (nativeCreate) return nativeCreate(prototype);
    Ctor.prototype = prototype;
    var result = new Ctor;
    Ctor.prototype = null;
    return result;
    };


    //数组索引的最大值
    var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;

    //是不是类数组,包括原生数组和nodelist类型
    var isArrayLike = function(collection) {
    var length = collection && collection.length;
    return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
    };

    //前面一些基础函数写完了。写正式的了。首先集合方法。


    //对数组和对象的键集合进行迭代遍历.每个key调用函数,返回原来的obj

    _.each = _.forEach = function(obj, iteratee, context) {
    //iteratee默认3个参数
    iteratee = optimizeCb(iteratee, context);
    var i, length;
    //数组直接遍历,对象遍历键集合
    if (isArrayLike(obj)) {
    for (i = 0, length = obj.length; i < length; i++) {
    iteratee(obj[i], i, obj);
    }
    } else {
    var keys = _.keys(obj);
    for (i = 0, length = keys.length; i < length; i++) {
    iteratee(obj[keys[i]], keys[i], obj);
    }
    }
    return obj;
    };

    //每个key调用函数,返回调用后集合数组

    _.map = _.collect = function(obj, iteratee, context) {
    iteratee = cb(iteratee, context);
    //数组和对象的不同处理。
    var keys = !isArrayLike(obj) && _.keys(obj),
    length = (keys || obj).length,
    results = Array(length);
    for (var index = 0; index < length; index++) {
    var currentKey = keys ? keys[index] : index;
    results[index] = iteratee(obj[currentKey], currentKey, obj);
    }
    return results;
    };
    //参数一大堆先不看了。黑
    function createReduce(dir) {

    function iterator(obj, iteratee, memo, keys, index, length) {
    for (; index >= 0 && index < length; index += dir) {
    var currentKey = keys ? keys[index] : index;
    memo = iteratee(memo, obj[currentKey], currentKey, obj);
    }
    return memo;
    }

    return function(obj, iteratee, memo, context) {
    iteratee = optimizeCb(iteratee, context, 4);
    var keys = !isArrayLike(obj) && _.keys(obj),
    length = (keys || obj).length,
    index = dir > 0 ? 0 : length - 1;
    if (arguments.length < 3) {
    memo = obj[keys ? keys[index] : index];
    index += dir;
    }
    return iterator(obj, iteratee, memo, keys, index, length);
    };
    }
    _.reduce = _.foldl = _.inject = createReduce(1);
    _.reduceRight = _.foldr = createReduce(-1);


    //predicate为一个函数,遍历数组,若return true直接返回。里面用到一个 _.findIndex函数,先研究下。研究在下面。
    //_.findKey也差不多。最终函数返回obj[key];

    _.find = _.detect = function(obj, predicate, context) {
    var key;
    if (isArrayLike(obj)) {
    key = _.findIndex(obj, predicate, context);
    } else {
    key = _.findKey(obj, predicate, context);
    }
    if (key !== void 0 && key !== -1) return obj[key];
    };
    //这个函数就是index搜索器,dir表示正者搜还是反着搜,搜到predicate函数返回的是ture的时候就把索引返回的,都没搜到就返回-1;
    function createIndexFinder(dir) {
    return function(array, predicate, context) {
    predicate = cb(predicate, context);
    var length = array != null && array.length;
    var index = dir > 0 ? 0 : length - 1;
    for (; index >= 0 && index < length; index += dir) {
    if (predicate(array[index], index, array)) return index;
    }
    return -1;
    };
    }

    _.findIndex = createIndexFinder(1);
    //和前面那个差不多,不过返回键名
    _.findKey = function(obj, predicate, context) {
    predicate = cb(predicate, context);
    var keys = _.keys(obj), key;
    for (var i = 0, length = keys.length; i < length; i++) {
    key = keys[i];
    if (predicate(obj[key], key, obj)) return key;
    }
    };

    //把predicate返回为true的堆到一个新数组,再返回这个新数组
    _.filter = _.select = function(obj, predicate, context) {
    var results = [];
    predicate = cb(predicate, context);
    _.each(obj, function(value, index, list) {
    if (predicate(value, index, list)) results.push(value);
    });
    return results;
    };


    //扩展自己,利用attrs类型的json,调用传入一个对象
    _.matcher = _.matches = function(attrs) {
    attrs = _.extendOwn({}, attrs);
    return function(obj) {
    return _.isMatch(obj, attrs);
    };
    };

    //传对象和attrs,attrs类似{"a":1,"b":2}这种json的东西,若json类似的存在于obj中,则返回true,否则false;
    _.isMatch = function(object, attrs) {
    var keys = _.keys(attrs), length = keys.length;
    if (object == null) return !length;
    var obj = Object(object);
    for (var i = 0; i < length; i++) {
    var key = keys[i];
    if (attrs[key] !== obj[key] || !(key in obj)) return false;
    }
    return true;
    };
    //遍历obj中的每一个值,返回一个数组,这个数组包含properties所列出的属性的所有的键值对。
    //这里的obj是类[{},{},{}]的东西
    _.where = function(obj, attrs) {
    return _.filter(obj, _.matcher(attrs));
    };
     
    //全部obj的元素分别调用predicate,有一个返回值是false,整个返回false
    //否则返回true
    _.every = _.all = function(obj, predicate, context) {
    predicate = cb(predicate, context);
    var keys = !isArrayLike(obj) && _.keys(obj),
    length = (keys || obj).length;
    for (var index = 0; index < length; index++) {
    var currentKey = keys ? keys[index] : index;
    if (!predicate(obj[currentKey], currentKey, obj)) return false;
    }
    return true;
    };


    //全部obj的元素分别调用predicate,有一个返回值是true,整个返回false
    //否则返回false
    _.some = _.any = function(obj, predicate, context) {
    predicate = cb(predicate, context);
    var keys = !isArrayLike(obj) && _.keys(obj),
    length = (keys || obj).length;
    for (var index = 0; index < length; index++) {
    var currentKey = keys ? keys[index] : index;
    if (predicate(obj[currentKey], currentKey, obj)) return true;
    }
    return false;
    };
    //检查obj是否包含target,就是indexOf方法的调用。
    _.contains = _.includes = _.include = function(obj, target, fromIndex) {
    if (!isArrayLike(obj)) obj = _.values(obj);
    return _.indexOf(obj, target, typeof fromIndex == 'number' && fromIndex) >= 0;
    };


    //对每个obj元素调用method,注意method方法的获取,例子用了[]['sort'],获取到sort函数引用
    //args,是传给method的参数,可选。
    _.invoke = function(obj, method) {
    var args = slice.call(arguments, 2);
    var isFunc = _.isFunction(method);
    return _.map(obj, function(value) {
    var func = isFunc ? method : value[method];
    return func == null ? func : func.apply(value, args);
    });
    };
    _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');


    //传key,获得值组成的数组。
    _.pluck = function(obj, key) {
    return _.map(obj, _.property(key));
    };
    var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 50}, {name: 'curly', age: 60}];
    _.pluck(stooges, 'name');
    //结果["moe", "larry", "curly"]


    //普通的数组且没有传iteratee的情况下,直接用交换机的方式比较最大值
    //传了迭代函数的话,一般obj是[{a:1},{a:2},{a:3}],迭代函数把各个对象的相应键的值取出来比较。再返回最大值
    _.max = function(obj, iteratee, context) {
    var result = -Infinity, lastComputed = -Infinity,
    value, computed;
    if (iteratee == null && obj != null) {
    obj = isArrayLike(obj) ? obj : _.values(obj);
    for (var i = 0, length = obj.length; i < length; i++) {
    value = obj[i];
    if (value > result) {
    result = value;
    }
    }
    } else {
    iteratee = cb(iteratee, context);
    _.each(obj, function(value, index, list) {
    computed = iteratee(value, index, list);
    if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
    result = value;
    lastComputed = computed;
    }
    });
    }
    return result;
    };

    //用 Fisher-Yates shuffle算法,产生随机排序数组
    //https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
    _.shuffle = function(obj) {
    var set = isArrayLike(obj) ? obj : _.values(obj);
    var length = set.length;
    var shuffled = Array(length);
    for (var index = 0, rand; index < length; index++) {
    rand = _.random(0, index);
    //这里用交换机,rand<index的时候,把shuffled【rand】索引的值放到shuffled【index】索引上
    if (rand !== index) shuffled[index] = shuffled[rand];
    shuffled[rand] = set[index];
    }
    return shuffled;
    };

    //guard默认是1,n==null的时候,默认随机返回一个obj数组元素
    //n有值的时候,返回n个元素
    _.sample = function(obj, n, guard) {
    if (n == null || guard) {
    if (!isArrayLike(obj)) obj = _.values(obj);
    return obj[_.random(obj.length - 1)];
    }
    return _.shuffle(obj).slice(0, Math.max(0, n));
    };

    //一般用来转换nodelist为真正的数组,可以调用原生的数组方法。
    _.toArray = function(obj) {
    if (!obj) return [];
    if (_.isArray(obj)) return slice.call(obj);
    if (isArrayLike(obj)) return _.map(obj, _.identity);
    return _.values(obj);
    };

    //数组的长度或者对象键集合的长度。
    _.size = function(obj) {
    if (obj == null) return 0;
    return isArrayLike(obj) ? obj.length : _.keys(obj).length;
    };
    //obj每个元素分别调用predicate,返回true的扔到pass数组,false的扔到fail数组
    _.partition = function(obj, predicate, context) {
    predicate = cb(predicate, context);
    var pass = [], fail = [];
    _.each(obj, function(value, key, obj) {
    (predicate(value, key, obj) ? pass : fail).push(value);
    });
    return [pass, fail];
    };
    集合的函数差不多就这些,其实还有4个函数,觉得用不太到还挺难就不写了,哈哈哈哈哈
  • 相关阅读:
    sql server将多条数据,通过指定列拼接成一条数据
    sql server游标demo
    C# 使用HttpCilent请求接口,传递表单数据(可上传图片)
    sql server 把日期时间类型 转为字符串
    Http请求失败,获取返回状态码和消息
    url
    解决基础连接已经关闭: 未能为 SSL/TLS 安全通道建立信任关系。
    MD5 加密
    C# 读取txt文件内容
    微信小程序 图片转为base64
  • 原文地址:https://www.cnblogs.com/wz0107/p/4969528.html
Copyright © 2011-2022 走看看