第1引数と第2引数を見比べ型や値が同じなら「true」を返す関数。
■使用例
var obj = {
music : "弦楽四重奏によるドラゴンクエスト",
author : "すぎやまこういち",
game : {}
}
var obj2 = {
music : "弦楽四重奏によるドラゴンクエスト",
author : "すぎやまこういち",
game : {}
}
// re = true;
var re = _.isEqual( obj, obj2 );
■内部構造
_.isEqual = function(a, b) {
// 引数を内部関数eq()に代入し結果を返す。
return eq(a, b);
};
var eq = function(a, b, aStack, bStack) {
if (a === b) return a !== 0 || 1 / a === 1 / b;
// 「null = undefined」用
if (a == null || b == null) return a === b;
// a,b が _の instance だった時。
if (a instanceof _) a = a._wrapped;
if (b instanceof _) b = b._wrapped;
// 第1引数の型が文字列になる。
// Array だったら [object Array]、Object だったら [object Object]、arguments は [object Arguments]、function は [object Function]、
// 文字列だったら [object String]、数字は [object Number]。
var className = toString.call(a);
// 第1引数と第2引数の型が違ったら、false を返す。
if (className !== toString.call(b)) return false;
// 型の種類によって対応を分ける。
switch (className) {
// 型が 正規表現 だったら。
case "[object RegExp]":
// RegExps are coerced to strings for comparison (Note: "" + /a/i === "/a/i")
// 型が 文字列 だったら。
case "[object String]":
return "" + a === "" + b;
// 型が 数字 だったら。
case "[object Number]":
if (+a !== +a) return +b !== +b;
return +a === 0 ? 1 / +a === 1 / b : +a === +b;
// 型が Date Object だったら。
case "[object Date]":
// 型が 真偽値 だったら。
case "[object Boolean]":
return +a === +b;
}
var areArrays = className === "[object Array]";
// 型が 配列 じゃなかったら。
if (!areArrays) {
// 第1引数もしくは第2引数の型が object でない場合。
if (typeof a != "object" || typeof b != "object") return false;
var aCtor = a.constructor, bCtor = b.constructor;
// 第1引数と第2引数の Constructor が違うなど下記条件が全部 true なら return false を返す。
if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
_.isFunction(bCtor) && bCtor instanceof bCtor)
&& ("constructor" in a && "constructor" in b)) {
return false;
}
}
// //型が 配列 じゃなかったら。
// 再帰処理された時は、各値の出所の配列(Object)などが入っている。
aStack = aStack || [];
bStack = bStack || [];
var length = aStack.length;
// 配列、Object の値がまた 配列 か Object の時、while 文が実行される。
while (length--) {
if (aStack[length] === a) return bStack[length] === b;
}
// 配列などの各値を調べる時に元の配列などを入れておく。
aStack.push(a);
bStack.push(b);
// 型が 配列 だったら。
if (areArrays) {
// 第1引数と第2引数のそれぞれの配列の長さが違ったら false を返す。
length = a.length;
if (length !== b.length) return false;
// 配列の各値を再度 eq() に代入。false が返された時、false を返す。
while (length--) {
if (!eq(a[length], b[length], aStack, bStack)) return false;
}
// 型が Object だったら。
} else {
// 第1引数「Object」のkey値の配列を作成。
var keys = _.keys(a), key;
length = keys.length;
// 第1引数と第2引数のkey値の配列の長さが違ったら false を返す。
if (_.keys(b).length !== length) return false;
// 第1引数「Object」の各key値とその値を比べる while ループ。
while (length--) {
key = keys[length];
// 第1引数の key値を第2引数が持っていて、 eq(a[key], b[key], aStack, bStack) の結果が true ならこの if は実行されない。
if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
}
}
// Remove the first object from the stack of traversed objects.
aStack.pop();
bStack.pop();
// 途中で return false; されなかったら true を返す。
return true;
};