each _.each(list, iteratee, [context])
Alias: forEach
遍歷list中的全部元素,按順序用遍歷輸出每一個元素。若是傳遞了context參數,則把iteratee綁定到context對象上。每次調用iteratee都會傳遞三個參數:(element, index, list)。若是list是個JavaScript對象,iteratee的參數是 (value, key, list))。返回list以方便鏈式調用。javascript
_.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
源碼java
// 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
源碼spa
var optimizeCb = function(func, context, argCount) { if (context === void 0) return func; };
由於context
沒有,因此這裏直接返回alert
, 因此 iteratee
如今是alert
prototype
再使用 isArrayLike
判斷 傳進來的obj
是否是數組,判斷數組的方法code
//原理就是經過判斷它是否具備長度且長度大於0且小於MAX_ARRAY_INDEX var 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
屬性的three
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) // true console.log(true || false && true) // true
而後若是瀏覽器支持 ES5
的 Object.keys
方法,就優先使用
nativeKeys = Object.keys,
不支持就繼續遍歷循環 對象
var keys = []; // own enumerable properties for (var key in obj) // hasOwnProperty if (_.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
的源碼,感受不是太難,一個函數一個函數分析就好。