一、语法

ECMAScript 的语法很大程度上借鉴了 C 语言和其他类 C 语言,如 Java 和 Perl
ECMASCript 中的一切都区分大小写,变量 test 和变量 Test 是两个不同的变量,类似地,typeof 不能作为函数名,因为它是一个关键字,但 Typeof 是一个完全有效的函数名。

1. 区分大小写

ECMAScript 中的一切都是区分大小写。无论是变量、函数名还是操作符。类似地,typeof 不能作为函数名,因为它是一个关键字,但 Typeof 是一个完全有效的函数名。

2. 标识符

标识符是变量、函数、属性或函数参数的名称,由一或多个下列字符组成

  • 第一个字符必须是一个字母、下划线(_)或美元符号($)
  • 省下的其他字符可以是字母、下划线、美元符号或数字

标识符中的字母可以是扩展 ASCII(Extended ASCII)中的字母,也可以是 Unicode 的字母字符
按照惯例,ECMAScript 表示符使用驼峰大小写形式,即第一个单词的首字母小写,后面每个单词的首字母大写

  • firstSecond
  • myCar
  • doSomethingImportant

关键字、保留字、true、false 和 null 不能作为标识符
**

注释

ECMAScript 采用 C 语言风格的注释

  1. // 单行注释
  2. /*
  3. 多行注释
  4. */

严格模式

ECMAScript5 增加了严格模式(strict mode)的概念,严格模式是一种不同的 JavaScript 解析和执行模型,ECMAScript3 的一些不规范写法在这种模式下会被处理,对于不安全的活动将抛出错误。要对整个脚本启用严格模式,在脚本开头加上这一行:

  1. "use strict"

它是一个预处理指令,任何支持的 JavaScript 引擎看到它都会切换到严格模式。选择这种语法形式的目的是不破坏 ECMAScript3 语法。单独指定一个函数在严格模式下执行,在函数开头加这句话:

  1. function doSomething(){
  2. // 函数体
  3. }

语句

ECMAScript 中的语句以分号结尾,也可以省略

关键字

ECMAScript-262 描述了一组保留的关键字,这些关键字有特殊用途,按照规定,保留的关键字不能用作标识符或属性名,ECMA-262 第 6 版规定的所有关键字如下(33个):

break else new while
case export return with
catch extends super yield
class finally switch
const for this
continue function throw
debugger if try
default import typeof
delete in var
do instanceof void

规范中也描述了未来的保留字
始终保留:

enum

严格模式下保留:

implements protected
interface private
let public
package static

模块代码中保留:

await

这些词汇不能用作标识符,但现在还可以用作对象的属性名,但不建议使用关键字和保留字作为标识符和属性名,以确保兼容过去和未来的 ECMAScript 版本

var 声明

要定义变量,可以使用 var 操作符,后跟变量名

  1. var message = "hi"

使用 var 操作符定义的变量会成为包含它的函数的局部变量,即 var 声明的范围是函数作用域
使用 var 声明变得变量会自动提升到函数作用域顶部

let 声明

let 声明的范围是块作用域
let 声明的变量不会在作用域中提升

  • 在let 声明之前的执行瞬间被称为“暂时性死区”(temporal dead zone),在此阶段引用任何后面才声明的变量都会抛出 ReferenceError

全局声明

使用 var 在全局作用域中声明的变量会称为 window 对象的属性
使用 let 在全局作用域中声明的变量不会称为 window 对象的属性,不过 let 声明仍然是全局作用域中发生的,相应变量会在页面的声明周期内存续,因此,为了避免 SyntaxError,必须确保页面不会重复声明同一个变量

条件声明

顾明思意思,条件声明就是“如果变量 xxx 没有,我去声明”
结论是:无法使用条件声明

使用 var 声明时,由于声明会被提升,JavaScript 引擎会自动将多余的声明在作用域顶部合并为一个声明

使用 let 声明时,由于作用域是块,所以不可能检查前面是否已经使用 let 声明过同名变量,同时也就不可能再没有声明的情况下声明它
使用 try/catch 语句或typeof 操作符也不能解决,因为条件块中 let 声明的作用域仅限与该块

不能使用 let 进行条件声明是件好事,它让程序变得更难理解。如果你发现自己在使用这个模式,那一定有更好的替代方式

for 循环中的 let 声明

在 let 出现之前,for 循环定义的迭代变量会渗透到循环体外部
改用 let 之后,这个问题就消失了,因为迭代变量的作用域仅限与 for 循环内部

const 声明

const 的行为与 let 基本相同,唯一一个重要的区别是用它声明变量时必须同时初始化变量,且尝试修改 const 声明的变量会导致运行时错误
const 声明的限制只适用于它指向的变量的引用,换句话说,如果 const 变量引用的是一个对象,那么修改这个对象内部的属性并不违反 const 的限制

声明风格及最佳实践

ECMAScript6 增加 let 和 const 从客观上为这门语言更精确地声明作用域和语义提供了更好的支持

  1. 不使用 var
  2. const 优先,let 次之

二、数据类型

