javascriptイディオム集


# はじめに

イディオムは、ある目的を達成するために **慣習的に書かれるコード** のことです。
他人のコードを見るときに戸惑わないようまとめました。

解説は極力無くしています。気になるものは、 **参考** から辿ってみてください。
**コメント** は、イディオムを使用しない場合の書き方の例です。
タイトルに **(嫌)** が付いているのは、嫌悪する人が多いので極力ライティングで避けるイディオムです
**推奨** 、 **非推奨** はライティングする際にどれにするかの目安としました。（主観です）
動作環境は、es5以上もしくはes5-shim適用です。

# イディオム

## 文字列を数値化

```javascript:
var x = '123';
var y = +x;     // 推奨
var y = x - 0;
var y = x * 1;
// var y = Number(x);  // 非推奨
```

## 数値を文字列化

```javascript:
var x = 123;
var y = x + '';  // 推奨
// var y = x.toString(); // 非推奨
// var y = String(x);    // 非推奨
```

## 整数化

```javascript:
var x = 12.3;
var y = x | 0;
var y = ~~x;
var y = x >> 0;
// var y = parseInt(x, 10);
// var y = Math.floor(x);
```

**補足** 推奨は速度優先なら`| 0`または`~~`、広範囲の数字を取り扱うなら`parseInt`です
**注意** `Math.floor`で負の値を使用すると結果が異なります

## true/falseに変換

```javascript:
var x = '';
var y = !!x;
// var y = Boolean(x);
```

## NaNチェック

```javascript:
var x = NaN;
if (x !== x) {
}
// if (isNaN(x) && typeof x === 'number') {
// }
// if (Number.isNaN(x)){  // es2015
// }
```

**注意** `isNaN`と`Number.isNaN`は型が数値以外の場合に結果が異なる場合があります

## 引数の既定値

```javascript:
function foo (x) {
  x = x || {};
// if (typeof x === 'undefined') {
//   x = {};
// }
}
```

**注意** `falsy`な値が設定されない時のみ使用できます

## 配列の作成

```javascript:
var arr = 'foo,bar,baz'.split(',');
// var arr = ['foo', 'bar', 'baz'];
```

## 文字列を指定回数繰り返す

```javascript:
var x = Array(10 + 1).join('foo');
// var x = '';
// for(var i = 0; i < 10; i++) {
//   x += 'foo';
// }
// var x = "foo".repeat(10); // es6 非推奨
```

## 0埋め

```javascript:
var x = 123;
var y = ('00000' + x).slice(-5);
// var y = x;
// var loop = 5 - (x + '').length;
// for(var i = 0; i < loop; i++) {
//   y = '0' + y;
// }
```

**注意** 桁あふれの可能性がある場合は、sliceを使うと上位桁が捨てられてしまいます

## 配列内の存在確認 (嫌)

```javascript:
var x = 'foo';
if (~['foo', 'bar', 'baz'].indexOf(x)) { // 非推奨
}
// if (['foo', 'bar', 'baz'].indexOf(x) !== -1) {  // 推奨
// }
// if (['foo', 'bar', 'baz'].includes(x)) {  // es7 非推奨
// }
```


## 一括代入

```javascript:
var x, y;
x = y = 1;
// x = 1;
// y = 1;
```


## 代入時に初期化されてないなら同時に行う (嫌)

```javascript:
var obj;
(obj || (obj = {})).prop = 'foo';
// if (!obj) {
//   obj = {};
// }
// obj.prop = 'foo';
```


## 値の入れ替え swap

```javascript:
var a = 1;
var b = 2;
(function(a_, b_){a = b_; b = a_;})(a, b); // 推奨
a ^= b; b ^= a; a ^= b; // 非推奨
b = [a, a = b][0];  // 非推奨
// var tmp = a; a = b; b = tmp; // 推奨
// [a, b] = [b, a]; // es6 非推奨
```

