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个函数,觉得用不太到还挺难就不写了,哈哈哈哈哈
原文地址:https://www.cnblogs.com/wz0107/p/4969528.html