4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

underscoreとlodashの比較(foreach)

Last updated at Posted at 2015-12-01

underscoreもlodashも詳しくないので、勉強半分でソースコードを読む。

利用するバージョン

underscore.js(v1.8.3)

lodash.js(v3.10.0)

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

コードはこのへん。

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にもエイリアス貼られている)

lodash.forEach
_.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;
}

・・長くなりそうなので明日に続く・・・

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?