**参考** [JavaScript, XOR による swap](http://blog.livedoor.jp/nanoris/archives/51739551.html)
**参考** [Javascriptでswap](http://qiita.com/_shimizu/items/1bc988c5d87e2b02c116)

## nullとundefinedのみ分岐 (嫌)

```javascript:
var foo;
if (foo == null) {
}
// if (foo === null || typeof foo === 'undefined') {
// }
```

**補足** jsHintの既定値ではアラートの出る構文です

## 配列の判定

```javascript:
var foo = [1, 2, 3];
var isArr = Object.prototype.toString.call(foo) === '[object Array]'; // 非推奨
// var isArr = Array.isArray(foo); // 推奨
```

**参考** [JavaScript で配列を判定する正しいやり方](http://qiita.com/38kun/items/8b1b91e998ef890e868c)

## 配列を空にする

```javascript:
var foo = [1, 2, 3, 4];
var bar = foo;
bar.length = 0;
// bar.splice(0, bar.length);
```

**参考** [参照を切らずに配列を空にする](http://qiita.com/tohashi/items/058edeaffd716c7234db)


## 長さ指定で配列を作成

```javascript:
var arr = Array.apply(null, {length:10});
// var arr = [];
// for (var i = 0; i < 10; i++) {
//   arr[i] = undefined;
// }
```

**参考** [JavaScriptで配列の生成](http://qiita.com/_shimizu/items/421b3f4d1657bdff030c)


## 配列の複製

```javascript:
var foo = [1, 2, 3];
var bar = foo.concat();
var bar = [].concat(foo);
var bar = Array.prototype.slice.call(foo);
var bar = [].slice.call(foo); // 推奨
// var bar = foo.map(function(x){return x;});  // その1
// var bar = [];    // その2 非推奨
// for(var i = 0;i<foo.length;i++){
//   bar[i] = foo[i];
// }
```

**補足** 後のイディオム"可変引数を配列にする"との関係のため、推奨は`slice`です。

## 配列の重複排除

```
var a = [1,2,3,3,2,2,5];
var b = a.filter(function (x, i, self) {
  return self.indexOf(x) === i;
});
var b = Array.from(new Set(a)); // ES2015
```

**参考** [配列の重複をはじく、もしくは重複を取り出す](http://qiita.com/cocottejs/items/7afe6d5f27ee7c36c61f)


## 可変引数を配列にする

```javascript:
function fn () {
  var params = [].slice.call(arguments);
}
fn(1, 2, 3, 4, 5);
```

**注意** 先のイディオム"配列の複製"の`concat`は`arguments`に使用しても意図したものになりません。

## ヒアドキュメント

```javascript:
var heredoc = (function () {/*
<div class="title">
    <h1>
        <a href="${url}">${title}</a>
    </h1>
</div>
*/}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1];
```

**参考** [Javascriptでヒアドキュメント](http://qiita.com/_shimizu/items/837b529de9f3302e315c)
**注意** jsをminifyすると動作しなくなります。その時はヒアドキュメントは諦めましょう。

## ヒアドキュメント風 (嫌)

```javascript:
var heredoc = '\
<div class="title">\n\
    <h1>\n\
        <a href="${url}">${title}</a>\n\
    </h1>\n\
</div>\
';
```

**補足** safari,chrome,firefox,operaでは動作しました。ieは調査できず不明。

## new 忘れ対策

```javascript:
function Klass() {
  if (!(this instanceof Klass)) {
    return new Klass();
  }
  this.prop1 = 'foo';
}
```

## switch(true)   (嫌)

```javascript:
var i = 5, j = 15; 
switch(true) {
case i < 10 && j < 10:
  break;
case i < 10:
  break;
case j < 10:
  break;
default:
  break;
}
// if (i < 10 && j < 10) {
// } else if (i < 10) {
// } else if (j < 10) {
// } else {
// }
```

**参考** [switch(true) イディオム考察](http://qiita.com/t_uda/items/1969e09a970d71e4cfd6)
**補足** バットノウハウだと感じる人がとても多いイディオムです。

## 三項演算子の連結 (嫌)

```javascript:
var x = 3;
var y = x === 1 ? 'first'  :
        x === 2 ? 'second' :
        x === 3 ? 'third'  :
        'big number';
// var y;  // その１
// switch(x) {
// case 1:
//   y = 'first';
//   break;
// case 2:
//   y = 'second';
//   break;
// case 3:
//   y = 'third';
//   break;
// default:
//   y = 'big number';
//   break;
// }
// var y;  // その２
// if (x === 1) {
//   y = 'first';
// } else if (x === 2) {
//   y = 'second';
// } else if (x === 3) {
//   y = 'third';
// } else {
//   y = 'big number';
// }
```

**補足** 三項演算子の自体が嫌いな人も多いです。

## スコープ化

```javascript:
(function() {
  var x = 'foo';
  // xはローカル変数
})();
```

## ifの省略 true編

```javascript:
var flg = true;
flg && fn();
// if (flg) {
//   fn();
// }
```


## ifの省略 false編

```javascript:
var flg = false;
flg || fn();
// if (!flg) {
//   fn();
// }
```

## ifの省略 三項演算子&カンマ演算子編   (嫌)

```javascript:
var flg = false;
var value = flg ? 'foo' : (fn(), 'bar');
// var value;
// if (flg) {
//   value = 'foo';
// } else {
//   fn();
//   value = 'bar';
// }
```

**補足** 普通はカンマ演算子を意識して使う事はほとんどありません。

## メソッドが存在したら実行

```javascript:
var obj = {
  fn: function() {}
};
obj && obj.fn && obj.fn();
// if(obj) {
//   if(obj.fn) {
//     obj.fn();
//   }
// }
```

**補足** if省略 true編を応用したイディオムです

## 関数自身に変数

```javascript:
function fn() {
  fn.count = fn.count || 0;
  return fn.count++;
}
// var count = 0;
// function fn() {
//   return count++;
// }
```

## 処理が失敗するまで順次処理

```javascript:
function fn1 (x) {
  return x < 10;
}
function fn2 (x) {
  return x < 5;
}
function fn3 (x) {
  return x < 3;
}
var y = 4;
var result = fn1(y) && fn2(y) && fn3(y);
```


## 処理が成功するまで順次実行

```javascript:
function fn1 (x) {
  return x === 1;
}
function fn2 (x) {
  return x === 2;
}
function fn3 (x) {
  return x === 3;
}
var y = 2;
var result = fn1(y) || fn2(y) || fn3(y);
```

## forの代わりにwhile

```javascript:
var items = ['foo', 'bar', 'baz'];
var x, i = 0;
while(x = items[i++]) {
}
// for (var i = 0, len = items.length; i < len; i++) { // その1
//   x = items[i];
// }
// items.forEach(function(x){ // その2 推奨
// });
```

**注意** `while`では`falsy`な値が`x`に代入された場合に繰り返しを抜けます

## whileの代わりにarray.some

```javascript:
var items = [1, 2, 3, 4];
items.some(function (x){
  if (x === 3) {
    return true;
  }
});
// var x, i = 0;
// while(x = items[i++]){
//   if (x === 3) {
//     break;
//   }
// }
```

**参考** [ループ途中で抜ける処理は for(in break) ではなく some を使おう♪](http://qiita.com/phi/items/f998763d2d52bdd57a7c)


## 処理部のないwhile (嫌)

```javascript:
var items = ['foo', 'bar', 'baz'];
var i = items.length;
while(i && fn(items[--i]), i);
// while(i) {
//   i--;
//   fn(items[i]);
// }
```

**補足** itemsを逆順に処理しています。

## オブジェクトのキーで順次処理

```javascript:
var obj = {
  foo: 1,
  bar: 2,
  baz: 3
};
Object.keys(obj).forEach(function(key) {  // 推奨
  var value = obj[key];
});
// for (var key in obj) {
//   if (obj.hasOwnProperty(key)) {
//     var value = obj[key];
//   }
// }
```

**参考** [for-inとObject.keysの違いを正しく知る](http://qiita.com/cocottejs/items/66eef8fef22c0082a15f)


## ネームスペースの確認し作成

```javascript:
if (typeof nsFoo === 'undefined') {
  nsFoo = {};
}
nsFoo.bar = {};
```

**補足** ライブラリを複数ファイルに分けた際に、クライアントサイドで読み込み順序に左右されなくなります

## グローバルオブジェクトの取得

```javascript:
var global = (function(){return this})(); // その1
var global = (0, eval)('this');           // その2
```

**補足**  その1はstrict modeでは`undefined`を返します
**参考1** [globalオブジェクトを取得する](http://qiita.com/Hiraku/items/d249a2f2f13532748324)
**参考2** [(0, eval)('this')とは何なのか](http://qiita.com/SFPGMR/items/7a09507eead5b5db8d17)

# 本投稿の対象

対象は **コアjavascriptのみ** としています。
クライアントサイドのオブジェクト(DOMとか)やjQueryなどライブラリに関しては、数が多すぎるので対象外です。
サーバサイドのみのオブジェクト(node.jsのライブラリなど)も対象外です。
また、コアjavascriptのオブジェクトですが、正規表現(RegExp)に関しても書き始めるとキリがないので対象としていません。

# おわりに

**この投稿は継続して更新する予定です。**
[JavaScript イディオム集](http://nmi.jp/archives/488)で書かれた内容が素晴らしいので、多くを参照しています。
ただ時間が経っているため、それ以外にもQiitaやいろんなブログに新しいイディオムが分散して溜まってきました。
いちいち見るのが面倒なので、ここにまとめていきます。
（まとめて欲しいものがあった場合は、コメか編集リクエストください）
参考にしたサイトはそれぞれのイディオムの **参考** に記述しますが、私自身すでに忘れてしまったのも多いので思い出したら追加します。
また複数のイディオムに関連している場合は、最後の参考サイトで確認してください。

紹介しておいてなんですが。。。
あまりにもトリッキーすぎて初見では処理がものすごく理解し難いイディオムもあります。
あくまでここで紹介しているイディオムはリーディングに役立てるものです。
けっして「俺って頭いい（ドヤッ」とするためではありません。

**ライティングでの使用は慎重になりましょう**

ずいぶんと数が増えたので、グルーピングしようかと思うけど。。。わかりにくくなりそうだな

# 参考サイト

  * [JavaScript イディオム集](http://nmi.jp/archives/488)
  * [とりあえず知っておきたい演算子テクニック（入門編）](http://lab.sonicmoov.com/development/javascript-bitwise-logical-comma-operator/)