ECMA-262第5版(ES5)定义的ECMAScript是目前使用最广泛的一个版本。
ECMA-262第6版(ES6)在浏览器中的实现程度次之,2017年底,大多数主流浏览器都支持了ES6。
本文所介绍的语法基于ECMAScript第6版。
语法
区分大小写
与大多数编程语言一样,严格区分标识符大小写。abc和ABC是完全不同的两个变量名。
标识符
标识符,即变量名、函数名、属性或参数。
- 首字符必须是字母、下划线(_)、美元符号($)。
- 剩下的字符可以是字母、下划线、美元符号或数字。
按照惯例,ECMAScript推荐使用驼峰命名法:第一个单词首字母小写,后面每个单词首字母大写。
注意:关键字、保留字、true、false和null不能用作标识符。
注释
ECMAScript采用C语言风格注释,包括单行注释和块注释。
1 | //单行注释 |
严格模式
ECMAScript5增加了严格模式(strict mode)的概念。
严格模式是为了不破坏ECMAScript3语法,ES3中一些不规范的写法会被处理,对于不安全的活动抛出错误。
开启严格模式:
1 | //在脚本开头加入,对整个脚本起作用。 |
语句
语句采用C风格,每个语句以分号结束。
并且使用花括号({})来标识代码块。
1 | let sum = a + b; |
注意:分号不是强制性的,但是规范的语句应当以分号结尾,这样会减少很多可能发生的错误。
关键字与保留字
ECMAScript262描述了一组保留的关键字。这些关键字有特殊用途,不能用作标识符或属性名:
1 | break do in typeof |
规范中也描述了一组未来保留字,以后可能会有特殊用途:
1 | 始终保留: |
变量
ECMAScript变量是松散类型的(弱类型),即变量可以保存任何类型的数据。
有三个关键字可以声明变量:var、const和let。
其中,var在ECMAScript所有版本中都可以使用。
而const和let只能在ECMAScript6及更晚的版本中使用。
var关键字
定义格式:var + 变量名
1 | var test; //声明变量,默认会保存特殊值undefined。 |
var声明作用域
在函数体外声明的变量默认为全局变量。
在函数体内声明的变量默认为局部变量。
1 | function test(){ |
如果在函数体内部省略var关键字,声明的则是全局变量:
1 | function test(){ |
但是,不推荐省略var,因为不确定省略var是不是有意为之。严格模式下给这样未声明的变量赋值会报错。
注意:在严格模式下,不能定义名为eval和arguments的变量。否则,导致语法错误。
var声明提升
1 | function test(){ |
所有声明的变量会自动被拉升到作用域的顶部。但是还是建议在最顶部声明变量。
var多次声明同一变量
反复多次使用var声明同一个变量也没有问题:
1 | function test(){ |
let声明
let和var作用差不多,但有着很重要的区别。
最明显的区别是:let声明的范围是块作用域,而var声明的范围是函数作用域。
1 | if (true){ |
1 | if (true){ |
之所以会报错是因为,let声明的范围是块作用域,只在if语句块中有效。
并且,let也不容易多次声明同一个变量。
暂时性死区
let与var的另一个重要区别是:let声明的变量不会被提升。
1 | // name会被提升 |
全局声明
与var关键字不同,使用let关键字在全局作用域中声明的变量不会成为window对象的属性。
1 | var name = 'Matt'; |
条件声明
不能使用let进行条件声明,因为let具有块作用域。
for循环中的let声明
在let出现之前,循环定义的迭代变量会渗透到循环体外部:
1 | for (var i = 0; i < 5; i++){ |
使用let后,这个问题就消失了。因为let作用域是块作用域。
const声明
const声明与let基本相同,唯一一个重要区别是用它声明变量时必须同时初始化变量,且尝试修改const声明的变量会导致运行时错误。
1 | const age = 26; |
const声明的限制只用于它指向的变量的引用。
如果const变量指向一个对象,这个对象的内部属性是可修改的,只是这个变量只能指向这个对象。
推荐的声明风格
不使用var
限制自己只使用let和const有助于提升代码质量,因为变量有了明确的作用域、声明位置。
const优先 let次之
如果某个变量不需要修改,优先使用const,这样不会导致误修改,能够迅速排查错误。
数据结构
ECMAScript有6种简单数据类型(也称原始类型):
- Undefined
- Null
- Boolean
- Number
- String
- Symbol
Symbol类型是ECMAScript6新增的。
还有一种复杂数据类型 Object(对象) ,这是一种无序名值对。
ECMAScript中不能定义自己的数据类型,所有数据都可使用上述7种类型来表示。
typeof操作符
因为ECMAScript的类型系统是松散的,所以需要一种手段确定变量的类型。
typeof操作符会返回下列字符串之一:
- “undefined”表示值未定义
- “boolean”表示值为布尔值
- “string”表示值为字符串
- “number”表示值为数值
- “object”表示值为对象(而不是函数)或null
- “function”表示值为函数
- “symbol”表示值为符号
1 | let message = "some string"; |
Undefined类型
Undefined类型只有一个值,特殊值undefined。
当使用var或let声明变量但未初始化时,相当于赋初值undefined。
Null类型
Null类型只有一个值,特殊值null。
逻辑上讲,null表示一个空对象的指针。
Boolean类型
Boolean(布尔值)类型是ECMAScript中最常用的类型。
有两个字面值:true和false,不同于数值类型。
通过Boolean()函数可以将非空字符串、非0、任意对象转换成true。
Number类型
NaN
NaN是一个特殊值,意思是“不是数值”。
非法运算,如分子是0时,会返回NaN。
如果分子非0,分母是0,相应的会返回Infinity和-Infinity表示正负无穷。
可以使用isNaN()函数判断一个数是否可以转换成数值。
数值转换
有三个函数可以将非数值转换为数值:Number()、parseInt()、parseFloat()。
Number是转型函数,可以用于任何类型。后两个函数主要用于字符串转换。
Number
Number函数基于下列规则转换:
- 布尔值,true为1,false为0。
- 数值,直接返回。
- null,返回0。
- undefined,返回NaN。
- 字符串:
- 空字符串:返回0
- 数值或十六进制:返回对应数值
- 其它情况:返回NaN
paserInt
从第一个非空字符开始检测,若第一个非空字符不是数值或+、-则返回NaN。
依次检测每个字符直至数值字符末尾,如parseInt(“ 12345ABC”) = 12345。
遇到小数点也会停止检测。
paserFloat
类似paserInt,遇到第一个小数点会继续检测,结果返回一个浮点数。
String类型
String类型标识零或多个16位Unicode字符序列。
字符串可以使用单引号(‘’)、双引号(“”)、反引号(``)进行标记。
字符串的长度可以通过length属性获取:
1 | let text = "This is the letter sigma: \u03a3."; |
转换字符串
有两种方式转换字符串:
第一种方式是使用toString()方法,可用于数值、布尔值、对象和字符串值。
这个方法返回一个副本,null和undefined没有toString()方法。
第二种方式是使用String()函数:
- 如果值有toString()方法,则调用该方法不返回结果。
- 如果值是null,返回”null”。
- 如果值是undefined,返回”undefined”。
字符串插值
模板字面值是指用反引号(``)包围的字符串,会保留原有格式,可以跨行定义。
模板字面值支持将变量插入字符串中,进行格式化输出。
所有插入的值都会使用toString()强制转型为字符串。
1 | let value = 5; |
模板中也可以调用函数和方法。
Symbol类型
Symbol(符号)是ES6新增的数据类型。
符号实例是唯一的、不可变的,符号作用的确保对象使用唯一标识符,不会发生属性冲突的危险。
符号基本用法
符号使用Symbol()函数初始化,符号本身是原始类型。
1 | let sym = Symbol(); |
Symbol函数允许接收String类型参数,这个字符串作为符号描述,将来通过这个String来调试程序。
但是,这个字符串参数和符号定义或标识完全无关。
全局符号注册表
如果运行时不同部分需要共享或重用符号实例,可以用字符串作为键,在全局符号注册表中创建并重用该符号。
为此,需要使用symbol.for()方法。
symbol.for()方法对每个字符串键执行幂等操作。
第一次使用某个字符串调用时,会创建一个新符号实例。
后续使用相同字符串调用时,会返回之前的符号实例。
1 | let fooGlobalSymbol = Symbol.for('foo'); //在全局注册表中创建符号实例,键:'foo' |
还可以使用Symbol.keyFor(符号实例)来查询对应的字符串键。
如果存在,则返回对应的字符串键。否则,抛出TypeError。
Object类型
ECMAScript中的对象其实就是一组数据和功能的集合。
对象通过new关键字和对象类型的名称来创建。
我们可以通过先创建Object类型的实例来创建自己的对象,然后加入属性和方法。
1 | let o = new Object(); |
每个Object实例都有如下属性和方法:
- constructor:用于创建当前对象的函数
- hasOwnProperty(propertyName):用于判断当前对象实体上是否存在给定属性,参数是属性字符串。
- isPrototypeOf(object):判断当前对象是否为另一个对象的原型。
- propertyIsEnumerable(propertyName):判断给定的属性是否可以使用for-in枚举。
- toLocalString():返回对象字符串表示,该字符串反映对象的本地化执行环境。
- toString():返回对象的字符串表示。
- valueOf():返回对象对应的字符串、数值或布尔值表示。
运算符
ES支持的运算符包括:
- 自增/自减运算符、位运算符
- 布尔运算符:逻辑与(&&)、逻辑或(||)、逻辑非(!)。
- 加法、减法、乘法、除法、取模运算符
- 相等运算符、关系运算符、赋值运算符
- 条件运算符、逗号运算符
上述运算符和类C语言语法类似,不再详细说明。
全等运算符
有一类运算符需要特别注意:全等、不全等运算符。
相等运算符和类C语言一样,会将两边操作数类型转换一致后比较。
为了比较两数类型和数值都一致,ES增加了全等运算符:
1 | let a = '666'; |