溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript詞法都有哪些

發(fā)布時間:2021-09-30 14:55:49 來源:億速云 閱讀:141 作者:柒染 欄目:web開發(fā)

這篇文章將為大家詳細(xì)講解有關(guān)JavaScript詞法都有哪些,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

InputElement 輸入元素

輸入元素是JS詞法掃描程序拿到的最基本元素了,也就是JS程序源代碼中表達特定意義的"單詞"。

輸入元素共分為四種:

InputElement ::
    WhiteSpace
    Comment
    Token
    LineTerminator

值得注意的是,JS規(guī)范里面其實定義了兩種InputElement ,如下所示

InputElementDiv ::
    WhiteSpace
    Comment
    Token
    LineTerminator
    DivPunctuator
InputElementRegExp ::
    WhiteSpace
    Comment
    Token
    LineTerminator
    RegularExpressionLiteral

這么做是因為JS的除法運算符和正則表達式直接量都使用了/字符,在詞法分析階段,是無法區(qū)分二者的。所以JavaScript的詞法分析有兩種狀態(tài),一種狀態(tài)是掃描InputElementDiv,另一種狀態(tài)是掃描InputElementRegExp,又所以,JS的詞法分析器應(yīng)該有兩種狀態(tài),由語法分析器來設(shè)置,JavaScript的詞法分析和語法分析必須交錯進行。

在學(xué)習(xí)web前端的過程中,難免會遇到很多的問題,這些問題可能會困擾你許久,為此我有個web開發(fā)學(xué)習(xí)交流群( 545667817 ),
里面都是  ITPUB  的小伙伴,并整理了一份最全面前端學(xué)習(xí)資料,從最基礎(chǔ)的HTML+CSS+JS 到移動端HTML5的項目實戰(zhàn)的學(xué)習(xí)資料都有整理,想學(xué)習(xí)的都可以申請加入,大家互相學(xué)習(xí),互相交流,共同進步,每日分享不同的學(xué)習(xí)資料!

下面的一個例子說明了除法和正則表達式寫法的沖突問題:

if(a+b)/a/g;
(a+b)/a/g;

可以看到完全相同的/a/g(而且前面一段字符也相同),可能被理解為除法或者正則表達式。因為必須區(qū)分所處的語法環(huán)境,所以單單靠詞法分析無論如何也無法決定該用除法還是正則表達式來理解。

因為基本上沒有任何編輯環(huán)境會對文本做語法分析,這個問題也造成了很多語法著色系統(tǒng)無法很好地處理JS正則表達式。

以非語言實現(xiàn)者的角度,完全應(yīng)該按照最上面一段產(chǎn)生式去理解JS的詞法。

WhiteSpace空白符

這個詞相信不用細(xì)說,所有JS程序員都比較熟悉。JavaScript接受5種ASCII字符為空白符,BOM以及Unicode分類中所有屬于whitespace分類的字符也可以作為空白符使用:

WhiteSpace ::
    <TAB>
    <VT>
    <FF>
    <SP>
    <NBSP>
    <BOM>
    <USP>

其中,<TAB>是U+0009,是縮進TAB符,也就是字符串中寫的'\t'。

<VT>是U+000B,也就是垂直方向的TAB符'\v',這個字符在鍵盤上很難打出來,所以很少用到。

<FF>是U+000C,F(xiàn)orm Feed,分頁符,字符串直接量中寫作'\f',現(xiàn)代已經(jīng)很少有打印源程序的事情發(fā)生了,所以這個字符在JS源代碼中很少用到。

<SP>是U+0020,就是最普通的空格了。

<NBSP>是U+00A0,非斷行空格,它是SP的一個變體,在文字排版中,可以避免因為空格在此處發(fā)生斷行,其它方面和普通空格完全一樣。多數(shù)的JS編輯環(huán)境都會把它當(dāng)做普通空格(因為一般源代碼編輯環(huán)境根本就不會自動折行……)

<BOM>是U+FEFF,這是ES5新加入的空白符,是Unicode中的零寬非斷行空格,在以UTF格式編碼的文件中,常常在文件首插入一個額外的U+FEFF,解析UTF文件的程序可以根據(jù)U+FEFF的表示方法猜測文件采用哪種UTF編碼方式。這個字符也叫做"bit order mark"。

<USP>表示Unicode中所有的"separator, space(Zs)"分類中的字符,包括:

