09 Apr 2011

JavaScript のコンマ演算子

いつも興味深い Angus Croll さんの ブログ ですが, 今回のコンマ演算子の話題も, そもそもコンマ演算子を意識して使ったことがなかったので, 面白かったです.

The JavaScript Comma Operator – JavaScript, JavaScript…

以下内容をかいつまんだメモです. (訳ではありません)

コンマ演算子とは

コンマ演算子は2項演算子です. 両オペランドを評価し2番目の評価結果を返します.

var a = (7, 5);
a; //5
 
var x, y, z
x = (y=1, z=4);
x; //4
y; //1
z; //4

コンマ演算子は演算子の優先順位が最も低い演算子です. 以下の例は最終的に 22 を return します.

//original
return 5 * 2 + 3,  22;

代入演算子の右辺として使う場合は注意が必要です. 以下の例では 5 という名前の定義することになってしまい, エラーです.

//original
var a = 7, 5;
//apply = operator
var a, 5; //a is now 7
//SyntaxError: missing variable name

括弧で囲むとうまくいきます.

var a = (7, 5); // a === 5

評価順は左から右です.

var a = (1, 2, 3, 4);
a; //4

// これは以下と同様の評価順序です

var a = (((1, 2), 3), 4);
a; //4

Comma Separator

リスト内や関数の引数で使われるコンマはセパレータで, 演算子ではありません. 以下はセパレータとしてのコンマの例です.

//set 4 array elements
var arr = [1, 2, 3, 4];
 
//create an object with 2 properties
var obj = {
  a: 22,
  f: function() {return this.a*this.a}
}
 
//define 3 distinct variables
var a = 1, b = 2, c = 3;
 
//invoke a function passing 2 arguments
Math.max(4, 7);

&&, || 演算子との違い

&&左項は常に評価右項は左項が true なら評価
||左項は常に評価右項は左項が false なら評価
,左項は常に評価右項も常に評価

コンマ演算子の使用例

コンマ演算子を使い, コードをコンパクトに書くことが出来ます. いくつか例を示します.

for ループ

for ループの条件式などを書く部分だけで計算を完結させています. ループのボディ部分は空です.

// 15 番目までのフィボナッチ数列を生成する例
for (
    var i=2, r=[0,1];
    i<15;
    r.push(r[i-1] + r[i-2]), i++
); 
while ループ

for と同様に while 条件部分に多くの処理を入れる書き方ができます. 以下は DOM の親以上の要素の中から指定した要素を探す処理です.

function firstAncestor(el, tagName) {
  while(el = el.parentNode, el && (el.tagName != tagName.toUpperCase()));
  return el;
}
 
//element in http://ecma262-5.com/ELS5_HTML.htm
var a = $('Section_15.1.1.2');
 
firstAncestor(a, 'div'); //
三項演算子

三項演算子の各項に1つ以上の処理をさせることができます.

//player loses
lives ? (lives--, go()) : (gameOver(), exit());
デバッグ

コンマ演算子を利用すると, コードのフォーマットをあまり書き変えることなく console.log() を埋め込めます.

/* 
 * while の括弧のすぐ後にセミコロンがあるバグ
 */
//sum products while i > n
var i=10, n=0, total=0;
while(console.log(i,n), i-- > n++); {
    total += i*n
}
循環する配列

配列の最後までループしたあと配列の先頭に戻る例です.

var colorIndex = 0,
    colors = ["FF0000", "008000", "FF0086", "A2FF00", "0000FF", "800080"];
 
function selectNextColor(){
    return colors[colorIndex++] || colors[colorIndex = 0, colorIndex++];
}

参考

感想

コンマ演算子のことは知りませんでしたが, たしかにいままでは意識せずに使っていたこともありました.

全般的にコードがトリッキーになりすぎるきらいはありますが, うまく使えば便利な場面も多そうです. (ループのボディを使わないやり方はバグを埋め込みやすいのでどうかと思いますが) ループの条件部分に複数処理を入れるのは便利です. console.log() を入れる方法も一時的なデバッグ用ならば手軽でいいと思いました.