LoginSignup
1
1

More than 5 years have passed since last update.

Underscore.jsソースコードリーディング

Last updated at Posted at 2017-11-26

バージョン: 1.8.3
参考文献: http://underscorejs.org/docs/underscore.html

読んだ感想

  • 前処理、実処理、後処理と分かれていて読みやすい
  • 1メソッドが短くて読みやすい

_.each(obj, iteratee, context)

  • サマリ
    • 別名を _.each = _.forEach で定義
    • 配列かそれ以外かで処理分岐
  // 別名として、`_.forEach` も定義
  _.each = _.forEach = function(obj, iteratee, context) {
    // TODO: `optimizeCb` のリーディング
    iteratee = optimizeCb(iteratee, context);
    var i, length;
        // 配列の場合
    if (isArrayLike(obj)) {
      // 配列長ループ
      for (i = 0, length = obj.length; i < length; i++) {
        // 第2引数として渡される関数を第1引数の各要素を引数として呼び出し
        iteratee(obj[i], i, obj);
      }
    // 配列でない場合
    } else {
      // 対象オブジェクトのプロパティ名を取得
      var keys = _.keys(obj);
      // プロパティ数分ループ
      for (i = 0, length = keys.length; i < length; i++) {
        // `iteratee` 呼び出し
        iteratee(obj[keys[i]], keys[i], obj);
      }
    }
    // `_.each` 対象オブジェクトをそのまま返す
    return obj;
  };

_.map(obj, iteratee, context)

  • サマリ
    • obj が「配列」か「それ以外のオブジェクト」かで軽く分岐
// 別名`_.collect` も定義
_.map = _.collect = function(obj, iteratee, context) {
  // `iteratee`のコンテキスト(`this`)設定
  iteratee = cb(iteratee, context);
  // オブジェクトのキー取得
  var keys = !isArrayLike(obj) && _.keys(obj),
      // オブジェクト長を取得
      length = (keys || obj).length,
      // 結果代入変数の初期化
      results = Array(length);
  // オブジェクト長分ループ
  for (var index = 0; index < length; index++) {
    // 結果の代入先が「プロパティ名」か「添え字」かを決定
    var currentKey = keys ? keys[index] : index;
    // `iteratee` を適用し結果を代入
    results[index] = iteratee(obj[currentKey], currentKey, obj);
  }
  // 結果返す
  return results;
};

_.reduce()

_.reduceRight()

_.find(obj, predicate, context)

  • サマリ
    • 実態は_findIndex_.findKey
    • 「配列」か「それ以外」かで分岐
// `_.detect` として別名定義
_.find = _.detect = function(obj, predicate, context) {
  var key;
  if (isArrayLike(obj)) {
    key = _.findIndex(obj, predicate, context);
  } else {
    key = _.findKey(obj, predicate, context);
  }
  // `predicate` な値が存在する場合、その値を返す
  if (key !== void 0 && key !== -1) return obj[key];
};

_.filter(obj, predicate, context)

  • サマリ
    • objのプロパティ(要素)に対してpredicate_.eachで適用する
// `_.select` として別名定義
_.filter = _.select = function(obj, predicate, context) {
   // 結果を初期化
   var results = [];
   // `predicate` のコンテキスト(`this`)設定
   predicate = cb(predicate, context);
   // `obj`ループ
   _.each(obj, function(value, index, list) {
     // `predicate`な値の場合、結果に積む
     if (predicate(value, index, list)) results.push(value);
   });
   // 結果を返す
   return results;
 };

