ショートコーディング

「ショートコーディング(short coding)」とは、より少ない文字で処理を記述することを指し、結果として同じ処理を記述するためにタイプするキーが少なくなります。その一方で、文字数を減らすためにどのような処理を表現しているのか分かりにくくなる(可読性が低下する)、つまり難読化を招くため、批判する向きもあります。

しかし、インタプリタ言語であるJavaScript(ECMAScript)の場合、文字数を削減するということはコードの読み込み時間が短縮できることを意味し、結果としてページの読み込み開始からJavaScriptの実行開始までの時間が短縮される可能性があります。

このページは、JavaScriptの主なショートコーディングをまとめたものですが、特に構文の似通っている他の言語でも通用するものもあります。

ブロックのブレースを省略する

ブロックを構成するステートメントが唯一の場合、ブレース(「{」「}」)を省略します。

for( var i = 0; i < 9; i++ ) {
a += b[i];
}
for( var i = 0; i < 9; i++ ) a += b[i];
空白文字を省略する

ステートメントやブロックの意味に影響を及ぼさないスペース、タブおよび改行文字を省略します。

for( var i = 0; i < 9; i++ ) a += b[i];
for(var i=0;i<9;i++)a+=b[i];

もちろん、varと変数の名前の間のスペースは意味が変わってしまうので、省略することはできません。

ステートメントの戻り値を利用する

戻り値を使用していないステートメントを、その戻り値を使用しているステートメントの中に組み込み(ネストし)ます。

for( var i = 0; i < 9; i++ ) {
a += b[i];
}
for( var i = 0; i < 9; ) a += b[i++];

代入ステートメントは代入した値が戻り値になるので、これもうまく利用します。

message = '更新しました。';
alert( message );
alert( message = '更新しました。' );
if-else?:で変換する

if-elseの条件分岐ブロックを、同じく条件に応じて2つの値のうち1つを戻り値とする?:演算子を使用して変換します。

if( a )
value = 'true';
else
value = 'false';
value = a ? 'true' : 'false';

ifelseのブロックが同じ変数への代入ステートメントなどではない場合でも、戻り値を使用しないステートメントとして変換します。

if( a )
pane.show( content );
else
frame.hide();
a ? pane.show( content ) : frame.hide();

elseブロックのない場合でも、一方を無視することで変換できます。

if( a )
pane.show( content );
a ? pane.show( content ) : 0;

3つ以上の条件分岐がある場合でも変換できます。

if( a > 0 )
value = 'plus';
else if( a < 0 )
value = 'minus';
else
value = 'zero';
value = a > 0 ? 'plus' : a < 0 ? 'minus' : 'zero';

?:演算子を使った階層ステートメントは可読性が悪くなりますので、パーレン(「(」「)」)や改行・インデントなどを適宜使用するのが無難です。

if( a > 0 )
b.insert();
else if( a < 0 )
b.delete();
else if( a == 0 )
b.update();
else
b.clear();
a > 0 ? b.insert() : (
a < 0 ? b.delete() : (
a == 0 ? b.update() : b.clear()
)
);
ifを論理演算子で変換する

elseのない条件分岐を、&&演算子を使用して変換します。

if( a )
pane.show( content );
a && pane.show( content );

条件に!演算子を使用している場合は、||演算子を使用して変換します。

if( !a )
frame.hide();
a || frame.hide();

条件分岐ブロックが代入ステートメントの場合などは、演算子の優先順位によって解釈が変わってしまうのでパーレンが必要です。

if( i < b.length )
a += b[i];
i < b.length && ( a += b[i] );

&&演算子と||演算子を組み合わせればif-else条件分岐ブロックを変換できますが、?:演算子の方がより短くなります。

truefalse

多くの場合、空ではない文字列や0ではない数値がtrue、空文字列や0falseとして解釈されます。しかし、それらの値とtruefalseの論理値が厳密に区別される場合や、「生の」論理値が必要な場合もあります。

そのような場合には、!演算子を使用して数値を論理値にします。

true
!0
false
!1

微々たるものですが、4文字のtrue、5文字のfalseがいずれも2文字になります。

undefined

truefalse同様に、undefinedが必要となる場合があります。

このような場合には、void演算子を使用します。

undefined
void 0

undefinedかどうかを調べる場合は、void演算子に加えて===演算子を使用します。

typeof a == 'undefined'
a === void 0

通常はa == undefinedでも問題ありませんが、undefinedはグローバル変数であるため、値を変更された場合に解釈が変わってしまいます。また、===演算子を==演算子にするとanullの場合にもtrueになってしまいます。

Infinity

数学的なアプリケーションの場合、無限大(∞)を示すInfinityが必要となる場合があります。

Infinityは、undefinedNaNと同じくグローバル変数であるため、値が変更される可能性があります。そのような環境ではこの点も考慮する必要があります。

この変数の値の変更対策とショートコーディングを合わせて、数値の演算結果でInfinityを表現します。

Infinity
1/0

NaNでは、文字数が変化しないためショートコーディングにはなりませんが、値の変更対策としてInfinityと同じように演算結果で表現できます。

NaN
0/0
with

特定のオブジェクトのメソッドやプロパティを連続的に参照する場合、withステートメントを使用するとオブジェクトの名前を省略することができます。

