Oka Laboratory

Tips!

JavaScriptでの小技集です。ソースコードでは関数リテラルとしていますが、関数宣言(function文)でも同様に動作します。

数値計算については "Algorithm!" を参照してください。

ゼロパディング

指定した桁数になるまで数字の左側を0で埋めるゼロパディング (zero padding) をします。Array.joinメソッドは、配列 (array) の全要素を引数 (parameter, argument) で指定した区切り文字 (delimiter) で連結した文字列を作成します。ここで空要素の配列を作成 (Array(digit))、区切り文字0で連結 (join('0')) すると0が並ぶ文字列になります。これを数字の左側に付加し、右側から指定した桁数分だけ切り取ります。負数にも対応しています。

var zero_padding = function(n, digit) {
/* Zero Padding */
	var zp = n < 0 ? '-' : '';
	n = Math.abs(n);
	if (n.toString().length < digit) {
		zp += (Array(digit).join('0') + n).slice(-digit);
	} else {
		zp += n;
	}
	return zp;
};

小数の桁指定

指定した小数点 (decimal separator) 以下の桁数で小数 (decimal) を四捨五入します。Number.toFixedメソッドで固定小数点数 (fixed-point number) の文字列に変換してから、先頭に"+"を付加することで数値に戻しています。桁数digitにはNumber.toFixedメソッドの仕様である0以上20以下の値を指定します。

var round = function(n, digit) {
/* 四捨五入して小数第digit位まで求める */
	return +n.toFixed(digit); 
};

倍精度表示

JavaScriptの数値型はIEEE 754で規定された倍精度浮動小数点数 (double precision floating point number) で、1bitの符号部、11bitの指数部 (exponent)、52bit (MSBは常に1なので実質53bit) の仮数部 (fraction) から成ります。このため最小桁で計算誤差が発生することがあります。仮数部の有効桁数は15.95桁 (≃53×log102)ですので、四則計算の結果をNumber.toPrecisionメソッドで15桁に丸めた文字列に変換してから、先頭に"+"を付加することで数値に戻しています。

var double_precision = function(n) {
/* 有効数字15桁(倍精度、double precision)に丸める */
	return +n.toPrecision(15); 
};

閏年判定

現行の暦法であるグレゴリオ暦 (Gregorian calendar) では、400年に97回の閏年 (leap year) を設けています。

  1. 西暦年が4で割り切れる年は原則閏年
  2. 但し、西暦年が100で割り切れる年は原則平年
  3. 但し、西暦年が400で割り切れる年は閏年

この置閏規則に基づく判定法を以下に示します。

var is_leap_year = function(year) {
/* 閏年判定 (置閏規則) */
	return (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0);
};

JavaScriptのDateオブジェクトを利用して2月の月末日(=3月0日の日付)を取得して判定する方法もあります。

var is_leap_year = function(year) {
/* 閏年判定 (3月0日の日付を取得) */
	return new Date(year, 2, 0).getDate() == 29;
};

なお、日本の閏年の根拠法はOka Laboratory 備忘録の記事「暦関係の法令」を参照ください。

月の日数

指定した月の日数を求めます。引数のyearは西暦年、monthは月(1〜12)です。

月の日数を保持した配列を用いる方法は他の言語でも使用できるアルゴリズムです。is_leap_year関数は閏年判定の関数です。

