underscoreもlodashも詳しくないので、勉強半分でソースコードを読む。
利用するバージョン
foreachとは
配列/連想配列 問わずに利用できる繰り返し処理を行える関数。
たとえば
'use strict';
var _ = require( "underscore" );
var arr = [ 1, 2, 3, 4, 5 ];
_.each( arr, function(element, index, array) {
console.log( 'element : ' + element + ' index: ' + index );
});
した場合も
'use strict';
var _ = require( "underscore" );
var arr = { 1: 1, 2: 2, 3: 3, 4: 4, 5: 5 };
_.each( arr, function(element, index, array) {
console.log( 'element :' + element + ' index:' + index );
});
した場合も同じように
element : 1 index: 1
element : 2 index: 2
element : 3 index: 3
element : 4 index: 4
element : 5 index: 5
という返り値が期待できます。
素で書いた場合
var arr = [ 1, 2, 3, 4, 5 ];
for( var i = 0; i < arr.length; i++ ) {
console.log( 'element:' + arr[i] + ' index:' + i );
}
とか
var arr = { 1: 1, 2: 2, 3: 3, 4: 4, 5: 5 };
for( var key in arr ) {
console.log( 'element:' + arr[key] + ' index:' + key );
}
こんな感じ
比較
underscore.each
コードはこのへん。
_.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;
};
underscoreの場合は、objectかarrayかを判定し、for文で処理をしている。
配列の場合はif文、配列ではない場合はelseで処理。
ただのobjectの場合は_.keysを用いて処理。
中で使われているisArrayLikeは以下
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;
};
その中で使われているproperty( 'length' )はこれ
var property = function(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
};
//このあたりからよくわからない
objがnullだったら、void 0を返す
objectだったらobj[key]を返す
今回 'length' を与えているので
getLength = function( obj ) {
return obj == null ? void 0 : obj['length'];
}
となって、void 0か配列のlengthを返している
(こういう書き方があるのか・・・)
なので、
var length = getLength(collection);
return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
ではgetLengthで返ってきた値が正常な範囲のnumberだったらArrayであるとみなしている(と思う)
配列か配列じゃないかで分岐したあとはoptimizeCbしたiterateeを回している
lodash.forEach
コードはこのあたり。underscoreは1ファイルだったがlodashは分割されている。
あとlodashの場合は_.eachではなく関数的には_.forEach
(_.eachにもエイリアス貼られている)
_.forEach({ 'a': 1, 'b': 2 }, function(n, key) {
console.log(n, key);
});
var arrayEach = require('lodash._arrayeach'),
baseEach = require('lodash._baseeach'),
bindCallback = require('lodash._bindcallback'),
isArray = require('lodash.isarray');
function createForEach(arrayFunc, eachFunc) {
return function(collection, iteratee, thisArg) {
return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
? arrayFunc(collection, iteratee)
: eachFunc(collection, bindCallback(iteratee, thisArg, 3));
};
}
var forEach = createForEach(arrayEach, baseEach);
module.exports = forEach;
起点となるのは createForEach。
(そもそも構造自体がunderscoreと違うけど)
配列/それ以外のチェックの際に
iterateeのtypeofのチェックがあるのがunderscoreと違うっぽさがある
_.isArray
で配列かどうかのチェック
_.arrayeach
function arrayEach(array, iteratee) {
var index = -1,
length = array.length;
while (++index < length) {
if (iteratee(array[index], index, array) === false) {
break;
}
}
return array;
}
_.bindcallback
function bindCallback(func, thisArg, argCount) {
if (typeof func != 'function') {
return identity;
}
if (thisArg === undefined) {
return func;
}
switch (argCount) {
case 1: return function(value) {
return func.call(thisArg, value);
};
case 3: return function(value, index, collection) {
return func.call(thisArg, value, index, collection);
};
case 4: return function(accumulator, value, index, collection) {
return func.call(thisArg, accumulator, value, index, collection);
};
case 5: return function(value, other, key, object, source) {
return func.call(thisArg, value, other, key, object, source);
};
}
return function() {
return func.apply(thisArg, arguments);
};
}
function identity(value) {
return value;
}
・・長くなりそうなので明日に続く・・・