❣️

JavaScriptにはあまり知られていない「5種類」のコメントがある

4 min read

JavaScriptには殆ど知られていませんが、環境によって5種類のコメント構文が存在します。厳密な内容は以下の仕様書をご覧いただくとして、この記事では概要を確認します。

https://tc39.es/ecma262/

Comments

仕様書 12.9より。これが一般的に誰でも知っているコメントです。

MultiLineComment(複数行コメント)

/* 複数行コメント
途中に改行を含むことができます。*と/が連続する文字列は含められません。
*/

SingleLineComment(単行コメント)

// 単行コメントは//から行末文字までをコメントにします。

Line Terminators (行末文字)

行末文字は以下の4種類です。一般的なLFとCR以外に、LSとPSも行末文字として扱われることに注意が必要です。

コードポイント Unicode名 略記
U+000A Line Feed <LF>
U+000D Carriage Return >CR>
U+2028 Line Separator <LS>
U+2029 Paragraph Separator <PS>

HTML-like Comments (非推奨

仕様書 B.1.3より。ここからが本題です。JavaScriptではHTMLのコメントっぽいものが規定されており、各ブラウザやNodeJS等でもコメントとして扱われます。

HTMLOpenComment

<!--から行末文字までがコメントになります。

<!-- コメント
const x = 1 <!-- コメント

HTMLCloseComment

-->から行末文字までがコメントになります。
-->より前は、コメントとホワイトスペース以外は許容されません。

--> コメント
/* コメントやホワイトスペースはOK */ --> コメント
// ↓は-->の前に文字があるので文法エラーである
1 --> コメントではない

考察

これはなんだ

これは本文ではなくてAnnex(付属文書)に記述されている、いわゆる後方互換、歴史的な事情の維持のための文法です。古来のブラウザ(IE3とか、MS-DOSとかそういうレベルの時代)ではscriptタグに非対応で、プログラムがまるごと文章中に表示されてしまう可能性がありました。そこで、scriptの中身をコメントアウトして書くことがマナーでした。

<script><!--
スクリプト本体
//--></script>

日本の一般人にまでインターネットが普及した時代にはもう不要のマナーだったかもしれませんが、古い本や解説サイトを参考にしたであろうサイトが現存します。これをある程度解釈できるようにという配慮がこのHTML-likeコメントです。

後方互換を維持するための付属文章であり、シンタックスハイライトや処理系によっては認識できません。ジョーク等でも使うのはやめたほうが良いでしょう。CTFとかやる人は覚えておいてもいいかもしれません。

⚠JavaScript モジュールではコメントとして扱われない

最近のJavaScriptにはJavaScript モジュールという仕様が追加されました。新しい機能であるため互換性を維持する必要が無く、HTML-likeコメントは使用できないようになっています。
(仕様書の書き方で言うなら、Moduleをgoal symbol(目標記号)とした文脈自由文法ではHTML-likeコメントを解釈しない)

<script type="module">
import {hoge} from "sample.js"; // sample.jsではHTML-likeコメントを使えない
</script>

<script type="module">
<!-- インラインでもエラー
</script>

仕様書のSingleLineDelimitedComment

仕様を読んでいて混乱したのですが、/* 改行のないコメント */を表すSingleLineDelimitedCommentという非終端記号が追加されています。あんまりしっかり見てないですが、おそらく以下のような文法の都合で分離されているだけです。

0 /* comment */ --> +1   ←文法エラー
0 /*
改行 */ --> +1  ←コメントと解釈

不具合につながる可能性はあるか

--はデクリメントの演算子でもあるのですが、前後に改行を挟むことが許されていません。(自動セミコロン挿入が行われる)。よって、以下のような不自然な狙い方をしない限り正常なJavaScriptでHTML-likeコメントが誤爆することはない…と思います。思いつく方がいれば教えて下さい。

a = 1;
if( false 
<!--a
){ console.log(true); } else { console.log(false)}

<!--をコメントとする環境ではfalse、<!--をコメントとしない環境ではif( false < !(--a))と解釈され、trueになります。

Hashbang

https://github.com/tc39/proposal-hashbang
https://caniuse.com/mdn-javascript_grammar_hashbang_comments
#!ココはコメント

Unixではスクリプトファイルの先頭にShebang(シバン)などと呼ばれるコメントを記述することでインタプリタを指定することができます。
JavaScriptがNodeJS等で積極利用される機会も増加し、これを書きたい場面も出てくるだろう、これもコメント扱いにしようというのがこのhashbangコメントです。JavaScriptがhashbangコメントを認識してなにかするということはありません。

現在正式な仕様には採用されておらず、Stage-3段階の提案仕様です。もう何年か放置されているようですが、FirefoxとChromiumでは実装されています。SafariとIEは非対応です。

Shebangという表記がUnixでは一般的ですが英語版Wikipediaによると色々と歴史的な都合があって、合理的な理由では無いようです。
JavaScriptでは「#」はsheとは読まずhashと呼ばれる、「!」は俗にbangと呼ばれる。よって「Hashbang」とするほうがわかりやすい、と決まったようです。

なお、その特性上スクリプトの最初の行に記述する必要があり、#の前にはスペースや改行含め、いかなる文字も禁止されています。(ただし、BOMだけ許容します)

BNF的に、ScriptとModuleが開始記号となるようなところの先頭のみに有効です。つまり、eval("#!comment")はHashbangコメントですが、new Function("#!comment")は開始記号がFunctionBodyなのでHashbangコメントではありません。

現在正式な仕様で内容に、必然性が無い限り使うべきではありません。

まとめ

JavaScriptには以下の5種類のコメントが存在する。

  • MultiLineComment /* Comment */
  • SingleLineComment // Comment
  • HTMLOpenComment <!-- Comment
  • HTMLCloseComment --> Comment
  • Hashbang #! Comment

MultiLineCommentとSingleLineComment以外は推奨できないので使わないこと。ただし、存在を覚えておくと何らかの脆弱性対応やCTFのときに役立つかもしれない。

厳密な文法規則は参考文献を確認すること。

参考文献

https://tc39.es/ecma262/
https://github.com/tc39/proposal-hashbang

Discussion

ログインするとコメントできます