JavaScriptの構文は比較的単純です。本章では、注意すべき点を説明します。
このセクションでは、JavaScriptの構文がどのようなものか、簡単に説明します。
以下は、5つの基本的な値の種類です。
// Two slashes start single-linecomments
var
x
;
// declaring a variable
x
=
3
+
y
;
// assigning a value to the variable `x`
foo
(
x
,
y
);
// calling function `foo` with parameters `x` and `y`
obj
.
bar
(
3
);
// calling method `bar` of object `obj`
// A conditional statement
if
(
x
===
0
)
{
// Is `x` equal to zero?
x
=
123
;
}
// Defining function `baz` with parameters `a` and `b`
function
baz
(
a
,
b
)
{
return
a
+
b
;
}
=
)は、変数に値を代入するために使用されます。===
)は、2つの値を比較するために使用されます(等号演算子を参照)。コメントには2種類あります。
//
による単一行コメントは行の最後まで続きます。例を以下に示します。
var
a
=
0
;
// init
/* */
による複数行コメントは、任意のテキスト範囲にわたることができます。ネストすることはできません。2つの例を以下に示します。
/* temporarily disabled
processNext(queue);
*/
function
(
a
/* int */
,
b
/* str */
)
{
}
このセクションでは、JavaScriptにおける重要な構文上の違い、つまり式と文の違いについて見ていきます。
式は値を生成し、値が期待される場所であればどこでも記述できます。たとえば、関数呼び出しの引数や代入の右辺などです。次の各行には式が含まれています。
myvar
3
+
x
myfunc
(
'a'
,
'b'
)
おおよそ、文はアクションを実行します。ループとif
文は、文の例です。プログラムは基本的に文のシーケンスです。[8]
JavaScriptが文を期待する場所では、式も記述できます。このような文は、式文と呼ばれます。逆は成り立ちません。JavaScriptが式を期待する場所に文を記述することはできません。たとえば、if
文を関数の引数にすることはできません。
文と式の違いは、類似した2つの構文カテゴリのメンバー、つまりif
文と条件演算子(式)を見るとより明確になります。
以下はif
文の例です。
var
salutation
;
if
(
male
)
{
salutation
=
'Mr.'
;
}
else
{
salutation
=
'Mrs.'
;
}
類似した種類の式、条件演算子があります。前の文は、次のコードと同等です。
var
salutation
=
(
male
?
'Mr.'
:
'Mrs.'
);
等号とセミコロンの間のコードは式です。括弧は必須ではありませんが、括弧で囲むと条件演算子をより読みやすくできると考えています。
2種類の式は文のように見えます。構文カテゴリに関して曖昧です。
オブジェクトリテラル(式)はブロック(文)のように見えます。
{
foo
:
bar
(
3
,
5
)
}
上記の構成は、オブジェクトリテラル(詳細:オブジェクトリテラル)か、ラベルfoo:
に続くブロック、関数呼び出しbar(3, 5)
のいずれかです。
名前付き関数式は関数宣言(文)のように見えます。
function
foo
()
{
}
上記の構成は、名前付き関数式か関数宣言のいずれかです。前者は関数を生成し、後者は変数を作成して関数を変数に代入します(2種類の関数定義の詳細:関数の定義)。
解析中の曖昧さを防ぐために、JavaScriptではオブジェクトリテラルと関数式を文として使用できません。つまり、式文は次で始まることはできません。
function
式がこれらのトークンのいずれかで始まる場合、式コンテキストでのみ表示できます。たとえば、式を括弧で囲むことで、この要件を満たすことができます。次に、それが必要な2つの例を見ていきます。
eval()
によるオブジェクトリテラルの評価eval
は、文コンテキストでその引数を解析します。eval
がオブジェクトを返すようにするには、オブジェクトリテラルを括弧で囲む必要があります。
> eval('{ foo: 123 }') 123 > eval('({ foo: 123 })') { foo: 123 }
以下のコードは、関数の即時呼び出し式(IIFE)です。これは、その本体がすぐに実行される関数です(IIFEの用途については、IIFEによる新しいスコープの導入で説明します)。
> (function () { return 'abc' }()) 'abc'
括弧を省略すると、構文エラーが発生します。JavaScriptは関数宣言と見なすため、匿名にすることはできません。
> function () { return 'abc' }() SyntaxError: function statement requires a name
名前を追加した場合も構文エラーが発生します。関数宣言は即時呼び出しできないためです。
> function foo() { return 'abc' }() SyntaxError: Unexpected token )
関数宣言の後に続くものは、合法的な文でなければならず、()
ではありません。
制御フロー文の場合、本体は単一の文です。2つの例を以下に示します。
if
(
obj
!==
null
)
obj
.
foo
();
while
(
x
>
0
)
x
--
;
ただし、任意の文は、常にブロック(中括弧)で置き換えることができます。0個以上の文を含みます。したがって、次のように記述することもできます。
if
(
obj
!==
null
)
{
obj
.
foo
();
}
while
(
x
>
0
)
{
x
--
;
}
後者の制御フロー文の形式を好みます。標準化することで、単一文の本体と複数文の本体に違いがなくなります。その結果、コードはより一貫性のあるものになり、1つの文と複数の文の切り替えが容易になります。
このセクションでは、JavaScriptにおけるセミコロンの使用方法について調べます。基本的な規則は以下のとおりです。
JavaScriptではセミコロンは省略可能です。省略されたセミコロンは、いわゆる自動セミコロン挿入(ASI;自動セミコロン挿入を参照)によって追加されます。ただし、この機能は常に期待通りに動作するとは限らないため、常にセミコロンを含める必要があります。
以下の文は、ブロックで終わる場合、セミコロンで終了しません。
for
、while
(ただしdo-while
は除く)if
、switch
、try
以下はwhile
とdo-while
の例です。
while
(
a
>
0
)
{
a
--
;
}
// no semicolon
do
{
a
--
;
}
while
(
a
>
0
);
以下は関数宣言と関数式の例です。後者はセミコロンで終わります。これはvar
宣言(これはセミコロンで終了する)の中に現れるためです。
function
foo
()
{
// ...
}
// no semicolon
var
foo
=
function
()
{
// ...
};
ブロックの後にセミコロンを追加しても、構文エラーは発生しません。空文と見なされるためです(次のセクションを参照)。
セミコロンについて知っておく必要があるのは、ほとんどこれだけです。常にセミコロンを追加すれば、このセクションの残りの部分を読む必要はないでしょう。
単独のセミコロンは空文であり、何も行いません。空文は、文が期待される場所にどこでも表示できます。文が必要だが不要な状況で役立ちます。そのような状況では、ブロックも通常許可されます。たとえば、次の2つの文は同等です。
while
(
processNextItem
()
>
0
);
while
(
processNextItem
()
>
0
)
{}
関数processNextItem
は、残りのアイテム数を返すものと想定されています。3つの空文で構成される次のプログラムも、構文的に正しくなります。
;;;
自動セミコロン挿入(ASI)の目標は、行末のセミコロンを省略可能にすることです。自動セミコロン挿入という用語が呼び起こすイメージは、JavaScriptパーサーがユーザーのためにセミコロンを挿入することです(内部的には、通常は異なる方法で処理されます)。
言い換えれば、ASIはパーサーが文の終了を判断するのに役立ちます。通常、文はセミコロンで終了します。ASIは、文が次の場合にも終了すると規定しています。
以下のコードには、行終端子に不正なトークンが続くものがあります。
if
(
a
<
0
)
a
=
0
console
.
log
(
a
)
トークンconsole
は0
の後に不正であり、ASIをトリガーします。
if
(
a
<
0
)
a
=
0
;
console
.
log
(
a
);
以下のコードでは、括弧内の文はセミコロンで終了していません。
function
add
(
a
,
b
)
{
return
a
+
b
}
ASIは、上記のコードの構文的に正しいバージョンを作成します。
function
add
(
a
,
b
)
{
return
a
+
b
;
}
ASIは、キーワードreturn
の後に行終端子がある場合にもトリガーされます。例:
// Don't do this
return
{
name
:
'John'
};
ASIは上記を次のように変換します。
return
;
{
name
:
'John'
};
これは、空のreturnに、ラベルname
が前にある式文'John'
を含むブロックが続くものです。ブロックの後に、空文があります。
改行で始まる文が、前の文の続きとして許容されるトークンで始まる場合があります。その場合、ASIはトリガーされません。一見トリガーされそうに見えますが、そうではありません。例:
func
()
[
'ul'
,
'ol'
].
forEach
(
function
(
t
)
{
handleTag
(
t
)
})
2行目の角括弧は、func()
によって返された結果へのインデックスとして解釈されます。括弧内のカンマは、カンマ演算子(この場合は'ol'
を返します。詳細はカンマ演算子を参照)として解釈されます。そのため、JavaScriptは次のコードとして認識します。
func
()[
'ol'
].
forEach
(
function
(
t
)
{
handleTag
(
t
)
});
識別子は、物事に名前を付けるために使用され、JavaScriptの様々な構文上の役割で現れます。例えば、変数名や引用符なしのプロパティキーは、有効な識別子である必要があります。識別子は大文字と小文字を区別します。
識別子の最初の文字は、次のいずれかです。
$
)_
)後続の文字は、次のとおりです。
有効な識別子の例
var
ε
=
0.0001
;
var
строка
=
''
;
var
_tmp
;
var
$foo2
;
これにより、JavaScriptコードで様々な言語を使用できるようになりますが、識別子とコメントの両方について、英語を使用することをお勧めします。これにより、コードをできるだけ多くの人に理解してもらうことができ、今日の国際的なコードの普及状況を考えると重要です。
次の識別子は予約語です。これらは構文の一部であり、変数名(関数名やパラメータ名を含む)として使用することはできません。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
次の3つの識別子は予約語ではありませんが、予約語として扱うべきです。
|
|
|
最後に、標準的なグローバル変数の名前も避けるべきです(第23章を参照)。ローカル変数として使用しても問題はありませんが、コードが分かりにくくなります。
予約語は、引用符なしのプロパティキーとして使用できます(ECMAScript 5以降)。
> var obj = { function: 'abc' }; > obj.function 'abc'
識別子の正確なルールについては、Mathias Bynensのブログ記事“Valid JavaScript variable names”を参照してください。
メソッドの呼び出しでは、浮動小数点のドットとメソッド呼び出しのドットを区別することが重要です。そのため、1.toString()
と書くことはできません。次のいずれかの方法を使用する必要があります。
1
..
toString
()
1
.
toString
()
// space before dot
(
1
).
toString
()
1.0
.
toString
()
ECMAScript 5には、よりクリーンなJavaScript、安全性の低い機能の削減、警告の増加、より論理的な動作をもたらす厳格モードがあります。通常の(非厳格)モードは、しばしば「寛容モード」と呼ばれます。
JavaScriptファイルの先頭または<script>
要素の内部に次の行を入力することで、厳格モードをオンに切り替えます。
'use strict'
;
ECMAScript 5をサポートしていないJavaScriptエンジンは、この文を無視します。文字列をこのように(式文として。詳細は文を参照)記述すると、通常は何も行われません。
関数ごとに厳格モードをオンにすることもできます。その場合は、関数を次のように記述します。
function
foo
()
{
'use strict'
;
...
}
これは、厳格モードをすべてに適用すると問題が発生する可能性のあるレガシーコードベースを操作する場合に便利です。
一般的に、厳格モードによって有効になる変更はすべて改善のためのものです。そのため、記述する新しいコードには厳格モードを使用することを強くお勧めします。ファイルの先頭に切り替えるだけです。ただし、2つの注意点があります。
以降のセクションでは、厳格モードの機能について詳しく説明します。ほとんどの場合、そもそも行ってはいけないことについての警告が増えるだけなので、通常は知る必要はありません。
厳格モードでは、すべての変数を明示的に宣言する必要があります。これは、タイプミスを防ぐのに役立ちます。寛容モードでは、宣言されていない変数に代入すると、グローバル変数が作成されます。
function
sloppyFunc
()
{
sloppyVar
=
123
;
}
sloppyFunc
();
// creates global variable `sloppyVar`
console
.
log
(
sloppyVar
);
// 123
厳格モードでは、宣言されていない変数への代入は例外をスローします。
function
strictFunc
()
{
'use strict'
;
strictVar
=
123
;
}
strictFunc
();
// ReferenceError: strictVar is not defined
厳格モードは、関数関連の機能を制限します。
厳格モードでは、すべての関数をスコープの最上位(グローバルスコープまたは関数の内部)で宣言する必要があります。つまり、関数宣言をブロック内に配置することはできません。配置しようとすると、説明的なSyntaxError
が発生します。例えば、V8は「厳格モードコードでは、関数は最上位レベル、または別の関数の直下でのみ宣言できます」と表示します。
function
strictFunc
()
{
'use strict'
;
if
(
true
)
{
// SyntaxError:
function
nested
()
{
}
}
}
これは、関数はブロックの「内部」ではなく、周囲の関数のスコープに作成されるため、そもそも役に立ちません。
この制限を回避するには、変数宣言と関数式を使用して、ブロック内で関数を宣言できます。
function
strictFunc
()
{
'use strict'
;
if
(
true
)
{
// OK:
var
nested
=
function
()
{
};
}
}
関数パラメータのルールは、より制限的です。同じパラメータ名を2回使用することは禁止されており、パラメータと同じ名前のローカル変数も禁止されています。
厳格モードではarguments
オブジェクトがシンプルになります。arguments.callee
とarguments.caller
のプロパティが削除され、arguments
変数に代入できなくなり、arguments
はパラメータの変更を追跡しません(パラメータが変更されても、対応する配列要素は変更されません)。詳細はargumentsの非推奨機能を参照してください。
寛容モードでは、非メソッド関数のthis
の値はグローバルオブジェクト(ブラウザではwindow
。グローバルオブジェクトを参照)です。
function
sloppyFunc
()
{
console
.
log
(
this
===
window
);
// true
}
厳格モードでは、undefined
です。
function
strictFunc
()
{
'use strict'
;
console
.
log
(
this
===
undefined
);
// true
}
これはコンストラクタに役立ちます。例えば、次のコンストラクタPoint
は厳格モードです。
function
Point
(
x
,
y
)
{
'use strict'
;
this
.
x
=
x
;
this
.
y
=
y
;
}
厳格モードのため、誤ってnew
を忘れ、関数として呼び出すと警告が表示されます。
> var pt = Point(3, 1); TypeError: Cannot set property 'x' of undefined
寛容モードでは、警告が表示されず、グローバル変数x
とy
が作成されます。詳細はコンストラクタの実装に関するヒントを参照してください。
厳格モードでは、プロパティの不正な操作は例外をスローします。例えば、読み取り専用プロパティの値を設定しようとすると例外がスローされ、非構成可能なプロパティを削除しようとすると例外がスローされます。前者の例を次に示します。
var
str
=
'abc'
;
function
sloppyFunc
()
{
str
.
length
=
7
;
// no effect, silent failure
console
.
log
(
str
.
length
);
// 3
}
function
strictFunc
()
{
'use strict'
;
str
.
length
=
7
;
// TypeError: Cannot assign to
// read-only property 'length'
}
寛容モードでは、次のようにグローバル変数foo
を削除できます。
delete
foo
厳格モードでは、修飾されていない識別子を削除しようとすると、構文エラーが発生します。グローバル変数は次のように削除できます。
delete
window
.
foo
;
// browsers
delete
global
.
foo
;
// Node.js
delete
this
.
foo
;
// everywhere (in global scope)
厳格モードでは、eval()
関数はより癖がなくなり、評価された文字列で宣言された変数は、eval()
を囲むスコープに追加されなくなります。詳細はeval()を使用したコードの評価を参照してください。
厳格モードでは、さらに2つのJavaScript機能が禁止されています。
with
文は使用できなくなりました(with文を参照)。コンパイル時(コードの読み込み時)に構文エラーが発生します。8進数を使用できなくなりました。寛容モードでは、先頭に0が付いた整数は8進数(8進法)として解釈されます。例:
> 010 === 8 true
厳格モードでは、このようなリテラルを使用すると、構文エラーが発生します。
> function f() { 'use strict'; return 010 } SyntaxError: Octal literals are not allowed in strict mode.