数据类型种类

ECMAScript 有 6 种简单数据类型(原始数据类型),1 种复杂数据类型

  • String、Number、Boolean、Symbol、Undefined、Null
  • Object(对象)

在 ECMAScript 中不能定义自己的数据类型,所有值都可以用上述 7 中数据类型之一来表示

typeof 操作符

typeof 用来确定任意变量的数据类型

  • “undefined” 表示值未定义
  • “boolean” 表示值为布尔值
  • “string” 表示值为字符串
  • “number” 表示值为数值
  • “object” 表示值为对象(而不是函数)或 null
  • “function” 表示值为函数
  • “symbol” 表示值为符号

调用 typeof null 返回的是 “object” 是因为特殊值 null 被认为是一个对空对象的引用

严格来讲,函数在 ECMAScript 中被认为是对象,并不代表一种数据类型,可是,函数也有自己的特殊属性。为此,就有必要通过 typeof 操作符来区分函数和其他对象

Undefined 类型

Undefined 类型只有一个值,就是特殊值 undefined,当使用 var 或 let 声明了变量但没有初始化时,就相当于给变量赋予了 undefined 值。

一般来说,永远不用显式地给某个边明亮设置 undefined 值,字面值 undefined 主要用于比较,而且在 ECMA-262 第 3版之前是不存在的。增加这个特殊值的目的就是为了证实明确对象指针(null)和未初始化变量的区别

包含 undefined 值的变量跟未定义变量是有区别的

  1. let message
  2. console.log(message) // "undefined"
  3. console.log(age) // 报错

console.log 输出一个未声明的变量 age 的值会报错
对于未声明的变量,只能执行一个有用的操作,就是对它调用 typeof

  1. let message
  2. console.log(message) // "undefined"
  3. console.log(age) // "undefined"

无论是声明还是未声明,typeof 返回的都是字符串”undefined”。逻辑上将这是对的,因为虽然严格来讲这两个变量存在根本性差异,但它对任何一个变量都不可能执行什么真正的操作

即使为初始化的变量会被自动赋予 undefined 值,但我们仍然建议在声明变量的同时进行初始化。这样,当 typeof 返回”undefined”时,你就会知道那是因为给定的变量尚未声明,而不是声明了但未初始化

Null 类型

Null 类型同样只有一个值,即特殊值 null。逻辑上讲,null 值表示一个空对象指针,这也是 typeof 传一个 null 会返回 “object” 的原因

undefined 值是由 null 值派生而来的,即使他们有关系,但用途完全不一样
如前所述,永远不必显式地将变量设置为 undefined,但 null 不是这样
任何时候,只要变量要保存对象,而当时又没有那个对象保存,就要用 null 来填充该变量,这样就可以保持 null 是空对象指针的语义

Boolean 类型

Boolean 类型是 ECMAScript 中使用最频繁的类型之一,有两个字面值:true 和 false
它是区分大小写的,因此 True 和 False 是有效的标识符,但不是布尔值

调用特定的 Boolean() 转型函数可以将其他类型的值转换为布尔值

  1. let message = "Hello"
  2. let messageAsBoolean = Boolean(message)

除了 false,有 5 个 falsy 值

  1. ""
  2. 0NaN
  3. null
  4. undefined

记住上面 5 falsy 值个很重要
因为像 if 等控制流语句会自动执行其他类型值到布尔值的转换时,会自动转换为等价的布尔值

Number 类型

Number 类型使用 IEEE 754 格式表示整数和浮点值(在某些语言中也叫双精度值)
不同的数值类型相应地也有不同的数值字面量格式

十进制整数:let intNum = 55
八进制:let octalNum = 0o70
十六进制:let hexNum = 0xA
使用八进制和十六进制创建的数值在所有数学操作中都被视为十进制数值

由于 JavaScript 保存数值的方式,实际中可能存在正零(+0)和负零(-0)。正零和负零在所有情况下都被认为是等同的

浮点值使用的内存空间是存储整数数值的两倍,所以 ECMAScript 总是想方设法把纸转换为整数,比如 let floatNum1 = 1.let floatNum2 = 10.0 都会自动转换为 整数 1 和整数 10
浮点值的精确度最高可达 17 为小数,但在算数精算中远不如整数精确,例如,0.1 加 0.2 得到的不是 0.3

由于内存的限制,ECMAScript 并不支持这个世界上的所有数值,ECMAScript 可以表示的最小数值保存在 Number.MIN_VALUE 中,;可以表示的最大数值保存在 Number.MAX_VALUE 中
如果某个计算得到的数值结果超出了 JavaScript 可以表示的范围,会自动转换为特殊的 Infinity 或 -Infinity,要确定一个值是不是有限大,可以使用 isFinite() 函数

  1. let result = Number.MAX_VALUE + Number.MAXVALUE
  2. console.log(inFinite(result))

使用 Number.NEGATIVE_INFINITYNumber.POSITIVE_INFINITY 也可以获取正、负 Infinity,这两个属性值分别是 -Infinity 和 Infinity

「@浪里淘沙的小法师」