underscore.js 源碼分析之 _.each() 函數

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] , iterateealert , context 沒有瀏覽器

iteratee = optimizeCb(iteratee, context);

首先是調用optimizeCb()函數函數

分析 optimizeCb 源碼spa

var optimizeCb = function(func, context, argCount) {
    if (context === void 0) return func;
};

由於context 沒有,因此這裏直接返回alert , 因此 iteratee 如今是alertprototype

再使用 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

而後若是瀏覽器支持 ES5Object.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() ,該簡直pushkeys[]

// 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 的源碼,感受不是太難,一個函數一個函數分析就好。

相關文章
相關標籤/搜索