[1,2,3] === [1,2,3] で false なので deepEqual() を実装

## [] == [] // => false
よく知られたことですが、`{}` や `[]` リテラルで作られたもの同士は同値比較で `false` を返します

``` javascript
[] == []; // false
{} == {}; // false
[] === []; // false
({}) === ({}); // false
```
これは `[]` を　`new Array()`, `{}` を `new Object()` だと解釈すると自然と理解できると思います。つまりそれぞれ新しいオブジェクトなので、処理系内部的には違うメモリ空間にあるはずです。

## でもうっかりやってしまう `arr == [1, 2, 3];` の罠
そうとわかっていても、うっかりこうやってテストを書いてしまった経験がある人は多いんじないでしょうか

``` javascript
// ライブラリに書いた Array を作るメソッド
var makeArray = function () {
   return Array.prototype.slice.call(arguments);
}

// QUnit でのテスト
test("a test", function() {
  equal(makeArray(1, 2, 3), [1,2,3]); // test failed で「あれー？」ってなる
});
```

わりとやりがちかと思います。

## QUnit にある deepEqual() メソッド
なので QUnit には `deepEqual()` メソッド が用意されており、オブジェクトや配列を再帰的に精査して「要素が同じなので `true` でいいか」という判定をします。

``` javascript
// 先のテストの書き直し
test("a test", function() {
  deepEqual(makeArray(1, 2, 3), [1,2,3]); // test failed で「あれー？」ってなる
});
```

なお、 Jasmine では `.toEqual()` は deepEqual と同じく expected と actual の要素も見て判定します。

Node.js のための Should.js は内部的に QUnit とは挙動の仕様が違う deepEqual を private に実装していたりします。

## 自分で実装したい
上記のテストフレームワークを使うまでもなく、「いや、書き捨てで試したいんだけど」という場面は割とあるので、上のフレームワークとは実装と挙動に違いはあるものの、こう実装しておくと `deepEqual()` のようなものができます。

``` javascript
/**
 * @param {*} x
 * @return {boolean} "typeof x" is Primitive
 */
function isPrimitive(x) {
  // ECMAScript の仕様上, null はプリミティブであるはずなんだけど
  // typeof null => 'object' になってしまうので、
  if (x === null) {
    return true;
  }
  var type_expr = typeof x,
      primitives = [
        'undefined', 'boolean', 'number', 'string'
      ],
      i, l;
  for (i = 0, l = primitives.length; i < l; i++) {
    if (type_expr === primitives[i]) {
       return true;
    }
  }
  return false;
}

/**
 * @description function.toString() したものを正規化する
 * @param {string} expr
 * @return {string}
 */
function replaceSpaces(expr) {
  return expr.replace(/\s*\{\s*/,
           ' { ').replace(/\s*\}\s*/,
           ' }').replace(/\s*,\s*/,
           ', ').replace(/\s*;\s*/,
           '; ').replace(/\s+/, ' ');
}

/**
 * @description 2つの function がほとんど同じかどうか
 * @param {function(...*):*} a
 * @param {function(...*):*} b
 * @return {boolean}
 */
function isSameFunction(a, b) {
  if (a === b) {
    return true;
  }
  if (a.name !== b.name || a.length !== b.length) {
    return false;
  }
  var as = replaceSpaces(a.toString()),
      bs = replaceSpaces(b.toString());
  return as === bs;
}

/**
 * @param {*} a
 * @param {*} b
 * @return {boolean}
 */
function deepEq(a, b) {
  var i, l, a_ps, b_ps;
  // Same identifier, or alias
  if (a === b) {
    return true;
  }
  // Primitive
  if (isPrimitive(a) && isPrimitive(b)) {
    return a === b;
  }
  // Array
  if (Array.isArray(a) && Array.isArray(b)) {
    if (a.length === b.length) {
      for (i = 0, l = a.length; i < l; i++){
        if (!deepEq(a[i], b[i])) { // それぞれの要素を再帰で判定して、 false が来たら切り上げる
          return false;
        }
      }
      return true;
    }
    return false;
  }
  // Function
  if (typeof a === 'function' && typeof b === 'function') {
    return isSameFunction(a, b);
  }
  // Object
  if (a.constructor === b.constructor) {
    a_ps = Object.getOwnPropertyNames(a);
    b_ps = Object.getOwnPropertyNames(b);
    if (a_ps.length !== b_ps.length) {
      return false;
    }
    for (i = 0, l = a_ps.length; i < l; i++) {
      if (!deepEq(a[a_ps[i]], b[b_ps[i]])) {
        return false;
      }
    }
    return true;
  }
  return false;
}
```

### とりあえずチェックできるよー
``` javascript
deepEqual([1,2,3], [1,2,3]); // true
deepEqual([1,[2,3]], [[1,2,]3]); // false
deepEqual({hoge:'hoge', huga:'huga'}, {hoge:'hoge', huga:'huga'}); // true
```

ソースは [GitHub](http://github.com/VoQn/js-practice) においてあります

+ [object.js](http://github.com/VoQn/js-practice/blob/master/src/object.js) : ↓の eq.js で依存してる API 
+ [eq.js](http://github.com/VoQn/js-practice/blob/master/src/eq.js) : deepEqual の実装