a.insert( location.host, location.path );
with( location ) {
a.insert( host, path );
}
標準オブジェクトを変数に代入して使用する

documentwindowなど、JavaScript動作時に既に存在しているオブジェクトを複数回参照する場合に、短い名前の変数に代入してこちらを使用します。

a.insert( location.host, location.path );
var l = location;
a.insert( l.host, l.path );

withステートメントを使用した方が短くなる場合があります。

関数・メソッドを変数に代入して使用する

JavaScriptでは関数(メソッドを含む)もオブジェクトであり、変数に代入できるので、複数回使用する関数やメソッドを変数に代入してこちらを使用します。

このような使い方を「(関数)エイリアス」と呼ぶことがあります。

alert( 'Hello,' );
alert( 'World.' );
var a = alert;
a( 'Hello,' );
a( 'World.' );

DOM操作をするような場合には、document.getElementByIdなど(の長〜い名前)のメソッドを1文字の変数に代入したものを使用したりすると効果的です。

文字列リテラルの配列の初期化

要素数の多い文字列リテラルの配列の初期化には、クォート文字が多く必要となります。

このような場合には、セパレータ文字(列)を定め、セパレータで1つの文字列リテラルに連結したものをsplitメソッドで分割します。

var a = ['i', 'ro', 'ha', 'ni', 'ho', 'he', 'to'];
var a = 'i,ro,ha,ni,ho,he,to'.split( /,/ );

文字列リテラルがすべて1文字であればセパレータ文字(列)も省略することができます。

var a = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
var a = 'abcdefg'.split( '' );
数字文字列の数値化

数値を加算する場合、Perlのように数値の加算演算子(+)と文字列の連結演算子(.)が異なる言語では問題ありませんが、JavaScriptではどちらも同じ演算子(+)のため、想定していた演算結果にならない場合があります。

a + b

上記の演算時に、例えばaが数値の1、bが文字列の2であった場合、演算結果は文字列の12となります。文字列演算の結果としては正しいですが、数値演算の結果としては正しくありません。

数字のみの文字列を数値にする場合、Number関数(ノンコンストラクタコンテキスト)やparseFloat関数またはparseInt関数ではなく、数値演算時に自動的に行われるキャストを利用します。

Number( a )
+a
Number( a )
a - 0
Number( a )
a * 1
Number( a )
a / 1

通常は1文字だけの+符号で問題ありませんが、直前に後置のインクリメント演算子(++)がある場合など、演算に影響を及ぼす可能性がある場合には0の減算や1の乗算・除算で数値化します。

.表記を[]表記にする

JavaScriptでは、通常.で表記するオブジェクトのメソッドやプロパティを、[]を使用して連想配列(またはハッシュ)のように表記できます。

長いメソッドやプロパティの名前を変数に代入してこれを利用します。

alert( a.selectedIndex + ' ' + b.selectedIndex );
var i = 'selectedIndex';
alert( a[i] + ' ' + b[i] );

文字列処理の結果を利用することもできます。

alert( a.offsetWidth + ',' + a.offsetHeight );
var o = 'offset';
alert( a[o + 'Width'] + ',' + a[o + 'Height'] );

もちろん、オブジェクトプロパティのメソッドやプロパティも同様に表記できます。

alert( a.style.width + ' ' + b.style.width );
var s = 'style', w = 'width';
alert( a[s][w] + ' ' + b[s][w] );
局所変数を使いまわす

一時的にしか使用しないような局所変数は、そのつど分かりやすい名前で宣言せず、最大同時使用数分をまとめて宣言し、CPUのレジスタのように使いまわします。

for( var i = 0; i < 9; i++ )
a += b[i];
for( var y = 0; y < 9; y++ )
for( var x = 0; x < 9; x++ )
a += c[y][x];
var x, y;
for( x = 0; x < 9; x++ )
a += b[x];
for( y = 0; y < 9; y++ )
for( x = 0; x < 9; x++ )
a += c[y][x];

同じ名前で使いまわすと、そこでどのように使用しているのか分からなくなるので、コメントなどで控えておくとよいでしょう。…コードが短くなる一方でコメントが増えて本末転倒ですが。

局所変数を引数に追加する

関数内で使用する局所変数の宣言(varステートメント)を、同じく局所変数となる引数の宣言に加えます。

function f( a ) {
var s = 'scroll', o = 'offset';
return [a[s + 'Left'], a[s + 'Top'], a[o + 'Width'], a[o + 'Height']];
}
function f( a, s, o ) {
s = 'scroll', o = 'offset';
return [a[s + 'Left'], a[s + 'Top'], a[o + 'Width'], a[o + 'Height']];
}

var(とスペース)が省略できるだけですが、局所変数の使いまわしと併用すると効果的です。

String.matchRegExp.execにする

グローバルマッチフラグ(「g」)なしの正規表現を引数に指定したString.matchは、その正規表現のRegExp.execと同じです。

'abc bca cab'.match( /a\w+/ );
/a\w+/.exec( 'abc bca cab' );

matchexecになる以外は文字数が変わらないため、短くなるのは1文字だけです。

JavaScriptが有効になっていません。