0.1.0
isEqual : function(a, b) {
// Check object identity.
if (a === b) return true;
// Different types?
// 如果不是一个类型则直接返回false
var atype = typeof(a), btype = typeof(b);
if (atype != btype) return false;
// Basic equality test (watch out for coercions).
if (a == b) return true;
// One of them implements an isEqual()?
if (a.isEqual) return a.isEqual(b);
// If a is not an object by this point, we can't handle it.
if (atype !== 'object') return false;
// Nothing else worked, deep compare the contents.
var aKeys = _.keys(a), bKeys = _.keys(b);
// Different object sizes?
if (aKeys.length != bKeys.length) return false;
// Recursive comparison of contents.
for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
return true;
}
1.0.0
isEqual = function(a, b) {
// Check object identity.
if (a === b) return true;
// Different types?
var atype = typeof(a), btype = typeof(b);
if (atype != btype) return false;
// Basic equality test (watch out for coercions).
if (a == b) return true;
// One is falsy and the other truthy.
if ((!a && b) || (a && !b)) return false;
// One of them implements an isEqual()?
if (a.isEqual) return a.isEqual(b);
// Check dates' integer values.
if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
// Both are NaN?
if (_.isNaN(a) && _.isNaN(b)) return true;
// Compare regular expressions.
if (_.isRegExp(a) && _.isRegExp(b))
return a.source === b.source &&
a.global === b.global &&
a.ignoreCase === b.ignoreCase &&
a.multiline === b.multiline;
// If a is not an object by this point, we can't handle it.
if (atype !== 'object') return false;
// Check for different array lengths before comparing contents.
if (a.length && (a.length !== b.length)) return false;
// Nothing else worked, deep compare the contents.
var aKeys = _.keys(a), bKeys = _.keys(b);
// Different object sizes?
if (aKeys.length != bKeys.length) return false;
// Recursive comparison of contents.
for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
return true;
}
isEmpty(判断数组or对象是否为空)实现思路数组利用length来判断,对象遍历来判断
isEmpty = function(obj) {
if (_.isArray(obj)) return obj.length === 0;
for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
return true;
}
接着上一篇,继续分析,显然underscore0.1.0版本,还不是最优的版本,也不能去覆盖更多的使用场景,所以必然会去继续优化。所以从0.2.0开始看,但是当自己去总结的时候如果一个版本一个版本去总结分析的话,也不合理,因为有些小版本只是添加了一个方法,or修改了某个方法的兼容性,索性从0.2.0一直到1.0.0这中间的版本合到一起来分析,学下作者当时的优化思路及新增的方法是为了哪些业务场景;
总结下0.2.0版本到1.0.0版本之间,作者主要新增及优化了哪些内容,如下所示:
一、基础设置
使用root变量来保存当前的全局对象,root = this; 保证浏览器端与服务端(node)端的兼容性
使用previousUnderscore保存了underscore加载完之前的全局对象上的变量,previousUnderscore = root.
使用了breaker变量来替换中断循环字符串'break',breaker = typeof StopIteration !== 'undefined' ? StopIteration : 'break'; 查了下资料StopIteration这个方法大部分浏览器不支持;
使用ArrayProto,ObjProto分别保存了数组的原型即对象原型 ArrayProto = Array.prototype, ObjProto = Object.prototype
使用变量保存了一些数组or对象原型上的方法
把定义成了一个函数var = function(obj) { return new wrapper(obj); };便于支持面向对象的写法
适配commonJs if (typeof exports !== 'undefined') exports. = ;
暴露underscore全局函数 root. = ; 注意0.1.0里面直接是window._ = {}来定义的哦
二、集合方法
each方法进行了优化,优化如下1.判断条件,2.去掉了each方法支持的条件,3.变成了一个单独的函数表达式而不是直接定义在underscore上面, 4.改变了遍历对象时传入回调内的参数格式,这样优化的好处了保证遍历数组or对象,回调函数参数的一致性,代码更健壮,即向ECMASCRIPT标准靠拢;
这里有一点需要提下的是,遍历为什么要放到try catch内,原因是forEach遍历是无法中断的,而要想中断forEach的话我们可以借助throw关键字,这个关键字是可以中断当前程序的执行,如果是在try catch内的话,在catch中是能够捕获到throw抛出的异常的,这样既能够中断循环又不会让整个程序抛出异常,如果不放在try catch内而是直接throw抛异常,则会直接中断整个代码的执行
inject方法改名为reduce方法,并增加了一个判断语句,及增加了一个传入回调函数的参数,整体实现不变
reduceRight(与reduce的用法一致,只是从集合后面开始进行操作)
select方法更名为filter,实现原理基本不变
all方法改成了every方法,实现原理不变,不过这里把之前的默认函数抽出去了
any方法换成some方法,实现原理基本不变
include优化了写法
pluck优化了内部实现方式,将内部的each遍历,改成了map遍历,因为map方法本身就返回一个数组,从而不需要在去定义与返回数组,简化代码
max方法优化了内部实现
min方法优化了内部实现,与max方法同理
toArray优化了内部实现,增加了对自身支持toArray方法及arguments,对象的转换方式
三、数组方法
first方法进行了拓展,允许传入第二个参数及第三个参数,第二个参数用来获取数组前n个元素,第三个参数用来直接获取数组的第一个元素
rest(用于获取传入index之后的数组元素)
lastIndexOf(从右至左查找数组内是否有某个元素,有则返回index,没有则返回-1)实现思路如果支持lastIndexOf,则直接调用lastIndexOf,反之从arr,length开始遍历,而不是0开始遍历
range(按某个固定值,返回包含start到stop之间的值的数组)实现思路判断传入参数的个数,然后判断起始值,结束值,确定遍历的length,最后遍历
四、函数方法
bind优化了实现方式,这里优化只是实现更合理一些,当传入的上下文对象为空时,不是直接返回func而是传一个空给上下文对象,含糊调用的时候上下文对象就是{}对象
bindAll优化了参数处理的方式
delay、defer优化了内部获取参数的方式
compose(允许按顺序调用函数队列(从后往前调),并获取所有函数调用完之后的返回值)
五、对象方法
keys优化内部实现,实现思路是先判断是否支持元素的keys事件,如果不支持则,先判断是否是数组,如果是数组则,取数组内的所有元素,并返回,如果是对象则使用for in遍历,加一层hasOwnProperty判断
values优化内部实现,因为map返回值为函数,所以直接调用map方法即可
functions(获取对象内的方法,并按顺序返回一个包含改对象内所有方法的数组)
extend优化获取参数的方式
clone拓展了内部功能
isEqual优化内部判断方式及拓展了判断支持的范围
isEmpty(判断数组or对象是否为空)实现思路数组利用length来判断,对象遍历来判断
isArray(判断是否是数组)实现思路是先判断是否支持isArray方法,不支持则直接判断传入的数据是否有数组原型上特有的方法
isArguments(判断是否是arguments)实现思路是判断length属性是否为number类型,是否有数组原型上的concat方法,字符串上的substr方法,函数的apply方法及对象的length属性是否可枚举从而判断是原型上的属性还是对象上的属性
isFunction(判断是否是函数)优化了内部实现,之所以不使用typeof来直接判断是因为typeof在检查正则时,在不同的浏览器下也会检查为function,如Chrome 1-12中,所以使用constructor属性及原型上的call与apply方法来进行判断
isString(判断是否是字符串)通过判断空字符及是否具有原型上的charCodeAt及substr方法来进行判断
isNumber(判断是否是数字)通过判断是否等于自身,这里使用了一个+,这个+的作用是可以进行隐式转化,可以将其它类型的值转换为number类型的值,类似于parseInt但是比parseInt更强大,另外就是直接使用Object.proptotype.toString.call()方法来进行判断
isBoolean(判断是否是布尔值)通过判断true和false来进行判断
isDate(判断是否是日期类型)通过判断是否有原型上的getTimezoneOffset 与setUTCFullYear方法来进行判断
isRegExp (判断是否是正则)通过判断是否有原型上的test、exec方法及是否使用了i标志,ignoreCase 属性表明正则表达式是否使用了 "i" 标志,使用了则表示true,没使用则表示false
isNaN(判断是否是NaN)
isNull(判断是否是null)
六、工具方法
noConflict(避免产生冲突)实现的思路是先保存underscore加载之前的_变量值,在调用noConflict()方法的时候,在将之前保存的值赋给全局对象下的_属性,最后返回this
ko = noConflict();然后直接使用ko代替_来调用方法,其它不变
identity (返回自身)抽出来的一个公共函数
times(调用指定的函数n次)实现思路就是一个for循环
breakLoop(中断循环)抽出来的一个公共函数,breaker是基础设置那里定义的一个变量
mixin(混合方法)实现思路就是一个简单的遍历,用于将wrapper原型上绑定上underscore上的静态方法
uniqueId优化了内部实现在0.1.0中是包值赋给了_上的idCounter属性,而在1.0.0中是直接定义了idCounter这个变量,利用的是闭包的特性
template优化了内部实现
六、别名方法
七、OOP设置
定义wrapper函数用于支持面向对象的写法
定义实现链式调用方法
定义将wrapper原型添加上underscore上的静态方法
.mixin(); //将underscore上的静态方法混合到wrapper的原型上
链式调用的入口函数
链式调用之后的取值函数
资源链接及参考链接 https://github.com/jashkenas/underscore/tree/1.0.0 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/typeof https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase