ikemonn's blog

技術ネタをちょこちょこと

【Underscore.js】_.extendと_.extendOwnを読んだ

jashkenas/underscore_.extend_.extendOwnを読んだ。

概要

_.extend(destination, *sources) 
_.extendOwn(destination, *sources)
  • extend: sourcesに含まれている全てのプロパティ(プロトタイプを含む。)をdestinationにコピーしたものを返す
  • extendOwn: sourcesに含まれている全てのプロパティ(プロトタイプは除く)をdestinationにコピーしたものを返す
var Hoge = function(){};
Hoge.prototype.own = false;

var a = _.extend({}, {own: true}, new Hoge());
console.log(a); // {own: false}

var b = _.extendOwn({}, {own: true}, new Hoge());
console.log(b); // {own: true}

ソースコード

_.extend = createAssigner(_.allKeys);
_.extendOwn = _.assign = createAssigner(_.keys);


var createAssigner = function(keysFunc, undefinedOnly) {
    return function(obj) {
      var length = arguments.length;
      if (length < 2 || obj == null) return obj;
      for (var index = 1; index < length; index++) {
        var source = arguments[index],
            keys = keysFunc(source),
            l = keys.length;
        for (var i = 0; i < l; i++) {
          var key = keys[i];
          if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
        }
      }
      return obj;
    };
  };

.allKeysでは、自身のプロトタイプのプロパティのkeyまで取得でき、.keysでは自身が持っているプロパティのkeyのみを取得できる。

参考

jashkenas/underscore

【Underscore.js】_.mapObjectを読んだ

jashkenas/underscore_.mapObjectを読んだ。

概要

_.mapObject(object, iteratee, [context])

objectの各値に第2引数で指定した関数を適用した第1引数を返す。.mapは値の配列を返すが、.mapObjectはオブジェクトを返す。

_.mapObject({age: 20, score: 50}, function(val, key){
    return val + 5;
}); // {age: 25, score: 55}

_.map({age: 20, score: 50}, function(val, key){
    return val + 5;
}); // [25, 55]

ソースコード

_.mapObject = function(obj, iteratee, context) {
    iteratee = cb(iteratee, context);
    var keys =  _.keys(obj),
          length = keys.length,
          results = {},
          currentKey;
      for (var index = 0; index < length; index++) {
        currentKey = keys[index];
        results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
      }
      return results;
  };

_.mapとは違い、返す値をpushしていくresutlsがオブジェクトになっており、そのkeyには元のオブジェクトのkeyを指定している。

参考

jashkenas/underscore

【Underscore.js】_.throttleを読んだ

jashkenas/underscore_.throttleを読んだ。

概要

_.throttle(function, wait, [options])

functionを一度目は即時実行して、二回目以降はwaitミリ秒待ってから実行する関数を返す。waitミリ秒までに複数回関数を実行しようとしても、最後に受け付けた関数のみが実行される。

function printCurrTime(num) {
    console.log(`now: ${_.now()} num: ${num}`);
}

var printTime = _.throttle(printCurrTime, 5000);

printTime(0);
printTime(1); // 実行されない
printTime(2); // 実行されない
printTime(3); // 実行されない
printTime(4); // 実行されない
printTime(5);


// now: 1445396803605 num: 0
// now: 1445396808607 num: 5

ソースコード

_.throttle = function(func, wait, options) {
    var context, args, result;
    var timeout = null;
    var previous = 0;
    if (!options) options = {};
    var later = function() {
      previous = options.leading === false ? 0 : _.now();
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    };
    return function() {
      var now = _.now();
      if (!previous && options.leading === false) previous = now;
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;
      if (remaining <= 0 || remaining > wait) {
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      } else if (!timeout && options.trailing !== false) {
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  };

一度目の関数が実行された時刻をpreviousに代入し、クロージャーに渡している。 二回目以降、関数が呼ばれるたびに、残りの時間を設定したsetTimeoutを発行する。

参考

jashkenas/underscore

【Underscore.js】_.deferを読んだ

jashkenas/underscore_.deferを読んだ。

概要

_.defer(function, *arguments) ```

現在のコールスタックがクリアされたあとに、functionを実行させる。
setTimeoutを0msで実行するのと似ている。
処理が重い計算や、HTMLのレンダリング等に使うと便利。


console.log('0'); (function(){     _.defer(function(){console.log('1');}); }()); console.log('2’); // 0, 2, 1

## ソースコード

.defer = .partial(.delay, , 1);

_.delayを1msで実行している。

## 参考

[jashkenas/underscore](https://github.com/jashkenas/underscore)

【Underscore.js】_.memoizeを読んだ

jashkenas/underscore_.memoizeを読んだ。

概要

_.bind(function, object, *arguments

一度計算した値をキャッシュしておく機能を関数に付け加える。

var num = 40;

var fib = function(n) {
    return n < 2 ? n : fib(n - 1) + fib(n - 2);
};

console.time('not memoize');
console.log(fib(num));
console.timeEnd('not memoize’); // not memoize: 1701.395ms

var fib2 = _.memoize(function(n) {
    return n < 2 ? n : fib2(n - 1) + fib2(n - 2);
});

console.time('memoize');
console.log(fib2(num));
console.timeEnd('memoize’); // memoize: 0.979ms

ソースコード

_.memoize = function(func, hasher) {
    var memoize = function(key) {
      var cache = memoize.cache;
      var address = '' + (hasher ? hasher.apply(this, arguments) : key);
      if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
      return cache[address];
    };
    memoize.cache = {};
    return memoize;
  };

hasherが与えられなければ、関数に与えられる引数をkeyにしたキャッシュ用の配列を内部で持っておき、キャッシュにヒットしたらそれを返している。

参考

jashkenas/underscore

【Underscore.js】_.sortedIndexを読んだ

jashkenas/underscore_.sortedIndexを読んだ。

概要

_.sortedIndex(list, value, [iteratee], [context]

ソートされているlistに対して、valueがどの位置に入るかを返す。

_.sortedIndex([10, 20, 30, 40, 50], 35); // 3

ソースコード

_.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);
      if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
    }
    return low;
  };

while文の中で二分探索をしている。

参考

jashkenas/underscore

【Underscore.js】_.zipを読んだ

jashkenas/underscore_.zipを読んだ。

概要

_.zip(*arrays) 

引数である各配列のindex値の値をまとめた配列を作る。返される配列のlengthは引数の中で1番長いlengthを持つ配列と同じ長さになる。

var x = _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
console.log(x); // [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]

ソースコード

  _.zip = function() {
    return _.unzip(arguments);
  };

  _.unzip = function(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;
  };

下記のコードで、引数のそれぞれの配列に対し、そのindex番目を結果にいれている。

for (var index = 0; index < length; index++) {
    result[index] = _.pluck(array, index);
}

参考

jashkenas/underscore