前言
这篇文章是为之后的underscore现版本的源码做铺垫,先感受下最先版本
- 0.1.0版本足够小
- 这个版本已经有将近小10年的历史了
- 还是有一些不错的地方。
0.1.0版本源码分析
// Underscore.js
// (c) 2009 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the terms of the MIT license.
// Portions of Underscore are inspired by or borrowed from Prototype.js,
// Oliver Steele's Functional, And John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore/
window._ = {
VERSION : '0.1.0',
/*------------------------ Collection Functions: ---------------------------*/
// 集合函数
// The cornerstone, an each implementation.
// Handles objects implementing forEach, each, arrays, and raw objects.
each : function(obj, iterator, context) {
var index = 0;
try {
if (obj.forEach) {
// 有forEach优先选择forEach
obj.forEach(iterator, context);
} else if (obj.length) {
// 使用自定义迭代器迭代
for (var i=0; i= result.computed) result = {value : value, computed : computed};
// 对初始化以及每一次判断computed大于当前值更新
});
return result.value;
},
// Return the minimum element (or element-based computation).
min : function(obj, iterator, context) {
if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
var result;
_.each(obj, function(value, index) {
var computed = iterator ? iterator.call(context, value, index) : value;
if (result == null || computed < result.computed) result = {value : value, computed : computed};
});
return result.value;
},
// Sort the object's values by a criteria produced by an iterator.
sortBy : function(obj, iterator, context) {
// 根据对象的一些值进行排序
// 首先我们只要关注每个函数的目的即可
// map一次遍历,返回多个对象数组。然后根据数组对象排序
// 并且对象均有值为criteria表示我们迭代函数的值(就是根据什么排序)
return _.pluck(_.map(obj, function(value, index) {
return {
value : value,
criteria : iterator.call(context, value, index)
};
}).sort(function(left, right) {
var a = left.criteria, b = right.criteria;
// 判断大于小于等于
return a < b ? -1 : a > b ? 1 : 0;
}), 'value');
// 外面的一层pluck是为了解开map函数的一层打包
},
// Use a comparator function to figure out at what index an object should
// be inserted so as to maintain order. Uses binary search.
// 这是利用二分查找吧
// 利用二分查找找出元素应该插入到哪个位置中
sortedIndex : function(array, obj, iterator) {
iterator = iterator || function(val) { return val; };
// 初始化一个迭代器
var low = 0, high = array.length;//不严谨直接去length
// 初始化高低位
//
while (low < high) {
var mid = (low + high) >> 1;
// 对中位取半(important快捷方法)
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
}
return low;
},
// Convert anything iterable into a real, live array.
// 转换数组(转换一切可迭代的)
toArray : function(iterable) {
if (!iterable) return []; //为假值直接返回[]
if (_.isArray(iterable)) return iterable; //判断是否为数组
return _.map(iterable, function(val){ return val; });//如果为对象的话,利用map转成数组
},
// Return the number of elements in an object.
// 返回对象的元素数量
size : function(obj) {
return _.toArray(obj).length;
},
/*-------------------------- Array Functions: ------------------------------*/
// Get the first element of an array.
// 返回数组第一个元素
first : function(array) {
return array[0];
},
// Get the last element of an array.
// 返回数组最后一个元素
last : function(array) {
return array[array.length - 1];
},
// Trim out all falsy values from an array.
//去除假值的数组元素
//需要传入一个操作函数 false值在两次取反会被去掉
compact : function(array) {
return _.select(array, function(value){ return !!value; });
},
// Return a completely flattened version of an array.
//多维数组返回一个一维数组
// 数组扁平化
// 开始展现出函数式编程的灵活性了
flatten : function(array) {
return _.inject(array, [], function(memo, value) {
// 这边如果还是数组的话进行一个递归的扁平
if (_.isArray(value)) return memo.concat(_.flatten(value));
memo.push(value);
return memo;
});
},
// Return a version of the array that does not contain the specified value(s).
// 对传入的数组进行筛选
// 这里有一个点,我们在传参形参定义了array
// 而在下面的地方我们可以直接使用array且slice截取arguments
without : function(array) {
var values = array.slice.call(arguments, 0);
return _.select(array, function(value){ return !_.include(values, value); });
},
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// 唯一的数组。有一个参数可以选择是否排序的数组,是的话会选择最快的算法
uniq : function(array, isSorted) {
return _.inject(array, [], function(memo, el, i) {
if (0 == i || (isSorted ? _.last(memo) != el : !_.include(memo, el))) memo.push(el);
return memo;
});
},
// Produce an array that contains every item shared between all the
// passed-in arrays.
// 筛选多个元素数组的相同值
intersect : function(array) {
var rest = _.toArray(arguments).slice(1);
// 获得其余多个参数
// 最外面肯定是一层筛选。
// 里面做筛选的条件
return _.select(_.uniq(array), function(item) {
return _.all(rest, function(other) {
// 目的在于取交集
// 所以我们使用外层的item 对比层的个个数组 据此我们返回同时存在多个数组中的元素
return _.indexOf(other, item) >= 0;
});
});
},
// Zip together multiple lists into a single array -- elements that share
// an index go together.
zip : function() {
var args = _.toArray(arguments);
var length = _.max(_.pluck(args, 'length'));
// 返回最大数组的长度。
var results = new Array(length);
// 创建一个存放点
for (var i=0; i提取替换
var fn = new Function('obj',
'var p=[],print=function(){p.push.apply(p,arguments);};' +
'with(obj){p.push(\'' +
str
.replace(/[\r\t\n]/g, " ")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'")
+ "');}return p.join('');");
return data ? fn(data) : fn;
}
};
总结
- 后面的模板实现挺亮眼。
- try catch 设计each的跳出。
-
>>
取半的快捷 - 函数的复用。(有部分也许是不高效)
- 整个版本时间很前。所以我们可以从中看到一些现代api的影子,也许是在现代api中看到它们的影子。
- 源码篇幅较少,加上注释也不过400行左右。整篇阅读下来也没有很大的障碍,就是有复用性相对较高,但是对着test文件看看测试用例也就好了~~~
可以通过这些地方联系我
我的博客
我的邮箱:[email protected]