字符名稱你瀏覽器中的顯示
U+0020SPACE
U+00A0NO-BREAK SPACE
U+1680OGHAM SPACE MARK
U+180EMONGOLIAN VOWEL SEPARATOR
?
U+2000EN QUAD
U+2001EM QUAD
U+2002EN SPACE
U+2003EM SPACE
U+2004THREE-PER-EM SPACE
U+2005FOUR-PER-EM SPACE
U+2006SIX-PER-EM SPACE
U+2007FIGURE SPACE
U+2008PUNCTUATION SPACE
U+2009THIN SPACE
U+200AHAIR SPACE
U+202FNARROW NO-BREAK SPACE
U+205FMEDIUM MATHEMATICAL SPACE
U+3000IDEOGRAPHIC SPACE

注意雖然JS規(guī)范承認(rèn)這些字符可以被用做空白字符,但是除非對源代碼的打印、排版有特別的需求,還是應(yīng)該盡量使用<SP>,尤其是考慮到,相當(dāng)一批字體無法支持<USP>中的全部字符。

根據(jù)一些團隊的編碼規(guī)范,<TAB>常常用于縮進。編程語言中關(guān)于用<TAB>還是四個<SP>做縮進的爭論從未停止過,此處就不加討論了。

JS中,WhiteSpace的大部分用途是分隔token和保持代碼整齊美觀,基本上詞法分析器產(chǎn)生的WhiteSpace都會被語法分析器直接丟棄。

所以一些WhiteSpace能夠被去掉而完全不影響程序的執(zhí)行效果。但是也有一些WhiteSpace必須存在的情況,考慮下面代碼:

1 .toString();
1.toString(); //報錯

上面一段代碼中,空白符分隔了1和.,因此它們被理解為兩個token。

1.["toString"]();
1 .["toString"](); //報錯

相反的情況。

LineTerminator行終結(jié)符

這個也是一個非常常見的概念了,JS中只提供了4種字符作為換行符:

LineTerminator ::
    <LF>
    <CR>
    <LS>
    <PS>

其中,<LF>是U+000A,就是最正常換行符,在字符串中的'\n'。

<CR>是U+000D,這個字符真正意義上的"回車",在字符串中是'\r',在一部分Windows風(fēng)格文本編輯器中,換行是兩個字符\r\n。

<LS>是U+2028,是Unicode中的行分隔符。

<PS>是U+2029,是Unicode中的段落分隔符。

大部分LineTerminator在被詞法分析器掃描出之后,會被語法分析器丟棄,但是換行符會影響JS的兩個重要語法特性:自動插入分號和"no line terminator"規(guī)則。

考慮下面三段代碼:

var a = 1 , b = 1;
a
++
b

按照J(rèn)S語法的自動插入分號規(guī)則,代碼解釋可能產(chǎn)生歧義。

但是因為后自增運算符有no line terminator的限制,所以實際結(jié)果等價于:

var a = 1 , b = 1;
a;
++b;

考慮以下兩段代碼:

return
    123;
return 123;

因為return有no line terminator的限制,所以第一段代碼實際等同于

return;
123;

Comment注釋

JS的注釋分為單行注釋和多行注釋兩種: 

Comment :: 
    MultiLineComment 
    SingleLineComment

多行注釋定義如下:

MultiLineComment :: 
    /* MultiLineCommentCharsopt */ 
MultiLineCommentChars :: 
    MultiLineNotAsteriskChar MultiLineCommentCharsopt 
    * PostAsteriskCommentCharsopt PostAsteriskCommentChars :: 
    MultiLineNotForwardSlashOrAsteriskChar MultiLineCommentCharsopt 
    * PostAsteriskCommentCharsopt MultiLineNotAsteriskChar :: 
    SourceCharacter but not asterisk * 
MultiLineNotForwardSlashOrAsteriskChar :: 
    SourceCharacter but not forward-slash / orasterisk *

這個定義略微有些復(fù)雜,實際上這就是我們所熟知的JS多行注釋語法的嚴(yán)格描述。

多行注釋中允許自由地出現(xiàn)MultiLineNotAsteriskChar ,也就是除了*之外的所有字符。而每一個*之后,不能出現(xiàn)正斜杠符/

單行注釋則比較簡單:

SingleLineComment ::
    // SingleLineCommentCharsoptSingleLineCommentChars ::
    SingleLineCommentChar SingleLineCommentCharsoptSingleLineCommentChar ::
    SourceCharacter but not LineTerminator

除了四種LineTerminator之外,所有字符都可以作為單行注釋。

一般情況下,不論是單行還是多行注釋都不會影響程序的意義,但是含有LineTerminator的多行注釋會影響到自動插入分號規(guī)則:

return/*
    */123;
return /**/ 123;

兩者會產(chǎn)生不同的效果。

Token

Token是JS中所有能被引擎理解的最小語義單元。

JS中有4種Token:

Token ::
    IdentifierName 
    Punctuator 
    NumericLiteral 
    StringLiteral

如果不考慮除法和正則的沖突問題,Token還應(yīng)該包括RegularExpressionLiteral,而Punctuator中也應(yīng)該添加 / 和 /=兩種符號。

IdentifierName

IdentifierName的定義為:

IdentifierName ::
    IdentifierStart
    IdentifierName IdentifierPart
IdentifierStart ::
    UnicodeLetter
    $
    _ 
    \ UnicodeEscapeSequence
IdentifierPart ::
    IdentifierStart
    UnicodeCombiningMark
    UnicodeDigit
    UnicodeConnectorPunctuation
    <ZWNJ>
    <ZWJ>

IdentifierName可以以美元符$下劃線_ 或者Unicode字母開始,除了開始字符以外,IdentifierName中還可以使用Unicode中的連接標(biāo)記、數(shù)字、以及連接符號。

IdentifierName的任意字符可以使用JS的Unicode轉(zhuǎn)義寫法,使用Unicode轉(zhuǎn)義寫法時,沒有任何字符限制。

IdentifierName可以是Identifier、NullLiteral、BooleanLiteral或者keyword,在ObjectLiteral中,IdentifierName還可以被直接當(dāng)做屬性名稱使用。

僅當(dāng)不是保留字的時候,IdentifierName會被解析為Identifier。

 UnicodeLetter, UnicodeCombiningMark, UnicodeDigit, UnicodeConnectorPunctuation各自對應(yīng)幾個Unicode的分類。

JS詞法名Unicode分類名code字符數(shù)
UnicodeLetterUppercase letterLu1441
Lowercase letterLl1751
Titlecase letterLt31
Modifier letterLm237
Other letterLo11788
Letter numberNl224
UnicodeCombiningMarkNon-spacing markMn1280
Combining spacing markMc353
UnicodeDigitDecimal numberNd460
UnicodeConnectorPunctuationConnector punctuationPc10

注意<ZWNJ>和<ZWJ>是ES5新加入的兩個格式控制字符,但是目前為止實測還沒有瀏覽器支持。

JS中的關(guān)鍵字有:

Keyword :: one of
    break do instanceof typeof case else new var catch finally return void continue for switch while debugger function this with default if throw delete in try

還有7個為了未來使用而保留的關(guān)鍵字: 

FutureReservedKeyword :: one of
    class enum extends super const export import

在嚴(yán)格模式下,有一些額外的為未來使用而保留的關(guān)鍵字:

implements let private public interface package protected static yield

除了這些之外,NullLiteral: 

NullLiteral ::
    null

和BooleanLiteral:

BooleanLiteral ::
    true false

也是保留字,不能用于Identifier。

Punctuator

JavaScript使用48個運算符,因為前面提到的除法和正則問題, /和/=兩個運算符被拆分為DivPunctuator。其余的運算符為:

 Punctuator :: one of
    { } ( ) [ ] . ; , < > <= >= == != === !== + - * % ++ -- << >> >>> & | ^ ! ~ && || ? : = += -= *= %= <<= >>= >>>= &= |= ^=

所有運算符在語法分析器中作為不同的symbol出現(xiàn)。

NumericLiteral

JS規(guī)范中規(guī)定的數(shù)字直接量可以支持兩種寫法:十進制和十六進制整數(shù),盡管標(biāo)準(zhǔn)中沒有提到,但是大部分JS實現(xiàn)還支持以0開頭的八進制整數(shù)寫法。

所以實際上JS的NumericLiteral產(chǎn)生式應(yīng)該是這樣的:

NumericLiteral :: 
    DecimalLiteral
    HexIntegerLiteral
    OctalIntegerLiteralnot-standard

只有十進制可以表示浮點數(shù),DecimalLiteral 定義如下:

DecimalLiteral ::
    DecimalIntegerLiteral . DecimalDigitsopt ExponentPartopt
    . DecimalDigits ExponentPartopt
    DecimalIntegerLiteral ExponentPartoptDecimalIntegerLiteral ::
    0 
    NonZeroDigit DecimalDigitsoptDecimalDigits ::
    DecimalDigit
    DecimalDigits DecimalDigit
DecimalDigit :: one of
    0 1 2 3 4 5 6 7 8 9
NonZeroDigit:: one of
    1 2 3 4 5 6 7 8 9
