はじめに
イディオムは、ある目的を達成するために 慣習的に書かれるコード のことです。
他人のコードを見るときに戸惑わないようまとめました。
解説は極力無くしています。気になるものは、 参考 から辿ってみてください。
コメント は、イディオムを使用しない場合の書き方の例です。
タイトルに (嫌) が付いているのは、嫌悪する人が多いので極力ライティングで避けるイディオムです
推奨 、 非推奨 はライティングする際にどれにするかの目安としました。(主観です)
動作環境は、es5以上もしくはes5-shim適用です。
イディオム
文字列を数値化
var x = '123';
var y = +x; // 推奨
var y = x - 0;
var y = x * 1;
// var y = Number(x); // 非推奨
数値を文字列化
var x = 123;
var y = x + ''; // 推奨
// var y = x.toString(); // 非推奨
// var y = String(x); // 非推奨
整数化
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に変換
var x = '';
var y = !!x;
// var y = Boolean(x);
NaNチェック
var x = NaN;
if (x !== x) {
}
// if (isNaN(x) && typeof x === 'number') {
// }
// if (Number.isNaN(x)){ // es2015
// }
注意 isNaN
とNumber.isNaN
は型が数値以外の場合に結果が異なる場合があります
引数の既定値
function foo (x) {
x = x || {};
// if (typeof x === 'undefined') {
// x = {};
// }
}
注意 falsy
な値が設定されない時のみ使用できます
配列の作成
var arr = 'foo,bar,baz'.split(',');
// var arr = ['foo', 'bar', 'baz'];
文字列を指定回数繰り返す
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埋め
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を使うと上位桁が捨てられてしまいます
配列内の存在確認 (嫌)
var x = 'foo';
if (~['foo', 'bar', 'baz'].indexOf(x)) { // 非推奨
}
// if (['foo', 'bar', 'baz'].indexOf(x) !== -1) { // 推奨
// }
// if (['foo', 'bar', 'baz'].includes(x)) { // es7 非推奨
// }
一括代入
var x, y;
x = y = 1;
// x = 1;
// y = 1;
代入時に初期化されてないなら同時に行う (嫌)
var obj;
(obj || (obj = {})).prop = 'foo';
// if (!obj) {
// obj = {};
// }
// obj.prop = 'foo';
値の入れ替え swap
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
参考 Javascriptでswap
nullとundefinedのみ分岐 (嫌)
var foo;
if (foo == null) {
}
// if (foo === null || typeof foo === 'undefined') {
// }
補足 jsHintの既定値ではアラートの出る構文です
配列の判定
var foo = [1, 2, 3];
var isArr = Object.prototype.toString.call(foo) === '[object Array]'; // 非推奨
// var isArr = Array.isArray(foo); // 推奨
配列を空にする
var foo = [1, 2, 3, 4];
var bar = foo;
bar.length = 0;
// bar.splice(0, bar.length);
長さ指定で配列を作成
var arr = Array.apply(null, {length:10});
// var arr = [];
// for (var i = 0; i < 10; i++) {
// arr[i] = undefined;
// }
配列の複製
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
可変引数を配列にする
function fn () {
var params = [].slice.call(arguments);
}
fn(1, 2, 3, 4, 5);
注意 先のイディオム"配列の複製"のconcat
はarguments
に使用しても意図したものになりません。
ヒアドキュメント
var heredoc = (function () {/*
<div class="title">
<h1>
<a href="${url}">${title}</a>
</h1>
</div>
*/}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1];
参考 Javascriptでヒアドキュメント
注意 jsをminifyすると動作しなくなります。その時はヒアドキュメントは諦めましょう。
ヒアドキュメント風 (嫌)
var heredoc = '\
<div class="title">\n\
<h1>\n\
<a href="${url}">${title}</a>\n\
</h1>\n\
</div>\
';
補足 safari,chrome,firefox,operaでは動作しました。ieは調査できず不明。
new 忘れ対策
function Klass() {
if (!(this instanceof Klass)) {
return new Klass();
}
this.prop1 = 'foo';
}
switch(true) (嫌)
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) イディオム考察
補足 バットノウハウだと感じる人がとても多いイディオムです。
三項演算子の連結 (嫌)
var x = 3;
var y = x === 1 ? 'first' :
x === 2 ? 'second' :
x === 3 ? 'third' :
'big number';
// var y; // その1
// switch(x) {
// case 1:
// y = 'first';
// break;
// case 2:
// y = 'second';
// break;
// case 3:
// y = 'third';
// break;
// default:
// y = 'big number';
// break;
// }
// var y; // その2
// if (x === 1) {
// y = 'first';
// } else if (x === 2) {
// y = 'second';
// } else if (x === 3) {
// y = 'third';
// } else {
// y = 'big number';
// }
補足 三項演算子の自体が嫌いな人も多いです。
スコープ化
(function() {
var x = 'foo';
// xはローカル変数
})();
ifの省略 true編
var flg = true;
flg && fn();
// if (flg) {
// fn();
// }
ifの省略 false編
var flg = false;
flg || fn();
// if (!flg) {
// fn();
// }
ifの省略 三項演算子&カンマ演算子編 (嫌)
var flg = false;
var value = flg ? 'foo' : (fn(), 'bar');
// var value;
// if (flg) {
// value = 'foo';
// } else {
// fn();
// value = 'bar';
// }
補足 普通はカンマ演算子を意識して使う事はほとんどありません。
メソッドが存在したら実行
var obj = {
fn: function() {}
};
obj && obj.fn && obj.fn();
// if(obj) {
// if(obj.fn) {
// obj.fn();
// }
// }
補足 if省略 true編を応用したイディオムです
関数自身に変数
function fn() {
fn.count = fn.count || 0;
return fn.count++;
}
// var count = 0;
// function fn() {
// return count++;
// }
処理が失敗するまで順次処理
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);
処理が成功するまで順次実行
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
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
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 を使おう♪
処理部のないwhile (嫌)
var items = ['foo', 'bar', 'baz'];
var i = items.length;
while(i && fn(items[--i]), i);
// while(i) {
// i--;
// fn(items[i]);
// }
補足 itemsを逆順に処理しています。
オブジェクトのキーで順次処理
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の違いを正しく知る
ネームスペースの確認し作成
if (typeof nsFoo === 'undefined') {
nsFoo = {};
}
nsFoo.bar = {};
補足 ライブラリを複数ファイルに分けた際に、クライアントサイドで読み込み順序に左右されなくなります
グローバルオブジェクトの取得
var global = (function(){return this})(); // その1
var global = (0, eval)('this'); // その2
補足 その1はstrict modeではundefined
を返します
参考1 globalオブジェクトを取得する
参考2 (0, eval)('this')とは何なのか
本投稿の対象
対象は コアjavascriptのみ としています。
クライアントサイドのオブジェクト(DOMとか)やjQueryなどライブラリに関しては、数が多すぎるので対象外です。
サーバサイドのみのオブジェクト(node.jsのライブラリなど)も対象外です。
また、コアjavascriptのオブジェクトですが、正規表現(RegExp)に関しても書き始めるとキリがないので対象としていません。
おわりに
この投稿は継続して更新する予定です。
JavaScript イディオム集で書かれた内容が素晴らしいので、多くを参照しています。
ただ時間が経っているため、それ以外にもQiitaやいろんなブログに新しいイディオムが分散して溜まってきました。
いちいち見るのが面倒なので、ここにまとめていきます。
(まとめて欲しいものがあった場合は、コメか編集リクエストください)
参考にしたサイトはそれぞれのイディオムの 参考 に記述しますが、私自身すでに忘れてしまったのも多いので思い出したら追加します。
また複数のイディオムに関連している場合は、最後の参考サイトで確認してください。
紹介しておいてなんですが。。。
あまりにもトリッキーすぎて初見では処理がものすごく理解し難いイディオムもあります。
あくまでここで紹介しているイディオムはリーディングに役立てるものです。
けっして「俺って頭いい(ドヤッ」とするためではありません。
ライティングでの使用は慎重になりましょう
ずいぶんと数が増えたので、グルーピングしようかと思うけど。。。わかりにくくなりそうだな