ECMAScript仕様を理解する 第4部
ウェブの他の部分では
Jason Orendorff氏がMozillaからJS文法の特異性に関する深い分析を公開しました。実装詳細は異なるものの、どのJSエンジンもこれらの特異性に同じ課題を抱えています。
Jason Orendorff氏がMozillaからJS文法の特異性に関する深い分析を公開しました。実装詳細は異なるものの、どのJSエンジンもこれらの特異性に同じ課題を抱えています。
このエピソードでは、ECMAScript言語の定義とその構文についてさらに深掘りします。文脈自由文法に馴染みのない方は、今が基礎を確認する良いタイミングです。仕様では言語を定義するために文脈自由文法を使用しています。親しみやすい紹介として"Crafting Interpreters"の文脈自由文法に関する章を参照するか、より数学的な定義についてはWikipediaのページをご覧ください。
仕様を読むスキルをさらに練習しましょう。まだ前回のエピソードを見ていない場合は、今がそれを確認する良いタイミングです!
仕様を知る楽しみな方法として、まずJavaScriptの機能を選び、それがどのように仕様化されているかを調べます。
警告!このエピソードには、2020年2月時点のECMAScript仕様からコピーされたアルゴリズムが含まれています。これらはいずれ古くなります。
プロパティがプロトタイプチェーンで検索されることは知っています: オブジェクトが読もうとしているプロパティを持っていない場合、プロトタイプチェーンを検索し続け、プロパティが見つかるか、それ以上プロトタイプを持たないオブジェクトに達するまで探索します。
例えば:
const o1 = { foo: 99 };
const o2 = {};
Object.setPrototypeOf(o2, o1);
o2.foo;
// → 99
この動作がどこで定義されているかを見つけてみましょう。始めるのに良い場所はオブジェクト内部メソッドのリストです。
[[GetOwnProperty]]
と[[Get]]
の両方がありますが、自身のプロパティに制限されないバージョンを探しているので、[[Get]]
を選びます。
残念ながら、プロパティ記述子仕様型にも[[Get]]
というフィールドがあります。そのため、仕様を[[Get]]
のために閲覧する際には、この二つの独立した使用法を慎重に区別する必要があります。
o2.foo
はAssignmentExpression
なのか?o2.foo
はAssignmentExpression
には見えません。なぜなら代入がないからです。なぜこれがAssignmentExpression
なのでしょうか?
仕様では、AssignmentExpression
を引数として使用したり代入の右辺に使用することが認められています。例を挙げると:
function simple(a) {
console.log('引数は ' + a);
}
simple(x = 1);
// → 「引数は 1」とログに記録される。
x;
// → 1
…および…
x = y = 5;
x; // 5
y; // 5
o2.foo
は何も代入をしないAssignmentExpression
です。これには以下の文法生成規則が基づいています。それぞれ「最も簡単な」ケースをたどって最後に至ります:
AssignmentExpression
は代入を伴う必要はなく、ConditionalExpression
であることもできます:
(他にも生成規則がありますが、ここでは関連性のあるもののみを示しています。)
ConditionalExpression
は条件式(a == b ? c : d
)を持たない必要はなく、ShortCircuitExpression
だけでも構いません:
さらに続けて:
ShortCircuitExpression : LogicalORExpression
LogicalORExpression : LogicalANDExpression
LogicalANDExpression : BitwiseORExpression
BitwiseORExpression : BitwiseXORExpression
BitwiseXORExpression : BitwiseANDExpression
BitwiseANDExpression : EqualityExpression
この記事では、仕様内の簡単な関数を取り上げ、その記法を理解しようとします。さあ、始めましょう!
JavaScriptを知っていても、その言語仕様であるECMAScript Language specification、略してECMAScript仕様を読むのは非常に気が重い場合があります。少なくとも初めて読んだときはそう感じました。
ECMAScript国際化APIの仕様書 (ECMA-402、またはIntl
) は、日付フォーマット、数値フォーマット、複数形選択、並べ替えなどのロケール固有の重要な機能を提供します。Chrome V8とGoogle国際化チームは、V8のECMA-402の実装に機能を追加しつつ、技術的負債を整理し、パフォーマンスや他のブラウザとの互換性を向上させる取り組みを行っています。
Hai DangはV8チームでの3か月間のインターンシップ中に、[...array]
, [...string]
, [...set]
, [...map.keys()]
, および [...map.values()]
(配列リテラルの最初にスプレッド要素がある場合)のパフォーマンスを改善しました。さらに、Array.from(iterable)
の速度も大幅に向上させました。この記事では、彼の変更の詳細について説明します。これらの変更はv7.2以降のV8に含まれています。
JavaScriptにおける非同期処理は従来、特に速いとは言えないレピュテーションを持っていました。さらに悪いことに、ライブJavaScriptアプリケーション、特にNode.jsサーバーのデバッグは簡単ではありません。特に 非同期プログラミングに関してはそうです。しかし、時代は変わりつつあります。本記事では、V8で非同期関数とプロミスをどのように最適化したか(そしてある程度は他のJavaScriptエンジンでも)、および非同期コードのデバッグ体験をどのように改善したかを探ります。
Array.prototype.sort
は、V8でセルフホスティングJavaScriptで実装された最後のビルトインの1つでした。このポート作業を通じて、異なるアルゴリズムや実装戦略を試す機会を得、それを最終的にV8 v7.0 / Chrome 70で安定化することができました。
DataView
sは、JavaScriptで低レベルメモリアクセスを行うための2つの可能な方法の1つです。もう1つはTypedArray
sです。これまで、V8においてTypedArray
sはDataView
sよりもかなり最適化されており、グラフィックス集約ワークロードやバイナリデータのデコード/エンコードなどの作業において低いパフォーマンスを示していました。この理由の多くは歴史的な選択に起因しています。例えば、asm.jsがDataView
sではなくTypedArray
sを選択していたことにより、エンジンがTypedArray
sのパフォーマンスに焦点を当てるよう促されていました。