对的,如你所见,又开始造轮子了。哈哈,造轮子咱们是认真的~css
源码阅读是必须的,Underscore是由于刚刚学习整理了一波函数式编程,加上本身曾经没有太多阅读源码的经验,先拿Underscore练练手,跟着前辈们走一走,学一学。也相同时可以夯实js基础,从源码中学习到更多的编码技巧java
Underscore源码阅读大体按照官方文档来编写.尽可能的说明每个函数的写法,但愿本身能够从中能够收获大神的编码功力。node
Underscore源码+注释地址github
(function(){}())
复制代码
常规操做哈,跟jQuery一毛同样,经过IIFE来包裹业务逻辑,目的简单:一、避免全局污染。二、保护隐私编程
var root = typeof self == 'object' && self.self === self && self ||
typeof global == 'object' && global.global === global && global ||
this ||
{};
var previousUnderscore = root._;
复制代码
经过global和self来判断是node环境仍是window环境,说白了,就是为了拿到全局变量。由于咱们须要一个全局的变量_,因此为了防止冲突,咱们这里拿到root后,先暂存下以前的root._设计模式
var ArrayProto = Array.prototype, ObjProto = Object.prototype;
var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;
var push = ArrayProto.push,
slice = ArrayProto.slice,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
var nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeCreate = Object.create;
var Ctor = function(){};
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
if (typeof exports != 'undefined' && !exports.nodeType) {
if (typeof module != 'undefined' && !module.nodeType && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}
_.VERSION = '1.8.3';
复制代码
因为Underscore自己依赖不少原生js的方法,因此这里为了不原型链的查找性能消耗,Underscore经过局部变量来保存一些经常使用的对象和方法。既能够提高性能,减小对象成员访问深度也能够减小代码的冗长。数组
下面的Ctor和_ 是为了面向对象而准备的。浏览器
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 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);
};
};
var builtinIteratee;
var cb = function(value, context, argCount) {
if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);
if (value == null) return _.identity;
if (_.isFunction(value)) return optimizeCb(value, context, argCount);
if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);
return _.property(value);
};
_.iteratee = builtinIteratee = function(value, context) {
return cb(value, context, Infinity);
};
复制代码
这里的迭代,咱们须要理清楚一个概念,在Underscore中,咱们须要改变那种命令式的编程方式,具体的能够看我以前写的关于函数式编程的文章哈。bash
因此这里想说的就是关于遍历迭代的东西。
var results = _.map([1,2,3],function(elem){
return elem*2;
}); // => [2,4,6]
_.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;
};
复制代码
咱们传递给的 _.map 的第二个参数就是一个 iteratee,他多是函数,对象,甚至是字符串。
在上面的分析中,咱们知道,当传入的 value 是一个函数时,value 还要通过一个叫 optimizeCb 的内置函数才能得到最终的 iteratee:
var cb = function (value, context, argCount) {
// ...
if (_.isFunction(value)) return optimizeCb(value, context, argCount);
// ...
};
复制代码
因此此处的optimizeCb必然是优化回调的做用了。
// 优化回调的函数,遍历
var optimizeCb = function(func, context, argCount) {
// void 0 会返回真正的undefined 此处确保上下文的存在
if (context === void 0) return func;
switch (argCount == null ? 3 : argCount) {
case 1: return function(value) {
// argCount为0时候,迭代过程当中,咱们只须要这个value就能够了
return func.call(context, value);
};
// The 2-parameter case has been omitted only because no current consumers
// 3个参数(值,索引,被迭代集合对象).
case 3: return function(value, index, collection) {
return func.call(context, value, index, collection);
};
// 4个参数(累加器(好比reducer须要的), 值, 索引, 被迭代集合对象)
case 4: return function(accumulator, value, index, collection) {
return func.call(context, accumulator, value, index, collection);
};
}
return function() {
return func.apply(context, arguments);
};
};
复制代码
总体的代码很是清晰,待优化的回调函数func,上下文context以及迭代回调须要的参数个数。
上面的这个优化的回调涉及到不一样地方使用的不一样迭代。这里暂时 先放一放。等过了一遍源码后,看到每个用到迭代的地方,在回头来看,就会明白不少。
在 ES6中,咱们定义不定参方法的时候能够这么写
let a = (b,...c)=>{
console.log(b,c);
}
复制代码
可是在此以前,Underscore实现了本身的reset,使用以下:
function a(a,b,c,d,e){
console.log(a,b,c,d,e)
}
let aa = restArgs(a);//let aa = restArgs(a,4)
aa(1,2,3,4,5,6,7,8,8)
复制代码
看下restArgs的实现:
var restArgs = function(func, startIndex) {
//未传则取形参个数减一
startIndex = startIndex == null ? func.length - 1 : +startIndex;
return function() {
// 多传了几个参数
//length为多传了几个参数
var length = Math.max(arguments.length - startIndex, 0),
rest = Array(length),
index = 0;
for (; index < length; index++) {
rest[index] = arguments[index + startIndex];
}
//优化。注意rest参数老是最后一个参数, 不然会有歧义
switch (startIndex) {
case 0: return func.call(this, rest);
case 1: return func.call(this, arguments[0], rest);
case 2: return func.call(this, arguments[0], arguments[1], rest);
}
//撇去经常使用的startIndex,这里循环
//先拿到前面参数
var args = Array(startIndex + 1);
for (index = 0; index < startIndex; index++) {
args[index] = arguments[index];
}
//拿到后面的数组
args[startIndex] = rest;
return func.apply(this, args);
};
};
复制代码
关于面向对象,这里不作过多解释了,能够参考个人另外一篇文章: javasript设计模式之面向对象。
咱们直接看他的继承实现吧
var Ctor = function(){};
// 定义了一个用于继承的内部方法
var baseCreate = function(prototype) {
if (!_.isObject(prototype)) return {};
// nativeCreate = Object.create;
if (nativeCreate) return nativeCreate(prototype);
Ctor.prototype = prototype;
var result = new Ctor;
Ctor.prototype = null;
return result;
};
复制代码
es5 中,咱们有一种建立对象的方式,Object.create 。
function Animal(name){
this.name = name;
}
Animal.prototype.eat = function(){
console.log(this.name,'鸟为食亡');
}
var dog = Object.create(Animal.prototype);
dog.name = "毛毛";
dog.eat();
复制代码
ok,大概从上你们就看出来create的做用了。
baseCrate中,首先判断是否为对象,不然退出。浏览器能力检测是否具有Object.create方法,具有则用。不然采用寄生式继承建立对象。须要注意的是,baseCreate仅仅支持原型继承,而不能像Object.create那样传递属性列表。
开篇简单的介绍Collection Functions上面的代码部分。在介绍Collection Function每一个方法实现以前,咱们将在下一篇看一下一些工具方法的编写方式。
的确在造造轮子,只是更想本身撸一遍优秀代码。