JavaScript语法就是指构成合法的JavaScript代码的所有规则和特征的集合,它包括词法和句法。词法包括字符编码、命名规则、标识符、关键字、注释规则、特殊字符用法等。
字符编码
JavaScript程序使用Unicode字符集编写。Unicode字符集中每个字符使用两个字节来表示,这意味着用户可以使用中文来命名JavaScript变量。
【示例】启动Dreamweaver,新建文档,保存为test.html,在页面嵌入<script>标签,然后在该标签中输入下面代码,则可以正常执行,效果如图1所示。
<!doctype html> <html> <head> <meta charset="utf-8"> <title></title> <script> var 人名 = "老张"; function 睡觉(谁){ alert(谁 + ":快睡觉!都半夜鸡叫了。"); } 睡觉(人名); </script> </head> <body> </body> </html>
图1 使用中文编写脚本运行效果
【注意】
在第1、2版本中,ECMAScript标准只允许Unicode字符出现在注释,或者引号包含的字符串中,其他地方必须使用ASCII字符集。考虑到JavaScript版本的兼容性,以及开发习惯,不建议使用汉字来命名变量或函数名。
【提示】
由于JavaScript脚本一般都寄存在网页中,并最终由浏览器来解释,因此在考虑到JavaScript语言编码的同时,还要顾及嵌入页面的字符编码,以及浏览器支持的编码。一般建议保持页面字符编码与JavaScript编码一致,避免出现乱码。
区分大小写
JavaScript严格区分大小。为了避免输入错误,用户可以采用一致的字符大小写形式,例如,遵循习惯所有字符都采用小写形式,这样可以有效减少输入错误。不过有两点例外。(1)定义JavaScript构造函数时,可以让函数名首字母大写。
【示例1】下面脚本调用预定义的构造函数Date(),创建一个时间对象,最后把时间对象转换为字符串显示出来。
d = new Date(); // 获取当前日期和时间 alert(d.toString()); // 显示日期
(2)如果变量名是多个词语连写,可以考虑部分字符大写。
例如,骆驼命名法就是在名称中每一个逻辑断点都有一个大写字母来标记。
printEmployeePaychecks();
如果使用下画线命名法,则可以按如下方式输入:
print_employee_paychecks();
【提示】
在过渡型HTML版本中,HTML标签名和属性名是不区分大小写的,标签属性可以以任意大小写形式输入,但是JavaScript脚本在访问DOM节点时是区分大小的,为了避免错误,应该统一标签名和属性名为小写形式。
【示例2】下面代码在部分早期浏览器中JavaScript有可能仅会获取第一个div元素的节点,而忽略掉第2、3个div元素节点。
<div></div> <Div></Div> <DIV></DIV> <script> var div =document.getElementsByTagName("div") alert(div.length) </script>
标识符
标识符(identifier)表示名称的意思,JavaScript标识符主要包括变量名、函数名、参数名和属性名。合法的标识符命名应该注意如下几条规则,这些规则与C语系其他语言基本相同。
- 第一个字符必须是字母、下画线(_)或美元符号($)。
- 除了第一个字符外,其他位置字符可以使用字母、数字、下画线、美元符号。在ECMAScript v3中,用户可以使用完整的Unicode字符集来命名标识符,但是不建议使用。
- 标识符名称不能够与JavaScript关键字或保留字同名。
- 在ECMAScript v3版本中,可以在标识符中使用Unicode转义序列。例如,标识符a可以写成“\u0061”(Unicode转义序列),然后就可以在变量中使用这个转义序列代替字符本身。
【示例1】下面这两行代码中,先定义一个变量abc,变量abc中的a被转义序列表示,为变量abc初始化并在对话框中显示出来。
var \u0061bc = "标识符abc(变量)中a字符的Unicode转义序列是\u0061"; alert(\u0061bc);
在书写时,转义序列不是很方便,一般很少这样使用,只有在特殊情况使用转义序列来传递或显示特殊字符,如JavaScript关键字、程序脚本等。
【注意】
JavaScript有一些保留字,不能用作标识符:arguments、break、case、catch、class、const、continue、debugger、default、delete、do、else、enum、eval、export、extends、false、finally、for、function、if、implements、import、in、instanceof、interface、let、new、null、package、private、protected、public、return、static、super、switch、this、throw、true、try、typeof、var、void、while、with、yield。
另外,还有三个词虽然不是保留字,但是因为具有特别含义,也不应该用作标识符:Infinity、NaN、undefined。
【示例2】下面这些都是合法的标识符。
arg0 _tmp $elem π
【示例3】下面这些则是不合法的标识符。
1a // 第一个字符不能是数字 23 // 同上 *** // 标识符不能包含星号 a+b // 标识符不能包含加号 -d // 标识符不能包含减号或连词线
【示例4】中文是合法的标识符,可以用作变量名。
var 临时变量 = 1;
直接量
直接量(literal)是在程序中直接显示出来的值,如字符串、数值、布尔值、正则表达式、对象初始化、数组初始化等。
【示例】下面代码分别定义了字符串、数值、布尔值、正则表达式、特殊值、对象和数组直接量。
"字符串直接量" // 字符串直接量 123456 // 数值直接量 true // 布尔值直接量 /^ab.*/g // 正则表达式直接量 null // 特殊值直接量 {a:1,b:2} // 对象初始化直接量 [1,2] // 数组初始化
关键字和保留字
ECMA-262描述了一组具有特定用途的关键字,这些关键字可用于表示控制语句的开始或结束,或者用于执行特定操作等。按照规则,关键字也是语言保留的,不能用作标识符。以下是ECMAScript的全部关键字,如表1所示。
表1 ECMAScript关键字
关键字 | 关键字 | 关键字 | 关键字 | 关键字 |
---|---|---|---|---|
break | delete | function | return | typeof |
case | do | if | switch | var |
catch | else | in | this | void |
continue | false | instanceof | throw | while |
debugger | finally | new | true | with |
default | for | null | try |
ECMA-262还描述了另外一组不能用作标识符的保留宇。保留字就是在目前还没有任何特定的用途,但它们有可能在将来版本中被用作关键字。
- ECMAScript 5保留关键字:
class、const、enum、export、extends、import、super
- 在严格模式下,下面关键字是保留字:
implements、let、private、public、yield、interface、package、protected、static
- 在严格模式下,严格限制下面关键字用做变量名、函数名或参数名:
arguments、eval
- ECMAScript 3将Java的所有关键字都列为保留字,但ECMAScript 5放宽了限制。
如果希望代码能在基于ECMAScript 3实现的解释器上运行的话,应当避免使用这些关键字作为标识符,如表2所示。
表2 ECMAScript 3保留字
保留字 | 保留字 | 保留字 | 保留字 | 保留字 |
---|---|---|---|---|
abstract | double | goto | native | static |
boolean | enum | implements | package | super |
byte | export | import | private | synchronized |
char | extends | int | protected | throws |
class | final | interface | public | transient |
const | float | long | short | volatile |
JavaScript预定义了很多全局变量和函数,应当避免把它们的名字用做变量名和函数名,如表3所示。
表3 JavaScript预定义全局变量和函数
预定义 | 预定义 | 预定义 | 预定义 | 预定义 |
---|---|---|---|---|
arguments | encodeURL | Infinity | Number | RegExp |
Array | encodeURLComponent | isFinite | Object | String |
Boolean | Error | isNaN | parseFloat | SyntaxError |
Date | eval | JSON | parseInt | TypeError |
decodeURL | EvalError | Math | RangeError | undefined |
decodeURLComponent | Function | NaN | ReferenceError | URLError |
JavaScript实现可能定义独有的全局变量和函数,每一种特定的JavaScript运行环境(客户端、服务器端等)都有自己的一个全局属性列表,这些都需要注意。
分隔符
空格、制表符、换行符、换页符等在JavaScript程序中被统称为分隔符,用来分隔代码中的各种记号(如标识符、关键字、直接量、注释等信息),在解析时,JavaScript会忽略这些分隔符。
【示例1】对于下面代码块:
function toStr(a){return a.toString();}
可以使用如下任意格式进行排版:
function toStr(a){ return a.toString();} //或者: function toStr(a){ return a.toString(); } //或者: function toStr(a) { return a.toString(); }
用户可以根据阅读习惯格式代码显示。一般JavaScript编辑器也会提供代码格式化功能。
【拓展】
(1)分隔符虽然无实在意义,但是脚本中不能够缺少分隔符,特别是在标识符与关键字之间必须使用分隔符进行分隔,否则JavaScript会误认为它们是一个完整的标记而无法正确识别。
【示例2】在下面语句中,把关键字function与标识符toStr连在一起,以及把关键字return与toString标识符连在一起都是不正确的:
functiontoStr(a){returna.toString();} // 错误写法
在它们之间必须使用分隔符进行分隔:
function toStr(a){return a.toString();} // 正确写法
(2)JavaScript解析器一般采用最长行匹配原则,并在此基础上忽略代码中的分隔符。所谓最长行匹配原则,就是在一行内如果能够正确解析,那么就在一行内进行解析,否则会继续读取下一行代码,直到能够被正确解析为止。因此,用户不能够无节制地使用换行符。
【示例3】下面代码就会返回错误的信息。
function toStr(a){ return a.toString(); // 错误的换行 } alert(toStr("abc")); // 返回undefined
这是因为return作为一个独立语句,JavaScript解析器可以正确解析它,虽然它后面没有分号来标识该句的结束,但是解析器在正确解析的前提下,会自动为其补加一个分号,以表示该句已经结束。最后解析的脚本就变成了如下样式:
function toStr(a){ return; a.toString(); } alert(toStr("abc")); // 返回undefined
出现这个错误的根源是因为分号不是JavaScript语句结束的唯一标志。很多时候,JavaScript会自动把换行符也看做是一句结束的标志。因此,当使用换行符时,应该防止类似错误的发生。上面代码的正确写法应该是:
function toStr(a){ return a.toString(); } alert(toStr("abc")); // 返回字符串abc
(3)不能在标识符、关键字等名称内插入分隔符,否则JavaScript会误认为它们是两个独立的标记并进行解析。
【示例4】在下面函数中,错误地使用空格把toString()方法分隔为两部分,则JavaScript会误认为它们是to关键字和String()自定义函数两个实词标记。
function toStr(a){ return a.to String(); // 错误分隔符 }
(4)如果分隔符位于字符串或者正则表达式直接量内,则JavaScript会认为它们是具有一定语义的字符并进行解析。
【示例5】在下面代码中,变量a和b被赋予相同的字符串,但是变量b中间插入了空格,则比较结果发现它们是不相同的。
var a = "空格、制表符和换行符"; var b = "空格 、 制表符 和 换行符"; alert((a==b).toString()); // 返回false
注释
注释就是不被解析的语句行或段。JavaScript把位于“//”字符后一行内的所有字符视为注释信息,从而忽略掉。
【示例1】下面几条注释语句可以位于代码段的不同位置,分别描述不同区域代码的功能。
// 代码段前注释,概述代码段的功能 function toStr(a){ // 语句间注释,介绍函数 // 代码段中注释,区域代码功能描述 return a.toString(); // 语句结束后注释,语句作用描述 }
在使用单行注释时,在“//”符号后面的同一行内就不要输入任何代码,否则都被视为注释文本而忽略掉。
【示例2】还可以使用“/*”和“*/”符号来包含多行注释信息。例如:
/* 多行注释 多行注释 */
在多行注释中,包含在“/*”和“*/”符号之间的任何文本都视为注释文本而忽略掉。
【拓展】
此外,由于历史上JavaScript兼容HTML代码的注释,所以<!--和-->也被视为单行注释。例如:
x = 1; <!-- x = 2; --> x = 3;
上面代码中,只有x = 1会执行,其他的部分都被注释掉了。
注意,-->只有在行首,才会被当成单行注释,否则就是一个运算符。例如:
function countdown(n) { while (n --> 0) console.log(n); } countdown(3) // 2 // 1 // 0
上面代码中,n --> 0实际上会当作n-- > 0,因此输出2、1、0。
转义序列
在有些计算机硬件和软件里,无法显示或输入Unicode字符全集。为了支持那些使用老旧技术的程序员,JavaScript定义了一种特殊序列,使用6个ASCII字符来代表任意16位Unicode内码。
这些Unicode转义序列均以\u为前缀,其后跟随4个十六进制数,即使用数字以及大写或小写的字母A~F表示。这种Unicode转义写法可以用在JavaScript字符串直接量、正则表达式直接量和标识符中,但关键字除外。
【示例】字符“码”的Unicode转义写法为\u7801,如下两个JavaScript字符串是完全一样的:
document.write("字符编码"); document.write("字符编\u7801");
Unicode转义写法也可以出现在注释中,但由于JavaScript会将注释忽略,它们只是被当成上卞文中的ASCII字符处理,而且并不会被解析为其对应的Unicode字符。