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个函数,以为用不太到还挺难就不写了,哈哈哈哈哈