_.reject(obj, predicate, context)

  • サマリ
    • _.negate()を使ってサクッと実装 js _.reject = function(obj, predicate, context) { // `_.nagate()`で`predicate`の否定を使ってサクッと実装 return _.filter(obj, _.negate(cb(predicate)), context); };

_.every(obj, predicate, context)

  • サマリ
    • predicateが成立しない場合、即時 return falseとする
    • メソッド末尾到達でtrueを返す
// `_.all`で別名定義
_.every = _.all = function(obj, predicate, context) {
  // `predicate`の`this`設定
  predicate = cb(predicate, context);
  // 配列でない場合、`obj`の全プロパティ名を取得
  var keys = !isArrayLike(obj) && _.keys(obj),
      // `obj`の長さを取得
      length = (keys || obj).length;
  // `obj`のプロパティ(要素)長分ループ
  for (var index = 0; index < length; index++) {
    // 対象キー(プロパティか添え字)を取得
    var currentKey = keys ? keys[index] : index;
    // `predicate()`が成立しない場合、即`false`を返し、メソッド終了
    if (!predicate(obj[currentKey], currentKey, obj)) return false;
  }
  // ここに来るときは`obj`全てのプロパティ値(要素)に対する`predicate`が`true`となる
  return true;
};

_.some(obj, predicate, context)

  • サマリ
    • predicateな値を発見した時点で即時 return true
    • メソッド末尾到達→predicateな値が存在しない→return false
// `_.any` として別名定義
_.some = _.any = function(obj, predicate, context) {
  // `predicate`の`this`設定
  predicate = cb(predicate, context);
  // `obj`が配列でない場合、プロパティ名を取得
  var keys = !isArrayLike(obj) && _.keys(obj),
  // `obj`のプロパティ(要素)長取得
  length = (keys || obj).length;
  // オブジェクト長分ループ
  for (var index = 0; index < length; index++) {
    // `predicate`確認用キー値を取得
    var currentKey = keys ? keys[index] : index;
    // `predicate`な値の場合、trueを返す
    if (predicate(obj[currentKey], currentKey, obj)) return true;
  }
  // `predicate`な値が存在しないので`false`を返す
  return false;
};

_.contains(obj, item, fromIndex, guard)

  • サマリ
    • 実態は_.indexOf()
    • _.indexOf()よ呼び出すために配列変換の前処理を記述している
// 別名定義
_.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
  // 後述の`_.indexOf`で判定実行しているので、配列でないオブジェクトは配列に変換
  if (!isArrayLike(obj)) obj = _.values(obj);
  // `fromIndex`の確定, `gurar`の記述理由は不明
  if (typeof fromIndex != 'number' || guard) fromIndex = 0;
  // 実処理
  return _.indexOf(obj, item, fromIndex) >= 0;
};

_.invoke(obj, method)

  • サマリ
    • 実態は_.map(obj, method)
    • method には functionstringを渡すことができる
      • functionの場合、method.apply(objの各プロパティ, slice.call(arguments, 2))を実行
      • stringの場合、obj.method.apply(objの各プロパティ, slice.call(arguments, 2))を実行
_.invoke = function(obj, method) {
  // `method`の引数となる第3引数を切り出す
  var args = slice.call(arguments, 2);
  var isFunc = _.isFunction(method);
  return _.map(obj, function(value) {
    // `method`が`function`の場合、`method(args)`を実行し、
    // `function`でない場合、`obj.method(args)`を実行するように変数代入
    var func = isFunc ? method : value[method];
    // `func`が`null`でない場合、`func`を適用する
    return func == null ? func : func.apply(value, args);
  });
};

_.pluck(obj, key)

  • サマリ
    • objkeyの値を返す。
    • 実態は_.map(obj, _.property(key))
_.pluck = function(obj, key) {
  // `_.property`で`key`の値を返す関数を定義し、`_.map`で`obj`に順次適用する
  return _.map(obj, _.property(key));
};

_.where(obj, attrs)

  • objからattrsをもつオブジェクトを返す
_.where = function(obj, attrs) {
  return _.filter(obj, _.matcher(attrs));
};

_.findWhere(obj, attrs)

  • objからattrsを持つ最初のオブジェクトを返す
_.findWhere = function(obj, attrs) {
  return _.find(obj, _.matcher(attrs));
};

_.max(obj, iteratee, context)

  • obj内の最大値を返す
  • iteraeeで比較値を指定できる
_.max = function(obj, iteratee, context) {
  var result = -Infinity, lastComputed = -Infinity,
      value, computed;
  // `iteratee`が存在しない場合
  if (iteratee == null && obj != null) {
    // 配列ならそのまま、そうでないならプロパティ値を抽出する
    obj = isArrayLike(obj) ? obj : _.values(obj);
    // 配列長(プロパティ長)分ループ
    for (var i = 0, length = obj.length; i < length; i++) {
      // 比較値`obj`から比較値を取得する
      value = obj[i];
      // 最大値を更新している場合
      if (value > result) {
        // 最大値を更新する
        result = value;
      }
    }
  // `iteratee`が存在する場合
  } else {
    // `iteratee`の`this`を設定する
    iteratee = cb(iteratee, context);
    _.each(obj, function(value, index, list) {
      // `iteratee`から比較値を取得する
      computed = iteratee(value, index, list);
      // 最大値の更新またはループ初回の場合
      if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
        // 最大値を更新する
        result = value;
        // 比較最大値を更新する
        lastComputed = computed;
      }
    });
  }
  // 最大値を返す
  return result;
};

_.min(obj, iteratee, context)

  • _.maxとほぼ同一なので省略

_.shuffle(obj)

_.shuffle = function(obj) {
  // 配列形式へ変換する
  var set = isArrayLike(obj) ? obj : _.values(obj);
  // 対象オブジェクト長を取得する
  var length = set.length;
  // 処理結果格納変数を初期化する
  var shuffled = Array(length);
  // オブジェクト長分ループ
  for (var index = 0, rand; index < length; index++) {
    // 0〜`index`の範囲で乱数を生成する
    rand = _.random(0, index);
    // 乱数が対象`index`でない場合、挿入位置にすでにある値を末尾に移動する
    if (rand !== index) shuffled[index] = shuffled[rand];
    // ランダムに代入する
    shuffled[rand] = set[index];
  }
  // 結果を返す
  return shuffled;
};

_.sample(obj, n, guard)

  • objからn子の要素をランダムに返す
_.sample = function(obj, n, guard) {
  // `n`を指定していない場合
  if (n == null || guard) {
    // `obj`を配列に変換する
    if (!isArrayLike(obj)) obj = _.values(obj);
    // `_.random`で乱数を生成し、`obj`の要素を1つ返す
    return obj[_.random(obj.length - 1)];
  }
  // `obj`をランダムに並び替え、`n`個返す
  return _.shuffle(obj).slice(0, Math.max(0, n));
};

_.sortBy(obj, iteratee, context) {

  • objを昇順ソートする
  • 比較値の導出にはiterateeを利用する
_.sortBy = function(obj, iteratee, context) {
  // `iteratee`内の`this`を`context`に設定する
  iteratee = cb(iteratee, context);
  // `obj`を`{value, index, criteria}`に変換する
  // `sort`を適用する
  // `_.pluck`で`value`(`obj`のプロパティ)を配列にして返す
  return _.pluck(_.map(obj, function(value, index, list) {
    return {
      value: value,
      index: index,
      criteria: iteratee(value, index, list)
    };
  }).sort(function(left, right) {
    // 比較値を取得する
    var a = left.criteria;
    var b = right.criteria;
    if (a !== b) {
      // `a`が`b`より大きい
      if (a > b || a === void 0) return 1;
      // `a`が`b`より小さい
      if (a < b || b === void 0) return -1;
    }
    return left.index - right.index;
  }), 'value');
};

_.groupBy(list, iteratee, [context])

  • iterateeの計算結果に基づき、listを分割する`
  • メイン処理はgroup()に記述している
var group = function(behavior) {
  return function(obj, iteratee, context) {
    // 結果代入変数を初期化する
    var result = {};
    // `this`などを設定する
    iteratee = cb(iteratee, context);
    // 実処理実行ループ
    _.each(obj, function(value, index) {
      // valueをkeyにマップする
      var key = iteratee(value, index, obj);
      // 求めた結果を`behavier`に渡すことで、実処理を実行する
      behavior(result, value, key);
    });
    // 結果を呼び出し元へ返す
    return result;
  };
};
_.groupBy = group(function(result, value, key) {
  // 同一`key`が存在する場合、配列追加する
  if (_.has(result, key)) result[key].push(value);
  // 同一`key`が存在しない場合、新規追加する
  else result[key] = [value];
});

_.indexBy

_.indexBy = group(function(result, value, key) {
  // `key`を`result`のプロパティ名として、`value`を代入する
  result[key] = value;
});

_.countBy

_.countBy = group(function(result, value, key) {
  // 結果に同一`key`が存在する場合、インクリメントする
  if (_.has(result, key)) result[key]++;
  // 初出現の`key`の場合、1から始まる
  else result[key] = 1;
});

_.toArray(obj)

  • objを配列に変換する
_.toArray = function(obj) {
  // `false`の場合、から配列を返す
  if (!obj) return [];
  // 配列の場合、配列コピーを返す
  if (_.isArray(obj)) return slice.call(obj);
  // 配列ライクの場合、`obj`の各プロパティを`_.map`で配列化して返す
  if (isArrayLike(obj)) return _.map(obj, _.identity);
  // `obj`プロパティ値を配列化して返す
  return _.values(obj);
};

_.size(obj)

  • objの長さを返す
_.size = function(obj) {
  // `null`の場合、0を返す
  if (obj == null) return 0;
  // 配列ライクの場合、`length`プロパティを返す
  // それ以外の場合、`obj`のキー数を返す
  return isArrayLike(obj) ? obj.length : _.keys(obj).length;
};

_.partition(obj, predicate, context)

- objの各要素値がpredicateか否かで配列に分割する

_.partition = function(obj, predicate, context) {
  predicate = cb(predicate, context);
  // 分割値代入先変数を初期化する
  var pass = [], fail = [];
  // `obj`のプロパティに`predicate`を適用し、分割する
  _.each(obj, function(value, key, obj) {
    // `predicate`であるか計算する → 結果に応じて`pass`か`fail`に積む
    (predicate(value, key, obj) ? pass : fail).push(value);
  });
  return [pass, fail];
};

_.first(array, n, guard)

  • arrayの先頭要素を返す

- nを与えた場合、先頭からn番目の要素までを返す

// 別名を定義する
_.first = _.head = _.take = function(array, n, guard) {
  if (array == null) return void 0;
  // `n`が指定されていない場合、`array`の先頭を返す
  if (n == null || guard) return array[0];
  // `array`の先頭から`n`番目までの要素を返す
  return _.initial(array, array.length - n);
};

_.initial(array, n, guard)

  • arrayの最後の要素を除いた破裂を返す
  • nを指定した場合は、arrayの先頭からarray.length-nまでを返す
_.initial = function(array, n, guard) {
  // `array`の先頭から`array.length-n`番目までを返す
  return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
};

_.last(array, n, guard)

  • array最後の要素を返す
  • nを指定した場合、最後から目までを返す
_.last = function(array, n, guard) {
  if (array == null) return void 0;
  // 最後の要素を返す
  if (n == null || guard) return array[array.length - 1];
  // 最後から`n`個目までの要素を返す
  // 実質`array.slice(array.length - n)`
  return _.rest(array, Math.max(0, array.length - n));
};

_.rest(array, n, guard)

  • arrayn以降の要素を全て返す
_.rest = _.tail = _.drop = function(array, n, guard) {
  // `array.slice(n)`
  return slice.call(array, n == null || guard ? 1 : n);
};

_.compact(array)

  • falsyな値を除外する
_.compact = function(array) {
  return _.filter(array, _.identity);
};

_.flatten(array, shallow)

  • ネストしているarrayのネストを解除する
var flatten = function(input, shallow, strict, startIndex) {
  var output = [], idx = 0;
  for (var i = startIndex || 0, length = getLength(input); i < length; i++) {
    var value = input[i];
    // 配列か引数の場合
    if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {

      // シャロー指定していない場合、再帰的にネスト解除する
      if (!shallow) value = flatten(value, shallow, strict);
      var j = 0, len = value.length;
      output.length += len;
      // 対象`value`文ループする
      while (j < len) {
        // 結果を格納する
        output[idx++] = value[j++];
      }
    } else if (!strict) {
      // 結果を格納する
      output[idx++] = value;
    }
  }
  return output;
};

_.flatten = function(array, shallow) {
  return flatten(array, shallow, false);
};

_.without(array, arguments...)

  • arrayの中からargumentsに存在しない値を配列形式で返す
_.without = function(array) {
  // `array`と第2引数以降の値の差を返す
  return _.difference(array, slice.call(arguments, 1));
};

_.uniq(array, isSorted, iteratee, context)

  • 重複値を除外する
  • 重複判定には_.containsを使用
  • ソート済み状態で処理を分岐する
_.uniq = _.unique = function(array, isSorted, iteratee, context) {
  // `isSorted`が`bool`でない場合
  if (!_.isBoolean(isSorted)) {
    // 仮引数とインスタンスを調整する
    context = iteratee;
    iteratee = isSorted;
    isSorted = false;
  }
  // コールバックを設定する
  if (iteratee != null) iteratee = cb(iteratee, context);
  var result = [];
  var seen = [];
  // `array`長分ループする
  for (var i = 0, length = getLength(array); i < length; i++) {
    var value = array[i],
        // 基準値計算処理が渡されている場合、計算する
        computed = iteratee ? iteratee(value, i, array) : value;
    if (isSorted) {
      if (!i || seen !== computed) result.push(value);
      seen = computed;
    } else if (iteratee) {
      // 未出現の値の場合
      if (!_.contains(seen, computed)) {
        // 出現値管理変数に出現値を積む
        seen.push(computed);
        // 結果に積む
        result.push(value);
      }
    } else if (!_.contains(result, value)) {
      // 結果に積む
      result.push(value);
    }
  }
  return result;
};

_.union(array...)

  • 引数の配列を結合&ユニークな要素のみに変換する
  • fatten_.uniqの合わせ技 js _.union = function() { return _.uniq(flatten(arguments, true, true)); };

_.intersection(array)

  • 配列内の要素に共通する値を求める
_.intersection = function(array) {
  // 結果格納変数を初期化する
  var result = [];
  // 計算対象引数の数を取得する
  var argsLength = arguments.length;
  // `array`長分ループする
  for (var i = 0, length = getLength(array); i < length; i++) {
    // 計算対象となる値を取得する
    var item = array[i];
    // 共通と判明しているなら次のループに移る
    if (_.contains(result, item)) continue;
    // 計算対象引数分ループする
    for (var j = 1; j < argsLength; j++) {
      // 別配列に存在しない場合、ループを抜ける(配列内に共通する値がない)
      if (!_.contains(arguments[j], item)) break;
    }
    // 直前のループが最後まで回っている場合(共通する値が存在する)、結果に積む
    if (j === argsLength) result.push(item);
  }
  // 結果を返す
  return result;
};

_.difference = function(array...)

  • 差集合を返す
_.difference = function(array) {
  // 第2引数以降の配列のネストを解除する
  var rest = flatten(arguments, true, true, 1);
  // ネスト解除した配列に含まれていない要素のみ返す
  return _.filter(array, function(value){
    return !_.contains(rest, value);
  });
};

_.zip(array1, array2...)

  • 引数の各配列の同一インデックス要素が同一配列内に入るように組み替える
_.zip = function() {
  return _.unzip(arguments);
};

_.unzip(array)

  • 2次元配列array内の各配列を同一インデックス要素が同一配列内に入るように組み替える
_.unzip = function(array) {
  // `array`の最大配列長を求め、その分の長さの配列を生成する
  var length = array && _.max(array, getLength).length || 0;
  var result = Array(length);

  // 排列を組み替える
  for (var index = 0; index < length; index++) {
    result[index] = _.pluck(array, index);
  }
  return result;
};

_.object(list, values)

  • listobjectに変換する
_.object(list, values) {
  // 結果変数を初期化する
  var result = {};
  // `list`長分ループする
  for (var i = 0, length = getLength(list); i < length; i++) {
    if (values) {
      // `{list[i]: values[i]}`
      result[list[i]] = values[i];
    } else {
      // `list[i]`を`{list[i][0]: list[i][1]}`に変換する
      result[list[i][0]] = list[i][1];
    }
  }
  // 結果を返す
  return result;
};

_.findIndex(array, predicate, context)

  • array内からpredicateな値の添え字を返す
    • 複数の値が存在する場合、最初の添え字を返す
function createPredicateIndexFinder(dir) {
  return function(array, predicate, context) {
    // `predicate`の`context`を設定する
    predicate = cb(predicate, context);
    // 配列長を取得する
    var length = getLength(array);
    // `dir`(おそらくdirectionが由来)に基づき、走査対象インデックスを定める
    var index = dir > 0 ? 0 : length - 1;
    // 配列長分ループする
    for (; index >= 0 && index < length; index += dir) {
      // `array`の要素が`predicate`な値である場合、インデックスを返す
      if (predicate(array[index], index, array)) return index;
    }
    return -1;
  };
}

_.findIndex = createPredicateIndexFinder(1);

_.findLastIndex

  • (array, predicate, context)
    • 複数の値が存在する場合、最後の添え字を返す
_.findLastIndex = createPredicateIndexFinder(-1);

_.sortedIndex(array, obj, iteratee, context)

  • 昇順ソート済みのarray内からobjが挿入されるインデックスを返す
    • iterateeにて、比較値の指定を可能
_.sortedIndex = function(array, obj, iteratee, context) {
  iteratee = cb(iteratee, context, 1);
  // 初期化する
  var value = iteratee(obj);
  var low = 0, high = getLength(array);
  // バイナリーサーチ
  while (low < high) {
    // 中間となるインデックスを取得する
    var mid = Math.floor((low + high) / 2);
    // 比較値が`value`より小さい場合、2分割後半を探索対象とする
    // 大きい場合、2分割前半を探索対象とする
    if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
  }
  return low;
};

_.indexOf(array, item, idx)

  • array内の要素がitemであるインデックスを返す
    • itemな要素が2つ以上の場合より前のインデックスを返す
function createIndexFinder(dir, predicateFind, sortedIndex) {
  return function(array, item, idx) {
    var i = 0, length = getLength(array);
    if (typeof idx == 'number') {
      // 先頭からの探索の場合
      if (dir > 0) {
          // 探索開始位置を決定する
          i = idx >= 0 ? idx : Math.max(idx + length, i);
      // 末尾からの探索の場合
      } else {
          // 探索開始位置を決定する
          length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
      }
    } else if (sortedIndex && idx && length) {
      // `array`内要素から`item`を探索する
      idx = sortedIndex(array, item);
      // `item`と同値な`array`要素が見つかった場合、インデックスを返す
      return array[idx] === item ? idx : -1;
    }
    if (item !== item) {
      idx = predicateFind(slice.call(array, i, length), _.isNaN);
      return idx >= 0 ? idx + i : -1;
    }
    // 探索ループ
    for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
      // `array`の要素が`item`であるインデックスを返す
      if (array[idx] === item) return idx;
    }
    return -1;
  };
}

_.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);

_.lastIndexOf(array, item, idx)

  • array内の要素がitemであるインデックスを返す
    • itemな要素が2つ以上の場合より後のインデックスを返す js _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);

_.range(start, stop, step)

  • 指定範囲の値を要素に持つ配列を生成する
_.range = function(start, stop, step) {
  // `stop`が渡されていない場合、`start`を`stop`に置き換える
  if (stop == null) {
    stop = start || 0;
    start = 0;
  }
  // `step`が渡されていない場合`1`とする
  step = step || 1;

  // 必要な配列長を求め、配列を生成する
  var length = Math.max(Math.ceil((stop - start) / step), 0);
  var range = Array(length);

  // `step`加算しつつ配列に積んでいく
  for (var idx = 0; idx < length; idx++, start += step) {
    range[idx] = start;
  }

  // 結果を返す
  return range;
};

_.keys(obj)

  • objの列挙可能なプロパティ名を配列に積めて返す
_.keys = function(obj) {
  // オブジェクトでない場合、から配列を返す
  if (!_.isObject(obj)) return [];
  `Object.keys`定義済みの場合`Object.keys(obj)`の戻り値を返す
  if (nativeKeys) return nativeKeys(obj);
  var keys = [];
  // `obj`の`key`を`keys`に積む
  for (var key in obj) if (_.has(obj, key)) keys.push(key);
  // Ahem, IE < 9. とのこと
  if (hasEnumBug) collectNonEnumProps(obj, keys);

  // 結果を返す
  return keys;
};

_.allKeys(obj)

  • objのプロパティ名を配列に積めて返す
_.allKeys = function(obj) {
  // オブジェクトでない場合、空配列を返す
  if (!_.isObject(obj)) return [];
  var keys = [];
  // `obj`のプロパティ名を`keys`に積む
  for (var key in obj) keys.push(key);

  // Ahem, IE < 9. とのこと
  if (hasEnumBug) collectNonEnumProps(obj, keys);
  // 結果を返す
  return keys;
};

_.values(obj)

  • objのプロパティ値を配列として返す
_.values = function(obj) {
  // `obj`のプロパティ名を取得する
  var keys = _.keys(obj);
  // `obj`のプロパティ長を取得する
  var length = keys.length;
  // 結果を格納する配列を初期化する
  var values = Array(length);
  // プロパティ長分ループする
  for (var i = 0; i < length; i++) {
    // `obj`のプロパティ値を積める
    values[i] = obj[keys[i]];
  }
  // 結果を返す
  return values;
};

_.mapObject(obj, iteratee, context)

  • オブジェクトの各プロパティにiterateeを適用した結果を返す
_.mapObject = function(obj, iteratee, context) {
  // `iteratee`の`this`を設定する
  iteratee = cb(iteratee, context);
  var keys =  _.keys(obj),
      length = keys.length,
      results = {},
      currentKey;
    // `obj`のプロパティ長分ループする
    for (var index = 0; index < length; index++) {
      // 処理対象プロパティ名を取得する
      currentKey = keys[index];
      // `obj`プロパティ値に`iteratee`を適用し、結果を積む
      results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
    }
    // 結果を返す
    return results;
};

_.pairs(obj)

  • objの各プロパティ名とプロパティ値を[プロパティ名, プロパティ値]に変換して配列に積める
_.pairs = function(obj) {
  // `obj`のプロパティ名を取得する
  var keys = _.keys(obj);
  // `obj`のプロパティ長を取得する
  var length = keys.length;
  // 結果格納変数を取得する
  var pairs = Array(length);
  // `obj`プロパティ長分ループする
  for (var i = 0; i < length; i++) {
    pairs[i] = [keys[i], obj[keys[i]]];
  }
  // 結果を返す
  return pairs;
};

_.invert(obj)

  • obj{プロパティ値: プロパティ名}に変換する
_.invert = function(obj) {
  // 結果格納変数を初期化する
  var result = {};
  // `obj`のプロパティ名を取得する
  var keys = _.keys(obj);
  // `obj`プロパティ長分ループする
  for (var i = 0, length = keys.length; i < length; i++) {
    // プロパティ名とプロパティ値を入れ替える
    result[obj[keys[i]]] = keys[i];
  }
  // 結果を返す
  return result;
};

_.functions(obj)

  • objのメソッドを配列で返す
_.functions = _.methods = function(obj) {
  // 結果格納変数を初期化する
  var names = [];
  // `obj`プロパティ名分ループする
  for (var key in obj) {
    // プロパティが関数の場合、結果に積む
    if (_.isFunction(obj[key])) names.push(key);
  }
  // 昇順ソートし、結果を返す
  return names.sort();
};

_.extend = createAssigner(obj)

var createAssigner = function(keysFunc, undefinedOnly) {
  return function(obj) {
    // 引数の数を取得する
    var length = arguments.length;
    // 引数が2個未満の場合、`obj`を返す
    if (length < 2 || obj == null) return obj;
    // 引数の数ループする
    for (var index = 1; index < length; index++) {
      // 対象引数を取得する
      var source = arguments[index],
          // `source`のプロパティ名を取得する
          keys = keysFunc(source),
          // プロパティ長を取得する
          l = keys.length;
      // プロパティ長分ループする
      for (var i = 0; i < l; i++) {
        // プロパティ名の1つを取得する
        var key = keys[i];
        // プロパティが未定義の場合、`obj`のプロパティに`source`のプロパティを1つ突っ込む
        if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
      }
    }
    // 結果を返す
    return obj;
  };
};

_.extend = createAssigner(_.allKeys);

_.extendOwn(dst, src)

  • srcの列挙可能プロパティのみdstヘシャローコピースル
_.extendOwn = _.assign = createAssigner(_.keys);

_.findKey(obj, predicate, context)

  • predicateobjのプロパティ名を返す
_.findKey = function(obj, predicate, context) {
  predicate = cb(predicate, context);
  // `obj`のプロパティ名を取得する
  var keys = _.keys(obj), key;
  // `obj`プロパティ長分ループする
  for (var i = 0, length = keys.length; i < length; i++) {
    // 対象プロパティを取得する
    key = keys[i];
    // 対象プロパティが`predicate`な場合、そのプロパティ名を返す
    if (predicate(obj[key], key, obj)) return key;
  }
};

_.pick(object, oiteratee, context)

  • objectからoiterateetrueとなるプロパティのみ抽出する
_.pick = function(object, oiteratee, context) {
  var result = {}, obj = object, iteratee, keys;
  // 引数を渡していない場合、空オブジェクトを返す
  if (obj == null) return result;
  // `oiteratee`が関数の場合
  if (_.isFunction(oiteratee)) {
    // `obj`のすべてのプロパティ名を取得する
    keys = _.allKeys(obj);
    // `oiteratee`を設定する
    iteratee = optimizeCb(oiteratee, context);
  } else {
    keys = flatten(arguments, false, false, 1);
    // `obj`が`key`となるプロパティ名が存在するか判定する関数を定義する
    iteratee = function(value, key, obj) { return key in obj; };
    obj = Object(obj);
  }
  for (var i = 0, length = keys.length; i < length; i++) {
    // 対象プロパティ名を取得する
    var key = keys[i];
    // プロパティ値を取得する
    var value = obj[key];
    // `obj`に`key`プロパティが存在する場合、結果に積む
    if (iteratee(value, key, obj)) result[key] = value;
  }
  return result;
};

_.omit(obj, iteratee, context)

  • objからiterateefalseとなるプロパティを抽出する
_.omit = function(obj, iteratee, context) {
  // `iteratee`が関数の場合
  if (_.isFunction(iteratee)) {
    // `iteratee`の真偽の反転する関数を定義する
    iteratee = _.negate(iteratee);
  } else {
    var keys = _.map(flatten(arguments, false, false, 1), String);
    iteratee = function(value, key) {
      return !_.contains(keys, key);
    };
  }
  // _
  return _.pick(obj, iteratee, context);
};

_.defaults(object, defaults)

  • objectのプロパティの標準値を設定する
_.defaults = createAssigner(_.allKeys, true);

_.create(prototype, props)

_.create = function(prototype, props) {
  var result = baseCreate(prototype);
  if (props) _.extendOwn(result, props);
  return result;
};

_.clone(obj)

  • objコピーを返す
    • ネスとしているプロパティはシャローコピー
_.clone = function(obj) {
  // オブジェクトでない場合、そのまま返す
  if (!_.isObject(obj)) return obj;
  // 配列の場合`slice`でコピーを返し、配列でない場合`_.extend`で空オブジェクトを基にコピーを生成する
  return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};

_.tap(obj, interceptor)

  • objを引数に取るinterceptorを呼び出す
_.tap = function(obj, c) {
  interceptor(obj);
  return obj;
};

_.isMatch(object, attrs)

  • objectのプロパティにattrsが存在するか返す
_.isMatch = function(object, attrs) {
  // `attrs`のプロパティ名とプロパティ長を取得kする
  var keys = _.keys(attrs), length = keys.length;
  if (object == null) return !length;
  var obj = Object(object);
  for (var i = 0; i < length; i++) {
    // 処理対象プロパティ名を決定する
    var key = keys[i];
    // `attrs`のプロパティ値と`obj`のプロパティ値が異なる or `attrs`のプロパティ名が`obj`のプロパティ名に存在しないばい
    if (attrs[key] !== obj[key] || !(key in obj)) return false;
  }
  // `object`に`attrs`が存在する場合、`true`を返す
  return true;
};

_.isEmpty(obj)

  • objが空であるか判定する
_.isEmpty = function(obj) {
  // `null`は空とする
  if (obj == null) return true;
  // 配列長0の場合は空とする
  if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
  // `obj`のプロパティ長が0は空とする
  return _.keys(obj).length === 0;
};

_.isElement(obj)

  • objがエレメントであるか返す
_.isElement(obj) {
  // @see https://developer.mozilla.org/ja/docs/Web/API/Node/nodeType
  return !!(obj && obj.nodeType === 1);
};

_.isArray(obj)

`Array.isArray`未定義の場合独自メソッドを使用する
_.isArray = nativeIsArray || function(obj) {
  // 実行環境に任せる
  return toString.call(obj) === '[object Array]';
};

_.isObject(obj)

  • objがオブジェクトであるか判定する
_.isObject = function(obj) {
  // 型名(`typeof`)で判定する
  var type = typeof obj;
  return type === 'function' || type === 'object' && !!obj;
};

_.noConflict

  • 他で定義されている_との衝突を避けるため、underscoreをオブジェクトとして返す
_.noConflict = function() {
  // 事前に定義されている`_`をグローバルに保持する
  root._ = previousUnderscore;
  // underscoreオブジェクトを返す
  return this;
};

_.identity(value)

  • 引数をそのまま返す
_.identity = function(value) {
  // 引数である`value`を返す
  return value;
};

_.constant(value)

  • valueを返す関数を返す
  • 使用方法 _constant(5)()
_.constant = function(value) {
  // 関数を返す
  return function() {
    // `value`を返す
    return value;
  };
};

_.property(key)

  • オブジェクトのkeyプロパティ値を返す関数を返す
var property = function(key) {
  // 引数`obj`を受け取り、`key`プロパティの値を返す
  return function(obj) {
    return obj == null ? void 0 : obj[key];
  };
};

// `property`へ
_.property = property;

_.propertyOf(obj)

  • プロパティ名を引数として、objのプロパティ値を返す関数を返す
_.propertyOf = function(obj) {
  // `obj`の`key`プロパティ値を返す関数を返す
  // `_.propertyOf(obj)(key)` で`obj.key`を返す
  return obj == null ? function(){} : function(key) {
    return obj[key];
  };
};

_.matcher(attrs)

  • オブジェクトのプロパティにattrsが存在するか判定する関数を返す
_.matcher = _.matches = function(attrs) {
  attrs = _.extendOwn({}, attrs);
  // `_.isMatch`で`attrs`を存在確認する関数を返す
  return function(obj) {
    return _.isMatch(obj, attrs);
  };
};

_.times(n, iteratee, context)

  • iterateen回呼び出し、結果を配列で返す
_.times = function(n, iteratee, context) {
  var accum = Array(Math.max(0, n));
  iteratee = optimizeCb(iteratee, context, 1);
  // `iteratee`を`n`回呼び出し、結果を配列に格納する
  for (var i = 0; i < n; i++) accum[i] = iteratee(i);
  return accum;
};

_.random(min, max)

  • minmaxの乱数を返す
_.random = function(min, max) {
  if (max == null) {
    max = min;
    min = 0;
  }
  // 「最小値」と「最小値から最大値の差分*割合」で範囲内の乱数を求める
  return min + Math.floor(Math.random() * (max - min + 1));
};

_.now()

  • 1970/1/1/0:0:0からの経過時間をmsで返す
_.now = Date.now || function() {
  // `Date.now`が未定義の場合、`Date.getTime`を使用する
  return new Date().getTime();
};

_.result(object, property, fallback)

  • propertyobjectのメソッドの場合、object.property()を実行し、その戻り値を返す
  • propertyobjectのメソッドでないプロパティの場合、 object.propertyを返す
_.result = function(object, property, fallback) {
  var value = object == null ? void 0 : object[property];
  if (value === void 0) {
    value = fallback;
  }
  return _.isFunction(value) ? value.call(object) : value;
};

_.uniqueId(prefix)

  • ユニークな値を返す
  • ID値はクロージャで管理している
var idCounter = 0;
_.uniqueId = function(prefix) {
  // `+ ''`で文字列へ変換する
  var id = ++idCounter + '';
  // `prefix`指定がある場合、プリフィックスを付与する
  return prefix ? prefix + id : id;
};

_.templateSettings

  • テンプレート判定正規表現を変更する
    • 評価、補間、エスケープの3種類を設定可能
_.templateSettings = {
  evaluate    : /<%([\s\S]+?)%>/g,
  interpolate : /<%=([\s\S]+?)%>/g,
  escape      : /<%-([\s\S]+?)%>/g
};
`

### `_.chain(obj)`
```js
_.chain = function(obj) {
  // see `var _ = function(obj) ... `
  var instance = _(obj);
  // see  `var result = function ...`
  instance._chain = true;
  return instance;
};

公開メソッド以外

ルートオブジェクトの確立

var root = this;

Establish the root object, window in the browser, or exports on the server.

「ブラウザなら window 、サーバーなら exports」とのこと。

バージョン定義

_.VERSION = '1.8.3';
1
1
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
1
1