「ECMAScript」という名前になじみがないという人もJavaScriptといえばすぐに分かると思う。JavaScriptは、「ECMA」という標準化団体で標準化されたスクリプト言語のため、「ECMAScript」とも呼ばれている。
JavaScriptを使ったことがある人はご存じだと思うが、非常に柔軟性が高く、自由な書き方ができるのが特徴な言語である。逆にその柔軟性の高さで思わぬバグを生み出してしまい、デバッグに時間を浪費した経験がある方も多いのではないだろうか。今回、IE10でサポートされたのは、柔軟性を低減して、より厳密な書き方ができるようしたStrict(厳格)モードである。それでは、Strictモードについて解説していこう。
まず、Strictモードの指定の仕方だが、以下のように文字列として、“use strict”を指定するだけである。関数の中で記述すれば、その関数のみがStrictモードになり、先頭に記述すれば、全体がStrictモードになる。
// 全体をStrictモードに指定
"use strict";
function func() {
// 関数内のみStrictモードに指定
"use strict";
// コードを記述
}
では、「Strictモードでは、何が変わるのか」を解説していこう。まずは、Test Driveのデモ・ページを見てみよう。
このページでは、Strictモードで変更された振る舞いを実際に試してみることができる。[Code]欄にJavaScriptのコードを記述して、[Run Code]ボタンを押すと、[Result]欄に実行結果が、[Error]欄にエラーが表示される。ドロップダウンリストであらかじめ用意されているサンプル・コードは、以下の10個である。
(1) 識別子名に「eval」を使用することはできない
変数名やメソッド名に「eval」という名前を使うことはできない。
(2) 定義されていない変数を使用することはできない
これまで、変数の宣言は必要ではなかったが、Strictモードでは、事前にきちんと変数を宣言する必要がある。
(3) 書き込み不可プロパティの値を変更することはできない
詳細は、サンプル・コードで確認していただきたいが、以下のようにObject.definePropertyメソッドで、書き込み不可(writable:false)で定義したプロパティに値をセットしようとすると、実行時に例外がスローされる。
Object.defineProperty(obj, "prop", { value: "Don’t tread on me", writable: false }); // 書き込み不可のプロパティを定義
obj.prop = "New value"; // 書き込み不可のプロパティのため、例外がスローされる。
(4) 拡張不可オブジェクトを拡張することはできない
こちらも詳細は、サンプル・コードで確認していただきたいが、以下のように拡張不可にマークしたオブジェクトを拡張しようとした場合、実行時に例外がスローされる。
Object.preventExtensions(obj); // オブジェクトを拡張不可に設定
obj.extension = "invalid"; // 拡張不可のため、拡張しようとした場合、例外がスローされる。
(5) 削除できないプロパティは、削除できない
当たり前のことを書いているようだが、Strictモード以外で以下のような組み込みプロパティを削除しようとした場合でもエラーにはならない。ただし、このようなコードは明らかに間違えのため、Strictモードでは、エラーとして、例外がスローされることになる。
elete Object.prototype; // 削除できないプロパティのため、例外がスローされる。
(6) プロパティは重複できない
これも、当たり前といえば当たり前なのだが、1つのオブジェクトに同じ名前のプロパティを複数定義することはできない。
var obj = { prop: 1, prop: 2 }; // プロパティ名を重複できないため、例外がスローされる。
(7) 引数は重複できない
こちらも同様で、関数の引数に2つ以上同じ名前を設定することはできない。
function sample(a, a, b) { } // 2つの引数に同じ名前が設定されているため、例外がスローされる。
(8) 8進数は使えない
JavaScriptは、C系の言語のため、Cと同じく頭が「0」の数値は8進数として扱われる。文字の並びをそろえたいなどの理由で、無意味な値として頭に「0」を付けてしまって、バグを作り出してしまうようなケースを避けるために禁止していると思われる。
var result = 010 + 292; // 8進数の数値「010」(10進数で「8」)が指定されているため、例外がスローされる。
(9) Evalで変数の導入はできない
実は、一番影響を受けるのはこの部分かもしれない。以下のようにEvalで変数を宣言して、初期化しているコードは無視される。
eval("var x=5");
(10) 将来使われるキーワードは使用できない
Evalのように現在すでに使われているキーワードも使えないようになっていたが、将来使われるであろうキーワードもエラーになるようになっている。
var yield = 24; // 予約後のため、例外がスローされる。
エラーになるキーワードは次のとおり。
class, enum, extends, super, const, export, import, implements, let, private, public, yield, interface, package, protected, static
ここでは、デモ・ページで紹介されている10個のサンプルを紹介したが、ECMAの仕様書にはこれ以外にもStrictモードで実装するべき項目が書かれている。Strictモードでは、これまでバグが出やすかったコードを制限することで、厳格なコードのみが実行できるようになっているが、その代わりにコードが大きく制限されて、動作しなくなるコードも多くなるため、慎重に検討して選択するようにしてほしい。
IE10 PP2では、Web Performance Working Groupで定義しているパフォーマンスAPIのうち、以下の3つを実装している。
それでは、それぞれの機能について解説していこう。
● 9-1. requestAnimationFrame API
HTML5では、動的なページを作ることができるため、タイマーや画像の移動、再描画が必要になる機会が増加することが予想される。このような動きのあるページを表現するために、これまでもsetTimeout関数やsetInterval関数というタイマーAPIが提供されてきた。もちろんこれらのAPIでも問題なく動作させることはできるが、必要な処理が増えてくることを見越して、よりCPUの効率を上げるために作られたのが、requestAnimationFrame関数である。
これまでは、スムーズなアニメーションを実現するため、多くのケースでは、10ミリ秒など短い間隔で再描画を行ってきたと思う。だが、ディスプレイのリフレッシュ・レートは60Hz程度であるため、16.7ミリ秒に1回更新すれば、十分にスムーズがアニメーションを実現できる。またこのAPIでは、ブラウザが最小化されているときなど、再描画の必要がないときには呼び出されず、CPUの負荷を軽減することができる。これにより、「最大25%の省電力が期待できる」と言われている。
Test Driveには、アナログ時計の秒針をアニメーションで表示するデモが公開されている(次の画面を参照)。
このデモでは、setTimeout関数とrequestAnimationFrame関数の両方でアナログ時計を表示して、全く同じことを実現しているのにも関わらず、setTimeout関数では、CPUや電力消費量が大きいだけでなく、バックグラウンド処理への影響も大きいことが分かる。
それでは、requestAnimationFrame関数の呼び出し方をシンプルなサンプルで紹介しよう。ここでは、ウサギとカメが競争するサンプルを作ってみた。それでは、このコードを確認していこう。
<!DOCTYPE html>
<html>
<head>
<title>requestAnimationFrame</title>
</head>
<body>
<img id="kame" src="kame.PNG" style="position:absolute;top:50px;" />
<img id="usagi" src="usagi.PNG" style="position:absolute;top:150px;" />
<script>
var kame = document.getElementById('kame');
var usagi = document.getElementById('usagi');
renderTimeout();
renderAnimationFrame();
function renderTimeout() {
kame.style.left = kame.offsetLeft + 5 + 'px';
if (kame.offsetLeft < document.body.offsetWidth) {
setTimeout(renderTimeout, 20);
}
}
function renderAnimationFrame() {
usagi.style.left = usagi.offsetLeft + 5 + 'px';
if (usagi.offsetLeft < document.body.offsetWidth) {
msRequestAnimationFrame(renderAnimationFrame);
}
}
</script>
</body>
</html>
このサンプルでは、setTimeout関数とrequestAnimationFrame関数の両APIでタイマーを作って、5pxずつ画像を動かしている。なお、requestAnimationFrame関数は、現在、草案のため、ベンダ・プレフィックスである「ms」が必要になっている。requestAnimationFrame関数は、先ほど説明したとおり、およそ16.7ミリ秒ごとに呼び出される。一方、カメを動かしているsetTimeout関数は、それよりも若干遅い20ミリ秒ごとに呼び出されるように設定した。
これを実行した結果は、以下のようになる。
このサンプルでは、setTimeout関数を20ミリ秒ごとに呼び出すようにしているが、requestAnimationFrame関数は、16.7ミリ秒ごとに呼び出されるため、通常はウサギが勝つ。ただし、requestAnimationFrame関数は、描画が不要なとき(ブラウザが非表示になっているときなど)には呼び出されないため、ブラウザを最小化して復元したときには、次の画面のようにカメが勝つことになる(最小化中はウサギの動作は止まっているためである)。
このようにrequestAnimationFrame関数は、描画が必要なときにのみ呼び出されて、不要なときには呼び出されないようになっている。これは、ウサギの方がより省電力にアニメーションを実現できることを意味している。
このように、ブラウザが最小化されているときにも処理が必要な場合や、より細かい精度のタイマーが必要な場合には、従来どおりsetTimeout関数やsetInterval関数を使用すればよいし、単純にアニメーションなどで、ブラウザが表示されていないときに描画処理が不要な場合には、requestAnimationFrame関数を使用すればよい。
繰り返しになるが、requestAnimationFrame関数は、草案のため、ベンダ・プレフィックスが必要になっている。また、仕様が変わる可能性もあるため、注意が必要である。
● 9-2. Page Visibility API
Page Visibility APIでは、ページが表示されているかどうかの判定と、表示/非表示が切り替わったことを通知する機能が提供される。例えばWebベースのメール・クライアントで、非表示のときには新着メールの確認頻度を落としたり、ゲームで非表示のときには一時停止(=ポーズ)したり、といった使い方が想定されている。
Test Driveで提供されているPage Visibility APIのデモでは、表示されているときには緑のバーが表示され、非表示になると赤いバーが表示される。
それでは、ページの表示/非表示の切り替わりを判定するコードを確認していこう。
<!DOCTYPE html>
<html>
<head>
<title>Page Visibility API</title>
<script>
document.addEventListener("msvisibilitychange", visibilityChanged);
function visibilityChanged() {
document.getElementById('message').innerHTML += (document.msHidden ? "非表示" : "表示") + "<br />" ;
}
</script>
</head>
<body>
<div id="message" />
</body>
</html>
このコードでは、表示/非表示の切り替わりを通知するmsvisibilitychangeイベントに対するイベント・リスナを登録している。また、イベント・リスナの中では、document.msHiddenプロパティによって、非表示かどうかを判定してページに表示している。
Page Visibility APIは、現在、草案のため、「ms」というベンダ・プレフィックスが付与されている。今後、勧告になるとベンダ・プレフィックスが不要になる。また、仕様が変更される可能性があるので、注意が必要である。
● 9-3. setImmediate関数
setImmediate関数もW3C Web Performance Working Groupで設計されているパフォーマンス系APIの一つである。これまでに提供されていたsetTimeout関数と同様の機能を提供するが、setTimeout関数よりも高速かつ省電力に実行できるAPIである。
Test Driveでは、グラフィカルにソートを実行するデモが公開されている(次の画面を参照)。
このデモでは、15ミリ秒程度の精度があるHTML 4までのsetTimeout関数、4ミリ秒程度の精度があるHTML5のsetTimeout関数、そして非常に高速なsetImmediate関数の、3つのAPIでソート(=繰り返し処理)を実行するデモである。このデモを実行してみると、setImmediate関数は圧倒的な速さで処理が完了する。
それでは、シンプルなサンプルで確認していこう。
これを実現しているサンプル・コードは、以下の通りである。
<!DOCTYPE html>
<html>
<head>
<title>setImmediate</title>
<script language="JavaScript">
var timeoutCount = 0;
var immediateCount = 0;
var startTime = (new Date()).getTime();
var isRunnable = true;
setTimeout(callbackTimeout, 0);
msSetImmediate(callBackImmediate, 0);
function callbackTimeout() {
document.getElementById('timeout').textContent += '|';
if (isRunnable) {
setTimeout(callbackTimeout, 0);
} else {
document.getElementById('timeout').textContent += timeoutCount;
}
timeoutCount++;
}
function callBackImmediate() {
document.getElementById('immediate').textContent += '|';
if ((new Date()).getTime() - startTime > 30) {
isRunnable = false;
}
if (isRunnable) {
msSetImmediate(callBackImmediate, 0);
} else {
document.getElementById('immediate').textContent += immediateCount;
}
immediateCount++;
}
</script>
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=x-sjis">
</head>
<body>
<div id="timeout" style="color:red;">setTimeout:</div><br />
<div id="immediate" style="color:blue;">setImmediate:</div><br />
</body>
</html>
このサンプル・コードを実行してみると、30ミリ秒の間にsetTimeout関数は9〜10回程度なのに対して、setImmediate関数は300回程度呼び出された。もし現在、「setTimeout(0)」というコードで繰り返し処理を実施しているのであれば、setImmediate関数に置き換えない手はない。
setImmeidate関数は、現在、草案のため、「ms」というベンダ・プレフィックスが必要だ。勧告になった場合、ベンダ・プレフィックスが無くなるため、注意が必要である。
Copyright© Digital Advantage Corp. All Rights Reserved.