Skip to content

Instantly share code, notes, and snippets.

@mitsuru793
Last active December 2, 2015 10:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mitsuru793/855e12e61867c2d855d1 to your computer and use it in GitHub Desktop.
Save mitsuru793/855e12e61867c2d855d1 to your computer and use it in GitHub Desktop.
Underscore.jsの_.mixinメソッドをコードリーディングします。
// mixinの挙動をコメントで記したものです。
var people = [{name: 'mike', age: 18}, {name: 'jane', age: 21}, {name: 'lake', age: 32}];
var sortedPeople = _.chain(people)
.sortBy(function(poeple){ return people.age; })
.first()
.value();
// 下記は、上記のコードを実行した場合のコメントです。
// sortByのところでステップインした時のものです。
// 式の次の行に、実行後の変数の中身を記載しています。
// mixinは_functionオブジェクトに、objにある関数への参照をprototypeに渡します。
_.mixin = function(obj) {
_.each(_.functions(obj), function(name) {
var func = _[name] = obj[name];
_.prototype[name] = function() {
// chainで繋げた場合は、sortByはここから始まります。
// name = 'sortBy'
var args = [this._wrapped];
// 3人の配列。無名関数に渡した配列です。
// args = [Array[3]]
// pushはArray.prototype.pushのエイリアスとなっています。
push.apply(args, arguments);
// sortByに渡した無名関数を、配列の要素として加えます。
// args = [[Array[3], function]
// func = _.sortBy
// this = _のインスタンス 中身は_wrapped:Array[3], _chain:trueです。
// func.apply(_, args)は書き換えると、_.func(args)という意味です。
// メソッド名が固定ではないので、このような書き方をしています。
// applyだけでインスタンスメソッドの実行はできていますが、戻り値をchainResultに渡します。
return chainResult(this, func.apply(_, args));
};
});
};
// mixinの定義直後に、_のprototypeに、_にバインドしてある関数を結びつけています。
// これを行わないと、_のインスタンスで各メソッドが使えません。
_.mixin(_);
// _のメソッドでチェーン可能なら、objを_でラップして返します。
// 関数の戻り値を、chainした結果を返します。
// chainを使っていない場合は、そのままobjを返します。
var chainResult = function(instance, obj) {
// instance._chainは、_.chainメソッドが使われて生成された、
// _のインスタンスかどうか判定する真理値です。(true/false)
// この条件によって、_.chainで繋げられているかがわかります。
// trueの場合に実行されるchainには、引数が渡されておりません。
// ここから、上記のsortByが実行される場所にステップインします。
// chainが_のインスタンスメソッドだからです。
// 上記のfunc.apply(_, args)によって、chainにはpeopleの配列が渡ります。
// また、peopleは_にラップされます。
return instance._chain ? _(obj).chain() : obj;
};
// objを_でラップして、_chainプロパティを持たせます。
// _chainの中身がいじられるのここだけです。
// つまり、_chainはchainメソッドを使ったかのフラグです。
_.chain = function(obj) {
var instance = _(obj);
instance._chain = true;
return instance;
};
// objを_でラップします。
// ここの詳細は、長くなるので別途ブログで記事にします。
var _ = function(obj) {
// _のインスタンスなら引数objを返します
if (obj instanceof _) return obj;
// this(実行コンテキスト)が_のインスタンスではない場合は、_メソッドで再生成をします。
if (!(this instanceof _)) return new _(obj);
// 引数objが_のインスタンスではなく、this(実行コンテキスト)が_のインスタンスの場合
this._wrapped = obj;
};
// 最後のvalueメソッドだけは、mixinの中の無名関数にはステップインしません。
// 理由は、valueは静的に予めprototypeに定義されたメソッドだからです。
// mixinの無名関数にステップインするものは、
// mixinによって、prototypeに動的に定義されたものだけです。
_.prototype.value = function() {
return this._wrapped;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment