Javascriptにおけるオブジェクトの順序

最初のころはObjectの順序は保証されていないと思っていたのでまとめました。
	
## Javascriptエンジン
今回検証する3つのエンジン

### v8
* Node.js
* Chrome

### SpiderMonkey
* FireFox

### Nitoro
* Safari

## 結論

### v8: Node.js, Chrome
* Objectの順序
	1. 数値順
	2. 挿入順
* 1aなど数字から始まる文字は文字列扱い
* 0は数値扱いで00は文字列扱い

### SpiderMonkey: FireFox
* Objectの基本的な順序
	1. 数値順
	2. 挿入順
* 最初に挿入される数値が1000以上の場合、挿入順になる
* 最初に挿入される数値が1000未満の場合、数値順・挿入順になる
* 1aなど数字から始まる文字は文字列扱い
* 0は数値扱いで00は文字列扱い

### Nitoro: Safari
* 挿入順

----
* ブラウザ間で挙動が異なるので、実用的ではない
* v8は結構記事書かれてるけど結論出てない感じ 調査中…

## 検証1
* Node.js v0.10.33
* Chrome バージョン 39.0.2171.71
* FireFox 34.0
* Safari バージョン 6.0.5...

空オブジェクトに順番に入れてみる

```js
var obj = {};
obj.c = 'c';
obj.b = 'b';
obj.a = 'a';
obj['2'] = '2';
obj[1] = '1';
```

### Node.js

```js
console.log(Object.keys(obj));
// [ '1', '2', 'c', 'b', 'a' ]

console.log(JSON.stringify(obj));
// {"1":"1","2":"2","c":"c","b":"b","a":"a"}
```

### Chrome
```js
console.log(Object.keys(obj));
//	["1", "2", "c", "b", "a"]

console.log(JSON.stringify(obj));
// {"1":"1","2":"2","c":"c","b":"b","a":"a"}
```

### FireFox
```js
console.log(Object.keys(obj));
// Array [ "1", "2", "c", "b", "a" ]

console.log(JSON.stringify(obj));
// "{"1":"1","2":"2","c":"c","b":"b","a":"a"}"
```

### Safari
```js
console.log(Object.keys(obj));
// ["c", "b", "a", "2", "1"]

console.log(JSON.stringify(obj));
// {"c":"c","b":"b","a":"a","2":"2","1":"1"}
```

### 結果
* safari以外は数値順・挿入順
* safariは挿入順

## 検証2
初期値があるとどうなるか

```js
var obj = {
	c: 'c',
	b: 'b',
	a: 'a',
	'3': '3',
	2: 2
};
obj[1] = '1';
```

### Node.js

```js
console.log(Object.keys(obj));
// [ '1', '2', '3', 'c', 'b', 'a' ]

console.log(JSON.stringify(obj));
// {"1":"1","2":2,"3":"3","c":"c","b":"b","a":"a"}
```

### Chrome
```js
console.log(Object.keys(obj));
//	["1", "2", "3", "c", "b", "a"]

console.log(JSON.stringify(obj));
// {"1":"1","2":2,"3":"3","c":"c","b":"b","a":"a"}
```

### FireFox
```js
console.log(Object.keys(obj));
// Array [ "1", "2", "3", "c", "b", "a" ]

console.log(JSON.stringify(obj));
// "{"1":"1","2":2,"3":"3","c":"c","b":"b","a":"a"}"
```

### Safari
```js
console.log(Object.keys(obj));
// ["c", "b", "a", "3", "2", "1"]

console.log(JSON.stringify(obj));
// {"c":"c","b":"b","a":"a","3":"3","2":2,"1":"1"}
```

### 結果
* safari以外は数値順・挿入順
* safariは挿入順

## 検証3
数値が先頭だとどうなるか

```js
var obj = {
	'b': 'b',
	'a': 'a',
	'2c': '2c',
	'1b': '1b',
	'1a': '1a'
};
obj[3] = 3;
```

### Node.js

```js
console.log(Object.keys(obj));
// [ '3', 'b', 'a', '2c', '1b', '1a' ]

console.log(JSON.stringify(obj));
// {"3":3,"b":"b","a":"a","2c":"2c","1b":"1b","1a":"1a"}
```

### Chrome
```js
console.log(Object.keys(obj));
//	["3", "b", "a", "2c", "1b", "1a"]

console.log(JSON.stringify(obj));
//	{"3":3,"b":"b","a":"a","2c":"2c","1b":"1b","1a":"1a"}
```

### FireFox
```js
console.log(Object.keys(obj));
// Array [ "3", "b", "a", "2c", "1b", "1a" ]

console.log(JSON.stringify(obj));
// "{"3":3,"b":"b","a":"a","2c":"2c","1b":"1b","1a":"1a"}"
```

### Safari
```js
console.log(Object.keys(obj));
// ["b", "a", "2c", "1b", "1a", "3"]

console.log(JSON.stringify(obj));
// {"b":"b","a":"a","2c":"2c","1b":"1b","1a":"1a","3":3}
```

### 結果
* safari以外は数値順・挿入順
* safariは挿入順
* 頭数値は文字列扱い

## 検証4
0とか00とか0絡むとどうなる？

```js
var obj = {
	'b': 'b',
	'a': 'a',
	'01': '01',
	'00': '00',
	'0': '0'
};
obj[1] = 1;
```
### Node.js

```js
console.log(Object.keys(obj));
// [ '0', '1', 'b', 'a', '01', '00' ]

console.log(JSON.stringify(obj));
// {"0":"0","1":1,"b":"b","a":"a","01":"01","00":"00"}
```

### Chrome
```js
console.log(Object.keys(obj));
// ["0", "1", "b", "a", "01", "00"]

console.log(JSON.stringify(obj));
// {"0":"0","1":1,"b":"b","a":"a","01":"01","00":"00"}
```

### FireFox
```js
console.log(Object.keys(obj));
// Array [ "0", "1", "b", "a", "01", "00" ]

console.log(JSON.stringify(obj));
// "{"0":"0","1":1,"b":"b","a":"a","01":"01","00":"00"}"
```

### Safari
```js
console.log(Object.keys(obj));
// ["b", "a", "01", "00", "0", "1"]

console.log(JSON.stringify(obj));
// {"b":"b","a":"a","01":"01","00":"00","0":"0","1":1}
```

### 結論
* safari以外は数値順・挿入順
* safariは挿入順
* 0は数値、00は文字列

## 検証5
階層構造	

```js
var obj = {
	a: {
		3: '3',
		2: 2,
		1: '1'
	},
	2: {
		c: 'c',
		1: 1
	}
};
obj[1] = '1';
obj[0] = {};
```

### Node.js

```js
console.log(Object.keys(obj));
// [ '0', '1', '2', 'a' ]

console.log(JSON.stringify(obj));
// {"0":{},"1":"1","2":{"1":1,"c":"c"},"a":{"1":"1","2":2,"3":"3"}}
```

### Chrome
```js
console.log(Object.keys(obj));
//	["0", "1", "2", "a"]

console.log(JSON.stringify(obj));
//	{"0":{},"1":"1","2":{"1":1,"c":"c"},"a":{"1":"1","2":2,"3":"3"}}
```

### FireFox
```js
console.log(Object.keys(obj));
// Array [ "0", "1", "2", "a" ]

console.log(JSON.stringify(obj));
// "{"0":{},"1":"1","2":{"1":1,"c":"c"},"a":{"1":"1","2":2,"3":"3"}}"
```

### Safari
```js
console.log(Object.keys(obj));
// ["a", "2", "1", "0"]

console.log(JSON.stringify(obj));
// {"a":{"3":"3","2":2,"1":"1"},"2":{"c":"c","1":1},"1":"1","0":{}}
```

### 結果
* safari以外は数値順・挿入順
* safariは挿入順

## 検証6
「1000を堺に挙動がかわる」記事を発見したので調査  
http://d.hatena.ne.jp/teramako/20140205/p1

```js
var obj = {
	'b': 'b',
	'a': 'a',
	'1000': '1000',
	'999': '999',
	2: '2'
};
obj[1001] = 1001;
obj[998] = 998;
```

### Node.js

```js
console.log(Object.keys(obj));
// [ '2', '998', '999', '1000', '1001', 'b', 'a' ]

console.log(JSON.stringify(obj));
// {"2":"2","998":998,"999":"999","1000":"1000","1001":1001,"b":"b","a":"a"}`
```

### Chrome
```js
console.log(Object.keys(obj));
//	["2", "998", "999", "1000", "1001", "b", "a"]

console.log(JSON.stringify(obj));
//	{"2":"2","998":998,"999":"999","1000":"1000","1001":1001,"b":"b","a":"a"}
```

### FireFox
```js
console.log(Object.keys(obj));
// Array [ "b", "a", "1000", "999", "2", "1001", "998" ]

console.log(JSON.stringify(obj));
// "{"b":"b","a":"a","1000":"1000","999":"999","2":"2","1001":1001,"998":998}"
```

### Safari
```js
console.log(Object.keys(obj));
// ["b", "a", "1000", "999", "2", "1001", "998"]

console.log(JSON.stringify(obj));
// {"b":"b","a":"a","1000":"1000","999":"999","2":"2","1001":1001,"998":998}
```

### 結果
* Node, Chromeは数値順・挿入順
* FireFor, Safariは挿入順

## 検証7
途中から1000を超えるとどうなる？ Firefox編

```js
var obj = {
	b: 'b',
	a: 'a',
	999: 999,
	900: 900
};
obj[1] = '1';
obj[1000] = '1000!';
obj[998] = 998;
obj['1002'] = 1002;
obj[1001] = 1001;
obj[0] = 0;
```

### FireFox
```js
console.log(Object.keys(obj));
// Array [ "0", "1", "900", "998", "999", "1000", "1001", "1002", "b", "a" ]

console.log(JSON.stringify(obj));
// "{"0":0,"1":"1","900":900,"998":998,"999":999,"1000":"1000!","1001":1001,"1002":1002,"b":"b","a":"a"}"
```

### 結果
* 数値順・挿入順…

## 検証8
検証7を全部初期値にすると… FireFox編

```js
var obj = {
	b: 'b',
	a: 'a',
	999: 999,
	900: 900,
	1: 1,
	1000: '1000!',
	998: 998,
	'1002': 1002,
	1001: 1001,
	0: 0
};
```
### FireFox
```js
console.log(Object.keys(obj));
// Array [ "0", "1", "900", "998", "999", "1000", "1001", "1002", "b", "a" ]

console.log(JSON.stringify(obj));
// "{"0":0,"1":"1","900":900,"998":998,"999":999,"1000":"1000!","1001":1001,"1002":1002,"b":"b","a":"a"}"
```

### 結果
* 数値順・挿入順…

## 検証9
ラスト!! FireFox編

```js
var obj = {
	b: 'b',
	a: 'a',
	1000: '1000!',
	999: 999,
	900: 900,
	1: 1,
	998: 998,
	'1002': 1002,
	1000: 1001,
	0: 0
};
```
### FireFox
```js
console.log(Object.keys(obj));
// Array [ "b", "a", "1000", "999", "900", "1", "998", "1002", "1001", "0" ]
console.log(JSON.stringify(obj));
// "{"b":"b","a":"a","1000":"1000!","999":999,"900":900,"1":1,"998":998,"1002":1002,"1001":1001,"0":0}"
```

### 結果
* 最初に挿入される数値が1000以上だと挿入順になる