ExponentPart::
    ExponentIndicator SignedInteger
ExponentIndicator :: one of
    e E
SignedInteger ::
    DecimalDigits
    + DecimalDigits
    - DecimalDigits

JS中的StringLiteral支持單引號和雙引號兩種寫法。

十進制數(shù)的小數(shù)點前和小數(shù)點后均可以省略, 所以 1. 和 .1 都是合法的數(shù)字直接量,特別地,除了0之外,十進制數(shù)不能以0開頭(這其實是為了八進制整數(shù)預(yù)留的)。

.同時還是一個Punctuator,在詞法分析階段,.123 應(yīng)該優(yōu)先被嘗試?yán)斫鉃?NumericLiteral ,而非 Punctuator NumericLiteral。

十六進制整數(shù)產(chǎn)生式如下:

HexIntegerLiteral ::
    0x HexDigit
    0X HexDigit
    HexIntegerLiteral HexDigit
HexDigit :: one of
    0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F

JS中支持0x標(biāo)記的大小寫形式,十六進制數(shù)字中的大小寫也可以任意使用。

八進制整數(shù)是非標(biāo)準(zhǔn)的,但是大多數(shù)引擎都支持:

OctalIntegerLiteral :: 
    0 OctalDigit
    OctalIntegerLiteral OctalDigit 
OctalDigit :: one of
    0 1 2 3 4 5 6 7

StringLiteral

JS中的StringLiteral支持單引號和雙引號兩種寫法。

StringLiteral ::
    " DoubleStringCharactersopt "
    ' SingleStringCharactersopt '

單雙引號的區(qū)別僅僅在于寫法,在雙引號字符串直接量中,雙引號必須轉(zhuǎn)義,在單引號字符串直接量中,單引號必須轉(zhuǎn)義

DoubleStringCharacters ::
    DoubleStringCharacter DoubleStringCharactersoptSingleStringCharacters ::
    SingleStringCharacter SingleStringCharactersoptDoubleStringCharacter ::
    SourceCharacter but not double-quote " or backslash \ or LineTerminator
    \ EscapeSequence
    LineContinuation
SingleStringCharacter ::
    SourceCharacter but not single-quote ' orbackslash \ or LineTerminator
    \ EscapeSequence
    LineContinuation

字符串中其他必須轉(zhuǎn)義的字符是\和所有換行符。

JS中支持四種轉(zhuǎn)義形式,還有一種雖然標(biāo)準(zhǔn)沒有定義,但是大部分實現(xiàn)都支持的八進制轉(zhuǎn)義

EscapeSequence ::
    CharacterEscapeSequence
    0 [lookahead no DecimalDigit]
    HexEscapeSequence
    UnicodeEscapeSequence
    OctalEscapeSequencenot-standard

第一種是單字符轉(zhuǎn)義。 即一個反斜杠\ 后面跟一個字符這種形式。

CharacterEscapeSequence ::
    SingleEscapeCharacter
    NonEscapeCharacter
SingleEscapeCharacter :: one of
    ' " \ b f n r t v
NonEscapeCharacter ::
    SourceCharacter but notEscapeCharacter or LineTerminator

有特別意義的字符包括有SingleEscapeCharacter所定義的9種,見下表:

轉(zhuǎn)義字符轉(zhuǎn)義結(jié)果你瀏覽器中的顯示
'U+0022
"
"U+0027
'
\U+005C
\
bU+0008

fU+000C
nU+000A
rU+000D
tU+0009
vU+000B

除了這9種字符、數(shù)字、x和u以及所有的換行符之外,其它字符經(jīng)過\轉(zhuǎn)義都是自身。

十六進制轉(zhuǎn)義只支持兩位,也就是說,這種寫法只支持ASCII字符:

HexEscapeSequence ::
    x HexDigit HexDigit

Unicode轉(zhuǎn)義可以支持BMP中的所有字符:

UnicodeEscapeSequence ::
    u HexDigit HexDigit HexDigit HexDigit

LineContinuation可以被理解為一種特別的轉(zhuǎn)義。寫字符串直接量時靈活使用LineContinuation可以增加可讀性。

LineContinuation ::
    \ LineTerminatorSequence
LineTerminatorSequence ::
    <LF>
    <CR> [lookahead no <LF> ]
    <LS>
    <PS>
    <CR>
    <CR> <LF>

為了適應(yīng)Windows風(fēng)格的文本,JS把"\r\n"作為一個換行符使用。