var days_in_month = function(year, month) {
/* 月の日数の取得 (配列) */
	return [31, is_leap_year(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][--month];
};

JavaScriptではDateオブジェクトに翌月0日を設定することで月末日が求められ、これを月の日数とすることができます。

var days_in_month = function(year, month) {
/* 月の日数の取得 (Dateオブジェクトで月末日を求める) */
	return new Date(year, month, 0).getDate();
};

Internet Explorer 11とFirefoxでは、YYYY-MMの書式で入力してください。

最大値制限

<input>要素のnumber型ではmax属性で最大値を設定できますが、数値を直接入力したときは最大値を超えた値が入力されてしまいます。通常はsubmitによるブラウザの検証機能で十分です。しかし、<form>要素のinputイベントは<form>内の各要素のvalueが変化したタイミングで発火しますので、ブラウザの検証機能が働きません。このため最大値で制限する関数が必要となります。

var limit_max = function(ele_number) {
/* <input type="number">の入力値がmax(最大値)を超えたときの処理 */
	var val_max = ele_number.max;
	var val_length = ele_number.value.length;
	if (+ele_number.value > val_max) {
		ele_number.value = ele_number.value.slice(0, --val_length);
		if (+ele_number.value > val_max) {
		// 上位桁に挿入したときの値が最大値超えの可能性あり
			ele_number.value = ele_number.value.slice(0, --val_length);
		}
	}
};

この関数を<input>要素のnumber型inputイベントで呼び出します。引数には必ずthisキーワードを指定します。

<input type="number" name="Number1" min="1" max="20" oninput="limit_max(this);">

関数は以下の箇所で使用していますのでご確認ください。

このページ
Algorithm!

実行時間計測

PerformanceインターフェイスのTimeline APIを使うと、関数の実行や処理にかかる時間を計測することできます。

performance.mark(name)
指定された名前nameで計測地点を設定
performance.measure(measureName, startMark, endMark)
指定された2つの計測地点startMarkendMark間の測定に測定名mesureNameを設定
performance.getEntries()
全ての計測地点や測定を取得
performance.getEntriesByName(name[, type])
nameが一致する全ての計測地点 (type='mark') や測定 (type='measure') を取得
performance.getEntriesByType(type)
typeが一致する全ての計測地点 (type='mark') や測定 (type='measure') を取得
performance.clearMarks([name])
名前nameの計測地点を削除、引数が省略されたときは全ての計測地点 (type='mark') を削除
performance.clearMeasures([name])
名前nameの測定を削除、引数が省略されたときは全ての測定 (type='measure') を削除
performance.now()
ナビゲーション開始からの経過時間をミリ秒単位で取得

まず、performance.markで計測地点を設定します。複数の計測地点を設定したら、performance.measureで計測地点間の測定を設定します。それから、performance.getEntries系メソッドでPerformanceEntryオブジェクトの配列を取得し、そのdurationプロパティで計測値を取得します。

素因数分解のページで素因数分解、数論的関数値、類判定の実行時間の計測で使用しています。

performance.mark('prime_start');
func_prime();
performance.mark('prime_end');
func_arithmetic();
performance.mark('class_start');
func_class();
performance.mark('class_start');

// 計算・判定の実行時間
performance.measure('timer', 'prime_start', 'prime_end');	// func_prime
performance.measure('timer', 'prime_end', 'class_start');	// func_arithmetic
performance.measure('timer', 'class_start', 'class_end');	// func_class
var timer = performance.getEntriesByName('timer');			// 実行時間値の取得
for (var i = 0; i < timer.length; i++) {
	console.log(timer[i].name + ': ' + durationtimer[i].duration);
}	
performance.clearMarks();
performance.clearMeasures();

配列の空要素削除

空要素を含む配列変数から空要素を一括して削除するには、filterメソッドを使います。

filter(callback(element[, index, [array]])[, thisArg])
配列の各要素に対して引数に指定したcallback関数を実行し、trueを返した要素が残され、falseを返した要素が削除されます。callback関数は、3つの引数elementindexarrayを持ちます。
element
配列内で処理中の要素
index
配列内で処理中の要素の位置
array
filterメソッドが実行されている配列
thisArg
callbackを実行するときにthisとして使用する値
戻り値は残された要素からなる新しい配列で、元の配列はそのまま残ります。

filterメソッドの引数callbackにBooleanコンストラクタ関数を渡すことで、falsyな値である0、−0、空文字列("")、false、nullundefinedNaNである要素が削除されます。Booleanの引数は1個であるため、callback関数の引数elementが渡されます。

q = p.filter(Boolean);

具体的な使用例は、エラトステネスの篩のソースコードを参照してください。

利用上の注意


正当なCSSです!