each _.each(list, iteratee, [context])
Alias: forEach
_.each([1, 2, 3], alert);=> alerts each number in turn..._.each({one: 1, two: 2, three: 3}, alert);=> alerts each number value in turn...
_.each
源码
// The cornerstone, an `each` implementation, aka `forEach`. // Handles raw objects in addition to array-likes. Treats all // sparse array-likes as if they were dense. _.each = _.forEach = function(obj, iteratee, context) { 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; };
分析
_.each([1, 2, 3], alert); // _.each()使用方法,其中`alert`可以换成自己写的function
obj
是[1, 2, 3]
, iteratee
是alert
, context
没有
iteratee = optimizeCb(iteratee, context);
首先是调用optimizeCb()
函数
分析 optimizeCb
源码
var optimizeCb = function(func, context, argCount) { if (context === void 0) return func;};
因为context
没有,所以这里直接返回alert
, 所以 iteratee
现在是alert
再使用 isArrayLike
判断 传进来的obj
是不是数组,判断数组的方法
//原理就是通过判断它是否具有长度且长度大于0且小于MAX_ARRAY_INDEXvar MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;var getLength = property('length');var isArrayLike = function(collection) { var length = getLength(collection); return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX; };
因为这里是数组,所以继续
// 通过 for 循环来遍历数组里面每一个值,传给 iteratee 函数来运行for (i = 0, length = obj.length; i < length; i++) { iteratee(obj[i], i, obj); // 等于 alert(obj[i], i, obj);}
如果不是数组呢?对象默认是没有length
属性的
var keys = _.keys(obj);for (i = 0, length = keys.length; i < length; i++) { iteratee(obj[keys[i]], keys[i], obj);}
使用了_.keys()
函数,下面分析 _keys()
源码
_.keys = function(obj) { if (!_.isObject(obj)) return []; if (nativeKeys) return nativeKeys(obj); var keys = []; for (var key in obj) if (_.has(obj, key)) keys.push(key); // Ahem, IE < 9. if (hasEnumBug) collectNonEnumProps(obj, keys); return keys;};
首先使用_.isObject
函数判断是不是对象
// Is a given variable an object? _.isObject = function(obj) { var type = typeof obj; return type === 'function' || type === 'object' && !!obj; };
首先判断传进来的参数的类型,然后返回true
或者false
注意,通过以下两个例子可知, &&
的优先级比 ||
的高
console.log(true || false && false) // trueconsole.log(true || false && true) // true
然后如果浏览器支持 ES5
的 Object.keys
方法,就优先使用
nativeKeys = Object.keys,
不支持就继续遍历循环 对象
var keys = [];// own enumerable propertiesfor (var key in obj)// hasOwnPropertyif (_.has(obj, key)) keys.push(key);
使用了 _.has()
函数,下面解析_.has()
函数
// Shortcut function for checking if an object has a given property directly// on itself (in other words, not on a prototype)._.has = function(obj, key) { return obj != null && hasOwnProperty.call(obj, key);};
通过判断 传进来的obj
是否为null
并且调用hasOwnProperty
方法判断该对象是否有该键值
hasOwnProperty = ObjProto.hasOwnProperty = Object.prototype.hasOwnProperty;
如果有该属性,返回true
,这时回到_.keys()
,该简直push
到keys[]
内
// IE9以下不能用 for in 来遍历,所以使用collectNonEnumProps()函数来解决问题,暂时可以不看if (hasEnumBug) collectNonEnumProps(obj, keys);
这时已经把对象转化成数组了,回到_.each()
函数,继续使用for
循环遍历数组 ,把参数传递给alert
函数
for (i = 0, length = keys.length; i < length; i++) { iteratee(obj[keys[i]], keys[i], obj); // 等于 alert(obj[keys[i]], keys[i], obj);}
最后再返回obj
整个分析_.each()
函数,相继分析了_.has()
和_.keys()
函数,大概看了一下underscore.js
的源码,感觉不是太难,一个函数一个函数分析就好。