注意因為CR在某些windows風(fēng)格的編輯器中沒法顯示出來,所以亂用的話會產(chǎn)生奇怪的效果。

RegularExpressionLiteral

正則表達式由Body和Flags兩部分組成:

RegularExpressionLiteral ::
    / RegularExpressionBody / RegularExpressionFlags

其中Body部分至少有一個字符,第一個字符不能是*(因為/*跟多行注釋有詞法沖突。)

RegularExpressionBody ::
    RegularExpressionFirstChar RegularExpressionChars
RegularExpressionChars ::
    [empty]
    RegularExpressionChars RegularExpressionChar
RegularExpressionFirstChar ::
    RegularExpressionNonTerminator but not * or \ or / or [ 
    RegularExpressionBackslashSequence
    RegularExpressionClass
RegularExpressionChar ::
    RegularExpressionNonTerminator but not \ or / or [ 
    RegularExpressionBackslashSequence
    RegularExpressionClass

除了\  / 和 [ 三個字符之外,JS正則表達式中的字符都是普通字符。

RegularExpressionBackslashSequence ::
    \ RegularExpressionNonTerminator
RegularExpressionNonTerminator ::
    SourceCharacter but not LineTerminator

用 \和一個非換行符可以組成一個RegularExpressionBackslashSequence,這種方式可以用于表示正則表達式中的有特殊意義的字符。

RegularExpressionClass ::
    [ RegularExpressionClassChars ]

正則表達式中,用一對方括號表示class。class中的特殊字符僅僅為]和\。

class允許為空。

class中也支持轉(zhuǎn)義。

RegularExpressionClassChars ::
    [empty]
    RegularExpressionClassChars RegularExpressionClassChar
RegularExpressionClassChar ::
    RegularExpressionNonTerminator but not ] or \ 
    RegularExpressionBackslashSequence

正則表達式中的flag在詞法階段不會限制字符,雖然只有ig幾個是有效的,但是任何IdentifierPart序列在詞法階段都會被認(rèn)為是合法的。

RegularExpressionFlags ::
    [empty]
    RegularExpressionFlags IdentifierPart

一些詞法分析認(rèn)為合法,但是實際上不符合正則語法的例子:

附表 JS詞法摘要

英文名名稱簡述示例
InputElement輸入元素一切JS中合法的"詞"
┣Comments注釋用于幫助閱讀的文本
┃┣SingleLineComments單行注釋以//起始的單行注釋
//I'm comments
┃┗MultiLineComments多行注釋以/*起始以*/結(jié)束的注釋
/*I'm comments,too.*/
┣WhiteSpace空白起到分隔或者保持美觀作用的空白字符
┣Token詞法標(biāo)記一切JS中有實際意義的詞法標(biāo)記
┃┣IdentifierName標(biāo)識名稱以字母或_或$開始的一個單詞,可以用于屬性名
┃┃┣Identifier標(biāo)識符非保留字的IdentifierName,可以用于變量名或者屬性名
abc
┃┃┣Keyword關(guān)鍵字有特殊語法意義的IdentifierName
while
┃┃┣NullLiteralNull直接量表示一個Null類型的值
null
┃┃┗BooleanLiteral布爾直接量表示一個Boolean類型的值
true
┃┣Punctuator標(biāo)點符號表示特殊意義的標(biāo)點符號
*
┃┣NumericLiteral數(shù)字直接量表示一個Number類型的值
.12e-10
┃┣StringLiteral字符串直接量表示一個String類型的值
"Hello world!"
┃┗RegularExpressionLiteral正則表達式直接量表示一個RegularExpression類的對象
/[a-z]+$$/g
┗LineTerminator行終結(jié)符起到分隔或者保持美觀作用的換行字符,可能會影響自動插入分號

附表 所有JS詞法中的不可見字符

簡寫字符概述
<TAB>U+0009tab符,用于空白
<VT>U+000B豎向tab符,用于空白
<FF>U+000C換頁,用于空白
<SP>U+0020空格,用于空白
<NBSP>U+00A0非斷行空格,用于空白
<BOM>U+FEFF零寬非斷行空格,字節(jié)序標(biāo)記,用于空白
<ZWNJ>U+200C零寬非連接符,用于標(biāo)識符
<ZWJ>U+200D零寬連接符,用于標(biāo)識符
<LF>U+000A換行,用于行終結(jié)符
<CR>U+000D回車,用于行終結(jié)符
<LS>U+2028行分隔符,用于行終結(jié)符
<PS>U+2029頁分隔符,用于行終結(jié)符

關(guān)于JavaScript詞法都有哪些就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI