任何语言的核心所描述的都是这门语言在最基本的层面上如何工作,涉及语法、操作符、数据类型以及内置功能,在此基础之上才可以构建复杂的解决方案。如前所述,ECMA-262以一个名为ECMAScript的伪语言的形式,定义了JavaScript 的所有这些方面。
语法:法很大程度上借鉴了 C 语言和其他类 C 语言,如 Java 和 Perl。熟悉这些语言的开发者,应该很容易理解 ECMAScript 宽松的语法。
所谓标识符( 区分大小写),就是变量、函数、属性或函数参数的名称。标识符可以由一或多个下列字符组成:
第一个字符必须是一个字母、下划线(_)或美元符号($);
剩下的其他字符可以是字母、下划线、美元符号或数字。
标识符中的字母可以是扩展 ASCII(Extended ASCII)中的字母,也可以是 Unicode 的字母字符,如 À 和 Æ(但不推荐使用)。
按照惯例,ECMAScript 标识符使用驼峰大小写形式,即第一个单词的首字母小写,后面每个单词的首字母大写,虽然这种写法并不是强制性的,但因为这种形式跟 ECMAScript 内置函数和对象的命名方式一致,所以算是最佳实践。
注意 关键字、保留字、true、false 和 null 不能作为标识符。
ECMAScript 采用 C 语言风格的注释,包括单行注释和块注释。
// 单行注释
块注释以(/*)开头,以它们的反向组合(*/)结尾,如:/*这是多行注释 */
ECMAScript 5 增加了严格模式(strict mode)的概念。严格模式是一种不同的 JavaScript 解析和执行模型,ECMAScript 3 的一些不规范写法在这种模式下会被处理,对于不安全的活动将抛出错误。要对整个脚本启用严格模式,在脚本开头加上这一行:“use strict”;
虽然看起来像个没有赋值给任何变量的字符串,但它其实是一个预处理指令。任何支持的 JavaScript引擎看到它都会切换到严格模式。选择这种语法形式的目的是不破坏 ECMAScript 3 语法。
也可以单独指定一个函数在严格模式下执行,只要把这个预处理指令放到函数体开头即可:
function doSomething() {
"use strict";
// 函数体
}
严格模式会影响 JavaScript 执行的很多方面,因此本书在用到它时会明确指出来。所有现代浏览器是都支持严格模式的。
ECMAScript 中的语句以分号结尾。省略分号意味着由解析器确定语句在哪里结尾。即使语句末尾的分号不是必需的,也应该加上。记着加分号有助于防止省略造成的问题,比如可以避免输入内容不完整。此外,加分号也便于开发者通过删除空行来压缩代码(如果没有结尾的分号,只删除空行,则会导致语法错误)。加分号也有助于在某些情况下提升性能,因为解析器会尝试在合适的位置补上分号以纠正语法错误。
在控制语句中使用代码块可以让内容更清晰,在需要修改代码时也可以减少出错的可能性。
ECMA-262 描述了一组保留的关键字,这些关键字有特殊用途,比如表示控制语句的开始和结束,或者执行特定的操作。按照规定,保留的关键字不能用作标识符或属性名。ECMA-262 第 6 版规定的所有关键字如下:
break do in typeof case else instanceof var
catch export new void class extends return while
const finally super with continue for switch yield
debugger function this default if throw delete import try
规范中也描述了一组未来的保留字,同样不能用作标识符或属性名。虽然保留字在语言中没有特定用途,但它们是保留给将来做关键字用的。
以下是 ECMA-262 第 6 版为将来保留的所有词汇。
始终保留: enum
严格模式下保留: implements package public interface protected static let private
模块代码中保留: await
这些词汇不能用作标识符,但现在还可以用作对象的属性名。
一般来说,最好还是不要使用关键字和保留字作为标识符和属性名,以确保兼容过去和未来的 ECMAScript 版本。
ECMAScript 变量是松散类型的,意思是变量可以用于保存任何类型的数据。每个变量只不过是一个用于保存任意值的命名占位符。有 3 个关键字可以声明变量:var、const 和 let。其中,var 在ECMAScript 的所有版本中都可以使用,而 const 和 let 只能在 ECMAScript 6 及更晚的版本中使用。
用 var 操作符(注意 var 是一个关键字),后跟变量名(即标识符)可以用它保存任何类型的值,默认保存一个特殊值 undefined。ECMAScript 实现变量初始化,因此可以同时定义变量并设置它的值,并且在后续更改它的类型。
var 声明作用域,使用 var 操作符定义的变量会成为包含它的函数的局部变量。不过,在函数内定义变量时省略 var 操作符,可以创建一个全局变量。
虽然可以通过省略 var 操作符定义全局变量,但不推荐这么做。在局部作用域中定义的全局变量很难维护,也会造成困惑。这是因为不能一下子断定省略 var 是不是有意而为之。在严格模式下,如果像这样给未声明的变量赋值,则会导致抛出 ReferenceError。
因为 ECMAScript 是松散类型的,所以使用不同数据类型初始化的变量可以用一条语句来声明。插入换行和空格缩进并不是必需的,但这样有利于阅读理解。
在严格模式下,不能定义名为 eval 和 arguments 的变量,否则会导致语法错误。
var 声明关键字的变量会自动提升(hoist)到函数作用域顶部
let 声明的范围是块作用域,而 var 声明的范围是函数作用域。let 也不允许同一个块作用域中出现冗余明。JavaScript 引擎会记录用于变量声明的标识符及其所在的块作用域,因此嵌套使用相同的标识符不会报错,而这是因为同一个块中没有重复声明。对声明冗余报错不会因混用 let 和 var 而受影响。
全局变量:使用 let 在全局作用域中声明的变量不会成为 window 对象的属性(var 声明的变量则会)。
let 声明仍然是在全局作用域中发生的,相应变量会在页面的生命周期内存续。因此,为了避免 SyntaxError,必须确保页面不会重复声明同一个变量。
let 这个新的 ES6 声明关键字,不能依赖条件声明模式
for 循环中的 用let 声明,这种每次迭代声明一个独立变量实例的行为适用于所有风格的 for 循环,包括for
-in 和 for-of循环
const 的行为与 let 基本相同,唯一一个重要的区别是用它声明变量时必须同时初始化变量,且尝试修改 const 声明的变量会导致运行时错误
const 声明的限制只适用于它指向的变量的引用。换句话说,如果 const 变量引用的是一个对象,那么修改这个对象内部的属性并不违反 const 的限制。
不使用 var,const 优先,let 次之
有了 let 和 const,大多数开发者会发现自己不再需要 var 了。限制自己只使用 let 和 const
有助于提升代码质量,因为变量有了明确的作用域、声明位置,以及不变的值。
使用 const 声明可以让浏览器运行时强制保持变量不变,也可以让静态代码分析工具提前发现不合法的赋值操作。因此,很多开发者认为应该优先使用 const 来声明变量,只在提前知道未来会有修改时,再使let。这样可以让开发者更有信心地推断某些变量的值永远不会变,同时也能迅速发现因意外赋值导致的非预期行为
ECMAScript 有 6 种简单数据类型(也称为原始类型):Undefined、Null、Boolean、Number、
String 和 Symbol。Symbol(符号)是 ECMAScript 6 新增的。还有一种复杂数据类型叫 Object(对象)。Object 是一种无序名值对的集合。因为在 ECMAScript 中不能定义自己的数据类型,所有值都可
以用上述 7 种数据类型之一来表示。只有 7 种数据类型似乎不足以表示全部数据。但 ECMAScript 的数据类型很灵活,一种数据类型可以当作多种数据类型来使用。
typeof 操作符来确定任意变量的数据类型
typeof null 返回的是"object"。这是因为特殊值 null 被认为是一个对空对象的引用。
*严格来讲,函数在 ECMAScript 中被认为是对象,并不代表一种数据类型。可是,函数也有自己特殊的属性。为此,就有必要通过 typeof 操作符来区分函数和其他对象。
undefined 类型
Undefined 类型只有一个值,就是特殊值 undefined。当使用 var 或 let 声明了变量但没有初始化时,就相当于给变量赋予了 undefined 值
永远不用显式地给某个变量设置 undefined 值。字面值 undefined 主要用于比较,而且在 ECMA-262 第 3 版之前是不存在的。增加这个特殊值的目的就是为了正式明确空对象指针(null)和未初始化变量的区别。
即使未初始化的变量会被自动赋予 undefined 值,但我们仍然建议在声明变量的同时进行初始化。这样当 typeof 返回"undefined"时,你就会知道那是因为给定的变量尚未声明,而不是声明了但未初始化。
undefined 是一个假值。
let message; // 这个变量被声明了,只是值为 undefined
// age 没有声明
if (message) {
// 这个块不会执行
}
if (!message) {
// 这个块会执行
}
Null 类型同样只有一个值,即特殊值 null。逻辑上讲,null 值表示一个空对象指针,这也是给typeof 传一个 null 会返回"object"的原因
undefined 值是由 null 值派生而来的,因此 ECMA-262 将它们定义为表面上相等,如下面的例子所示:
console.log(null == undefined); // true
Boolean(布尔值)类型是 ECMAScript 中使用最频繁的类型之一,有两个字面值:true 和 false。这两个布尔值不同于数值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QKQN8ORi-1613819376556)(image-20210218162317928.png)]
if 等流控制语句会自动执行其他类型值到布尔值的转换
Number 类型使用 IEEE 754 格式表示整数和浮点值(在某些语言中也叫双精度值)。不同的数值类型相应地也有不同的数值字面量格式。
八进制,第一个数字必须是零(0)0o ,字面量在严格模式下是无效的,会导致 JavaScript 引擎抛出语法错误。
十六进制字面量,必须让真正的数值前缀 0x
使用八进制和十六进制格式创建的数值在所有数学操作中都被视为十进制数值
注意 由于 JavaScript 保存数值的方式,实际中可能存在正零(+0)和负零(-0)。正零和负零在所有情况下都被认为是等同的
浮点值,数值中必须包含小数点,而且小数点后面必须至少有一个数字。虽然小数点前面不是必须有整数,但推荐加上。因为存储浮点值使用的内存空间是存储整数值的两倍,所以 ECMAScript 总是想方设法把值转换为整数。ECMAScript 会将小数点后至少包含 6 个零的浮点值转换为科学记数法。
是 0.1 + 0.2,测试将失败。因此永远不要测试某个特定的浮点值之所以存在这种舍入错误,是因为使用了 IEEE 754 数值,这种错误并非 ECMAScript所独有。其他使用相同格式的语言也有这个问题。
ECMAScript 可以表示的最小数值保存在 Number.MIN_VALUE 中,这个值在多数浏览器中是 5e-324;可以表示的最大数值保存在Number.MAX_VALUE 中,这个值在多数浏览器中是 1.797 693 134 862 315 7e+308正 Infinity 或负 Infinity,则该值将不能再进一步用于任何计算。这是因为Infinity 没有可用于计算的数值表示形式。要确定一个值是不是有限大(即介于 JavaScript 能表示的最小值和最大值之间),可以使isFinite()函数
有一个特殊的数值叫 NaN,意思是“不是数值”(Not a Number),用于表示本来要返回数值的操作失败了(而不是抛出错误)。比如,用 0 除任意数值在其他语言中通常都会导致错误,从而中止代码执行。但在 ECMAScript 中,0、+0 或 -0 相除会返回 NaN。
任何涉及 NaN 的操作始终返回 NaN,NaN 不等于包括 NaN 在内的任何值。
ECMAScript 提供了 isNaN()函数。该函数接收一个参数,可以是任意数据类型,然后判断这个参数是否“不是数值”。把一个值传给 isNaN()后,该函数会尝试把它转换为数值。
数值转换
Number()、parseInt()和 parseFloat()。Number()是转型函数,可用于任何数据类型。后两个函数主要用于将字符串转换为数值。对于同样的参数,这 3 个函数执行的操作也不同。
Number()函数执行转换规则
布尔值,true 转换为 1,false 转换为 0。
数值,直接返回。
null,返回 0。
undefined,返回 NaN。
字符串,应用以下规则。
如果字符串包含数值字符,包括数值字符前面带加、减号的情况,则转换为一个十进制数值。
如果字符串包含有效的浮点值格式,则会转换为相应的浮点值(同样,忽略前面的零)。
如果字符串包含有效的十六进制格式如"0xf",则会转换为与该十六进制值对应的十进制整
数值。
如果是空字符串(不包含字符),则返回 0。
如果字符串包含除上述情况之外的其他字符,则返回 NaN。
对象,调用 valueOf()方法,并按照上述规则转换返回的值。如果转换结果是 NaN,则调用
toString()方法,再按照转换字符串的规则转换。
通常在需要得到整数时可以优先使用 parseInt()函数。这意味着空字符串也会返回 NaN(这一点跟 Number()不一样,它返回 0)。不同的数值格式很容易混淆,因此 parseInt()也接收第二个参数,用于指定底数(进制数)。还可以极大扩展转换后获得的结果类型。因为不传底数参数相当于让 parseInt()自己决定如何解析,所以为避免解析出错,建议始终传给它第二个参数(默认是10)。
parseFloat()函数的工作方式跟 parseInt()函数类似,都是从位置 0 开始检测每个字符。同样,它也是解析到字符串末尾或者解析到一个无效的浮点数值字符为止。它始终忽略字符串开头的零。这个函数能识别前面讨论的所有浮点格式,以及十进制格式(开头的零始终被忽略)。它只解析十进制值,因此不能指定底数。
String(字符串)数据类型表示零或多个 16 位 Unicode 字符序列。字符串可以使用双引号(")、单引号(’)或反引号(`)标示。ECMAScript 语法中表示字符串的引号没有区别,但是开闭引号必须相同。
字符字面量,用于表示非打印字符或有其他用途的字符。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i3nZ4g61-1613819376574)(image-20210219153053010.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h8kG9ix7-1613819376579)(image-20210219153141406.png)]
转义序列表示一个字符,所以只算一个字符。如果字符串中包含双字节字符,那么length 属性返回的值可能不是准确的字符数。*第 5 章将具体讨论如何解决这个问题。
字符串的特点
ECMAScript 中的字符串是不可变的(immutable),意思是一旦创建,它们的值就不能变了。要修改某个变量中的字符串值,必须先销毁原始的字符串,然后将包含新值的另一个字符串保存到该变量。
所有处理都是在后台发生的,而这也是一些早期的浏览器(如 Firefox 1.0 之前的版本和 IE6.0)在拼接字符串时非常慢的原因。这些浏览器在后来的版本中都有针对性地解决了这个问题。
转换为字符串
有两种方式把一个值转换为字符串。首先是使用几乎所有值都有的 toString()方法。这个方法唯一的用途就是返回当前值的字符串等价物。null 和 undefined 值没有 toString()方法。
多数情况下,toString()不接收任何参数。不过,在对数值调用这个方法时,toString()可以接收一个底数参数,即以什么底数来输出数值的字符串表示。通过传入参数,可以得到数值的二进制、八进制等任何有效基数的字符串表示。
如果你不确定一个值是不是 null 或 undefined,可以使用 String()转型函数,它始终会返回表示相应类型值的字符串。String()函数遵循如下规则。
如果值有 toString()方法,则调用该方法(不传参数)并返回结果。
如果值是 null,返回"null"
如果值是 undefined,返回"undefined"
数值和布尔值的转换结果与调用 toString()相同。因为 null 和 undefined 没有 toString()方法,所以 String()方法就直接返回了这两个值的字面量文本。用加号操作符给一个值加上一个空字符串""
也可以将其转换为字符串。
模板字面量
ECMAScript 6 新增了使用模板字面量定义字符串的能力。与使用单引号或双引号不同,模板字面量
保留换行字符,可以跨行定义字符串。模板字面量在定义模板时特别有用,不过由于模板字面量会保持反引号内部的空格,因此在使用时要格外注意。格式正确的模板字符串看起来可能会缩进不当。
模板字面量最常用的一个特性是支持字符串插值,也就是可以在一个连续定义中插入一个或多个值。
模板字面量不是字符串,而是一种特殊的 JavaScript 句法表达式,只不过求值后得到的是字符串。模板字面量在定义时立即求值并转换为字符串实例,任何插入的变量也会从它们最接近的作用域中取值。
所有插入的值都会使用 toString()强制转型为字符串,而且任何 JavaScript 表达式都可以用于插值,在插值表达式中可以调用函数和方法。嵌套的模板字符串无须转义。
标签函数本身是一个常规函数,通过前缀到模板字面量来应用自定义行为(不太懂)
使用模板字面量也可以直接获取原始的模板字面量内容(如换行符或 Unicode 字符),而不是被转
换后的字符表示。为此,可以使用默认的 String.raw 标签函数
Symbol(符号)是 ECMAScript 6 新增的数据类型。符号是原始值,且符号实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。符号就是用来创建唯一记号,进而用作非字符串形式的对象属性,符号并不是为了提供私有属性的行为才增加的。
符号需要使用 Symbol()函数初始化。因为符号本身是原始类型,所以 typeof 操作符对符号返回 symbol。
调用 Symbol()函数时,也可以传入一个字符串参数作为对符号的描述(description),将来可以通过这个字符串来调试代码。但是,这个字符串参数与符号定义或标识完全无关。符号没有字面量语法,这也是它们发挥作用的关键。按照规范,你只要创建 Symbol()实例并将其用作对象的新属性,就可以保证它不会覆盖已有的对象属性,无论是符号属性还是字符串属性。
Symbol()函数不能与 new 关键字一起作为构造函数使用。这样做是为了避免创建符号包装对象。
let mySymbol = new Symbol(); // TypeError: Symbol is not a constructor
符号包装对象,可以借用 Object()函数:
let mySymbol = Symbol();
let myWrappedSymbol = Object(mySymbol);
console.log(typeof myWrappedSymbol); // “object”
如果运行时的不同部分需要共享和重用符号实例,那么可以用一个字符串作为键,在全局符号注册表中创建并重用符号。为此,需要使用 Symbol.for()方法
Symbol.for()对每个字符串键都执行幂等操作。第一次使用某个字符串调用时,它会检查全局运行时注册表,发现不存在对应的符号,于是就会生成一个新符号实例并添加到注册表中。后续使用相同字符串的调用同样会检查注册表,发现存在与该字符串对应的符号,然后就会返回该符号实例。
用相同的符号描述,在全局注册表中定义的符号跟使用 Symbol()定义的符号也并不等同:
let localSymbol = Symbol(‘foo’);
let globalSymbol = Symbol.for(‘foo’);
全局注册表中的符号必须使用字符串键来创建,因此作为参数传给 Symbol.for()的任何值都会被转换为字符串。此外,注册表中使用的键同时也会被用作符号描述。
用 Symbol.keyFor()来查询全局注册表,这个方法接收符号,返回该全局符号对应的字
符串键。如果查询的不是全局符号,则返回 undefined。如果传给 Symbol.keyFor()的不是符号,则该方法抛出 TypeError。
凡是可以使用字符串或数值作为属性的地方,都可以使用符号。这就包括了对象字面量属性和Object.defineProperty()/Object.defineProperties()定义的属性。对象字面量只能在计算属性语法中使用符号作为属性。
于 Object.getOwnPropertyNames()返回对象实例的常规属性数组,Object.getOwnProperty-Symbols()返回对象实例的符号属性数组。这两个方法的返回值彼此互斥。Object.getOwnProperty-Descriptors()会返回同时包含常规和符号属性描述符的对象。Reflect.ownKeys()会返回两种类型的键:
符号属性是对内存中符号的一个引用,所以直接创建并用作属性的符号不会丢失。但是,如果没有显式地保存对这些属性的引用,那么必须遍历对象的所有符号属性才能找到相应的属性键。
ECMAScript 6 也引入了一批常用内置符号(well-known symbol),用于暴露语言内部行为,开发者可以直接访问、重写或模拟这些行为。这些内置符号都以 Symbol 工厂函数字符串属性的形式存在。
这些内置符号最重要的用途之一是重新定义它们,从而改变原生结构的行为。比如,我们知道for-of 循环会在相关对象上使用 Symbol.iterator 属性。 内置符号也没有什么特别之处,它们就是全局函数 Symbol 的普通字符串属性,指向一个符号的实例。所有内置符号属性都是不可写、不可枚举、不可配置的。
注意 在提到 ECMAScript 规范时,经常会引用符号在规范中的名称,前缀为@@。比如,
@@iterator 指的就是 Symbol.iterator
根据 ECMAScript 规范,这个符号作为一个属性表示“一个方法,该方法返回对象默认的 AsyncIterator。 由for-await-of 语句使用’’。换句话说,这个符号表示实现异步迭代器 API 的函数。
for-await-of 循环会利用这个函数执行异步迭代操作。循环时,它们会调用以 Symbol.asyncIterator为键的函数,并期望这个函数会返回一个实现迭代器 API 的对象。很多时候,返回的对象是实现该 API的 AsyncGenerator.技术上,这个由 Symbol.asyncIterator 函数生成的对象应该通过其 next()方法陆续返回
Promise 实例。可以通过显式地调用 next()方法返回,也可以隐式地通过异步生成器函数返回.
Symbol.asyncIterator 是 ES2018 规范定义的,因此只有版本非常新的浏览器支持它。关于异步迭代和 for-await-of 循环的细节
Symbol.hasInstance
这个符号作为一个属性表示“一个方法,该方法决定一个构造器对象是否认可一个对象是它的实例。由 instanceof 操作符使用”。instanceof 操作符可以用来确定一个对象实例的原型链上是否有原型。
在 ES6 中,instanceof 操作符会使用 Symbol.hasInstance 函数来确定关系。以 Symbol. hasInstance 为键的函数会执行同样的操作,只是操作数对调了一下.这个属性定义在 Function 的原型上,因此默认在所有函数和类上都可以调用。由于 instanceof操作符会在原型链上寻找这个属性定义,就跟在原型链上寻找其他属性一样,因此可以在继承的类上通过静态方法重新定义这个函数.
Symbol.isConcatSpreadable
这个符号作为一个属性表示“一个布尔值,如果是 true,则意味着对象应该用 Array.prototype.concat()打平其数组元素”。ES6 中的 Array.prototype.concat()方法会根据接收到的对象类型选择如何将一个类数组对象拼接成数组实例。覆盖 Symbol.isConcatSpreadable 的值可以修改这个行为。
数组对象默认情况下会被打平到已有的数组,false 或假值会导致整个对象被追加到数组末尾。类数组对象默认情况下会被追加到数组末尾,true 或真值会导致这个类数组对象被打平到数组实例。其他不是类数组对象的对象在 Symbol.isConcatSpreadable 被设置为 true 的情况下将被忽略。
Symbol.iterator
根据 ECMAScript 规范,这个符号作为一个属性表示“一个方法,该方法返回对象默认的迭代器。由 for-of 语句使用”。换句话说,这个符号表示实现迭代器 API 的函数。for-of 循环这样的语言结构会利用这个函数执行迭代操作。循环时,它们会调用以 Symbol.iterator 为键的函数,并默认这个函数会返回一个实现迭代器 API 的对象。很多时候,返回的对象是实现该 API的 Generator.技术上,这个由 Symbol.iterator 函数生成的对象应该通过其 next()方法陆续返回值。可以通过显式地调用 next()方法返回,也可以隐式地通过生成器函数返回.
Symbol.match
根据 ECMAScript 规范,这个符号作为一个属性表示“一个正则表达式方法,该方法用正则表达式去匹配字符串。由 String.prototype.match()方法使用”。String.prototype.match()方法会使用以 Symbol.match 为键的函数来对正则表达式求值。正则表达式的原型上默认有这个函数的定义,因此所有正则表达式实例默认是这个 String 方法的有效参数.给这个方法传入非正则表达式值会导致该值被转换为 RegExp 对象.
Symbol.replace
根据 ECMAScript 规范,这个符号作为一个属性表示“一个正则表达式方法,该方法替换一个字符串中匹配的子串。由 String.prototype.replace()方法使用”。String.prototype.replace()方法会使用Symbol.replace 为键的函数来对正则表达式求值。正则表达式的原型上默认有这个函数的定义,因此所有正则表达式实例默认是这个 String 方法的有效参数
Symbol.search
根据 ECMAScript 规范,这个符号作为一个属性表示“一个正则表达式方法,该方法返回字符串中匹配正则表达式的索引。由 String.prototype.search()方法使用”。String.prototype.search()方法会使用以 Symbol.search 为键的函数来对正则表达式求值。正则表达式的原型上默认有这个函数的定义,因此所有正则表达式实例默认是这个 String 方法的有效参数。给这个方法传入非正则表达式值会导致该值被转换为 RegExp 对象。如果想改变这种行为,让方法直接使用参数,可以重新定义 Symbol.search 函数以取代默认对正则表达式求值的行为,从而让search()方法使用非正则表达式实例。Symbol.search 函数接收一个参数,就是调用 match()方法的字符串实例。返回的值没有限制
Symbol.species
根据 ECMAScript 规范,这个符号作为一个属性表示“一个函数值,该函数作为创建派生对象的构造函数”。这个属性在内置类型中最常用,用于对内置类型实例方法的返回值暴露实例化派生对象的方法。用 Symbol.species 定义静态的获取器(getter)方法,可以覆盖新创建实例的原型定义
Symbol.split
根据 ECMAScript 规范,这个符号作为一个属性表示“一个正则表达式方法,该方法在匹配正则表达式的索引位置拆分字符串。由 String.prototype.split()方法使用”。String.prototype. split()方法会使用以 Symbol.split 为键的函数来对正则表达式求值。正则表达式的原型上默认有这个函数的定义,因此所有正则表达式实例默认是这个 String 方法的有效参数。
Symbol.toPrimitive
根据 ECMAScript 规范,这个符号作为一个属性表示“一个方法,该方法将对象转换为相应的原始值。由 ToPrimitive 抽象操作使用”。很多内置操作都会尝试强制将对象转换为原始值,包括字符串、数值和未指定的原始类型。对于一个自定义对象实例,通过在这个实例的 Symbol.toPrimitive 属性上定义一个函数可以改变默认行为。根据提供给这个函数的参数(string、number 或 default),可以控制返回的原始值
Symbol.toStringTag
根据 ECMAScript 规范,这个符号作为一个属性表示“一个字符串,该字符串用于创建对象的默认字符串描述。由内置方法 Object.prototype.toString()使用”。通过 toString()方法获取对象标识时,会检索由 Symbol.toStringTag 指定的实例标识符,默认为"Object"。内置类型已经指定了这个值,但自定义类实例还需要明确定义。
Symbol.unscopables
根据 ECMAScript 规范,这个符号作为一个属性表示“一个对象,该对象所有的以及继承的属性,都会从关联对象的 with 环境绑定中排除”。设置这个符号并让其映射对应属性的键值为 true,就可以阻止该属性出现在 with 环境绑定中。
*不推荐使用 with,因此也不推荐使用 Symbol.unscopables
ECMAScript 中的对象其实就是一组数据和功能的集合。对象通过 new 操作符后跟对象类型的名称来创建。开发者可以通过创建 Object 类型的实例来创建自己的对象,然后再给对象添加属性和方法。ECMAScript 中的 Object 也是派生其他对象的基类。Object 类型的所有属性和方法在派生的对象上同样存在。
每个 Object 实例都有如下属性和方法。
constructor:用于创建当前对象的函数。在前面的例子中,这个属性的值就是 Object() 函数。
hasOwnProperty(propertyName):用于判断当前对象实例(不是原型)上是否存在给定的属
性。要检查的属性名必须是字符串(如 o.hasOwnProperty(“name”))或符号。
isPrototypeOf(object):用于判断当前对象是否为另一个对象的原型。
propertyIsEnumerable(propertyName):用于判断给定的属性是否可以使用(本章稍后讨
论的)for-in 语句枚举。与 hasOwnProperty()一样,属性名必须是字符串。
toLocaleString():返回对象的字符串表示,该字符串反映对象所在的本地化执行环境。
toString():返回对象的字符串表示。
valueOf():返回对象对应的字符串、数值或布尔值表示。通常与 toString()的返回值相同。
因为在 ECMAScript 中 Object 是所有对象的基类,所以任何对象都有这些属性和方法。注意 严格来讲,ECMA-262 中对象的行为不一定适合 JavaScript 中的其他对象。比如浏览器环境中的 BOM 和 DOM 对象,都是由宿主环境定义和提供的宿主对象。而宿主对象不受 ECMA-262 约束,所以它们可能会也可能不会继承 Object。
ECMA-262 描述了一组可用于操作数据值的操作符,包括数学操作符(如加、减)、位操作符、关
系操作符和相等操作符等。ECMAScript 中的操作符是独特的,因为它们可用于各种值,包括字符串、
数值、布尔值,甚至还有对象。在应用给对象时,操作符通常会调用 valueOf()和/或 toString()方
法来取得可以计算的值。
一元操作符
递增和递减操作符直接照搬自 C 语言,但有两个版本:前缀版和后缀版。
无论使用前缀递增还是前缀递减操作符,变量的值都会在语句被求值之前改变。(在计算机科学中,
这通常被称为具有副作用。)
后缀版与前缀版的主要区别在于,后缀版递增和递减在语句被求值后才发生。
4 个操作符可以作用于任何值,意思是不限于整数——字符串、布尔值、浮点值,甚至对象都可以。递增和递减操作符遵循如下规则。
对于字符串,如果是有效的数值形式,则转换为数值再应用改变。变量类型从字符串变成数值。
对于字符串,如果不是有效的数值形式,则将变量的值设置为 NaN 。变量类型从字符串变成
数值。
对于布尔值,如果是 false,则转换为 0 再应用改变。变量类型从布尔值变成数值。
对于布尔值,如果是 true,则转换为 1 再应用改变。变量类型从布尔值变成数值。
对于浮点值,加 1 或减 1。
如果是对象,则调用其(第 5 章会详细介绍的)valueOf()方法取得可以操作的值。对得到的
值应用上述规则。如果是 NaN,则调用 toString()并再次应用其他规则。变量类型从对象变成
数值。
一元加和减操作符对大多数开发者来说并不陌生,它们在 ECMAScript 中跟在高中数学中的用途一样,用于基础运算。一元加由一个加号(+)表示,放在变量前头,对数值没有任何影响。
如果将一元加应用到非数值,则会执行与使用 Number()转型函数一样的类型转换:布尔值 false和 true 转换为 0 和 1,字符串根据特殊规则进行解析,对象会调用它们的 valueOf()和/或 toString()方法以得到可以转换的值。
操作符用于数值的底层操作,也就是操作内存中表示数据的比特(位)。ECMAScript中的所有数值都以 IEEE 754 64 位格式存储,但位操作并不直接应用到 64 位表示,而是先把值转换为32 位整数,再进行位操作,之后再把结果转换为 64 位。对开发者而言,就好像只有 32 位整数一样,因为 64 位整数存储格式是不可见的。既然知道了这些,就只需要考虑 32 位整数即可。
负值以一种称为二补数(或补码)的二进制编码存储。(绝对值按位取反加一)
一个数值的二补数通过如下 3 个步骤计算
得到:
(1) 确定绝对值的二进制表示(如,对于-18,先确定 18 的二进制表示);
(2) 找到数值的一补数(或反码),换句话说,就是每个 0 都变成 1,每个 1 都变成 0;
(3) 给结果加 1。
二进制操作变得与其他语言中类似。但这个转换也导致了一个奇特的副作用,即特殊值NaN 和Infinity在位操作中都会被当成 0 处理。如果将位操作符应用到非数值,那么首先会使用 Number()函数将该值转换为数值(这个过程是自动的),然后再应用位操作。最终结果是数值。
按位非操作符用波浪符(~)表示,它的作用是返回数值的一补数。按位非是 ECMAScript 中为数不多的几个二进制数学操作符之一。位操作的速度快得多。这是因为位操作是在数值的底层表示上完成的
按位与操作符用和号(&)表示,有两个操作数。本质上,按位与就是将两个数的每一个位对齐,然后基于真值表中的规则,对每一位执行相应的与操作。
按位或操作符用管道符(|)表示,同样有两个操作数。按位或遵循如下真值表
按位异或用脱字符(^)表示,同样有两个操作数。下面是按位异或的真值表
左移操作符用两个小于号(<<)表示,会按照指定的位数将数值的所有位向左移动。有符号右移由两个大于号(>>)表示,会将数值的所有 32 位都向右移,同时保留符号(正或负)。有符号右移实际上是左移的逆运算。
对正数来说,这跟有符号右移效果相同。但对负数来说,结果就差太多了。无符号右移操作符将负数的二进制表示当成正数的二进制表示来处理。因为负数是其绝对值的二补数,所以右移之后结果变得非常之大。
布尔操作符一共有 3 个:逻辑非、逻辑与和逻辑或。
逻辑非操作符由一个叹号(!)表示,可应用给 ECMAScript 中的任何值。这个操作符始终返回布尔值,无论应用到的是什么数据类型。逻辑非操作符首先将操作数转换为布尔值,然后再对其取反。换句话说,逻辑非操作符会遵循如下规则。
如果操作数是对象,则返回 false。
如果操作数是空字符串,则返回 true。
如果操作数是非空字符串,则返回 false。
如果操作数是数值 0,则返回 true。
如果操作数是非 0 数值(包括 Infinity),则返回 false。
如果操作数是 null,则返回 true。
如果操作数是 NaN,则返回 true。
如果操作数是 undefined,则返回 true。
逻辑非操作符也可以用于把任意值转换为布尔值。同时使用两个叹号(!!),相当于调用了转型函数 Boolean()。无论操作数是什么类型,第一个叹号总会返回布尔值。第二个叹号对该布尔值取反,从而给出变量真正对应的布尔值。结果与对同一个值使用 Boolean()函数是一样的
逻辑与操作符由两个和号(&&)表示,应用到两个值
逻辑与操作符可用于任何类型的操作数,不限于布尔值。如果有操作数不是布尔值,则逻辑与并不一定会返回布尔值,而是遵循如下规则。
如果第一个操作数是对象,则返回第二个操作数。
如果第二个操作数是对象,则只有第一个操作数求值为 true 才会返回该对象。
如果两个操作数都是对象,则返回第二个操作数。
如果有一个操作数是 null,则返回 null。
如果有一个操作数是 NaN,则返回 NaN。
如果有一个操作数是 undefined,则返回 undefined。
逻辑与操作符是一种短路操作符,意思就是如果第一个操作数决定了结果,那么永远不会对第二个操作数求值。对逻辑与操作符来说,如果第一个操作数是 false,那么无论第二个操作数是什么值,结果也不可能等于 true
逻辑或操作符由两个管道符(||)表示
与逻辑与类似,如果有一个操作数不是布尔值,那么逻辑或操作符也不一定返回布尔值。它遵循如下规则。
如果第一个操作数是对象,则返回第一个操作数。
如果第一个操作数求值为 false,则返回第二个操作数。
如果两个操作数都是对象,则返回第一个操作数。
如果两个操作数都是 null,则返回 null。
如果两个操作数都是 NaN,则返回 NaN。
如果两个操作数都是 undefined,则返回 undefined。
同样与逻辑与类似,逻辑或操作符也具有短路的特性。只不过对逻辑或而言,第一个操作数求值为true,第二个操作数就不会再被求值了
ECMAScript 定义了 3 个乘性操作符:乘法、除法和取模。这些操作符跟它们在 Java、C 语言及 Perl中对应的操作符作用一样,但在处理非数值时,它们也会包含一些自动的类型转换。如果乘性操作符有不是数值的操作数,则该操作数会在后台被使用 Number()转型函数转换为数值。这意味着空字符串会被当成 0,而布尔值 true 会被当成 1。
乘法操作符由一个星号(*)表示,可以用于计算两个数值的乘积
乘法操作符在处理特殊值时也有一些特殊的行为。
如果操作数都是数值,则执行常规的乘法运算,即两个正值相乘是正值,两个负值相乘也是正
值,正负符号不同的值相乘得到负值。如果 ECMAScript 不能表示乘积,则返回 Infinity 或-Infinity。
如果有任一操作数是 NaN,则返回 NaN。
如果是 Infinity 乘以 0,则返回 NaN。
如果是 Infinity 乘以非 0的有限数值,则根据第二个操作数的符号返回 Infinity 或-Infinity。
如果是 Infinity 乘以 Infinity,则返回 Infinity。
如果有不是数值的操作数,则先在后台用 Number()将其转换为数值,然后再应用上述规则。
除法操作符由一个斜杠(/)表示,用于计算第一个操作数除以第二个操作数的商
如果操作数都是数值,则执行常规的除法运算,即两个正值相除是正值,两个负值相除也是正
值,符号不同的值相除得到负值。如果ECMAScript不能表示商,则返回Infinity或-Infinity。
如果有任一操作数是 NaN,则返回 NaN。
如果是 Infinity 除以 Infinity,则返回 NaN。
如果是 0 除以 0,则返回 NaN。
如果是非 0 的有限值除以 0,则根据第一个操作数的符号返回 Infinity 或-Infinity。
如果是 Infinity 除以任何数值,则根据第二个操作数的符号返回 Infinity 或-Infinity。
如果有不是数值的操作数,则先在后台用 Number()函数将其转换为数值,然后再应用上述规则。
取模(余数)操作符由一个百分比符号(%)表示
如果操作数是数值,则执行常规除法运算,返回余数。
如果被除数是无限值,除数是有限值,则返回 NaN。
如果被除数是有限值,除数是 0,则返回 NaN。
如果是 Infinity 除以 Infinity,则返回 NaN。
如果被除数是有限值,除数是无限值,则返回被除数。
如果被除数是 0,除数不是 0,则返回 0。
如果有不是数值的操作数,则先在后台用 Number()函数将其转换为数值,然后再应用上述规则。
指数操作符
ECMAScript 7 新增了指数操作符,Math.
pow()现在有了自己的操作符**
不仅如此,指数操作符也有自己的指数赋值操作符**=,该操作符执行指数运算和结果的赋值操作
let squared = 3;
squared **= 2;
console.log(squared); // 9
加性操作符,即加法和减法操作符,一般都是编程语言中最简单的操作符。不过,在 ECMAScript中,这两个操作符拥有一些特殊的行为。与乘性操作符类似,加性操作符在后台会发生不同数据类型的转换。只不过对这两个操作符来说,转换规则不是那么直观。
如果两个操作数都是数值,加法操作符执行加法运算并根据如下规则返回结果:
如果有任一操作数是 NaN,则返回 NaN;
如果是 Infinity 加 Infinity,则返回 Infinity;
如果是-Infinity 加-Infinity,则返回-Infinity;
如果是 Infinity 加-Infinity,则返回 NaN;
如果是+0 加+0,则返回+0;
如果是-0 加+0,则返回+0;
如果是-0 加-0,则返回-0。
不过,如果有一个操作数是字符串,则要应用如下规则:
如果两个操作数都是字符串,则将第二个字符串拼接到第一个字符串后面;
如果只有一个操作数是字符串,则将另一个操作数转换为字符串,再将两个字符串拼接在一起。
如果有任一操作数是对象、数值或布尔值,则调用它们的 toString()方法以获取字符串,然后再应用前面的关于字符串的规则。对于 undefined 和 null,则调用 String()函数,分别获取"undefined"和"null"。
与加法操作符一样,减法操作符也有一组规则用于处理 ECMAScript 中不同类型之间的转换。
如果两个操作数都是数值,则执行数学减法运算并返回结果。
如果有任一操作数是 NaN,则返回 NaN。
如果是 Infinity 减 Infinity,则返回 NaN。
如果是-Infinity 减-Infinity,则返回 NaN。
如果是 Infinity 减-Infinity,则返回 Infinity。
如果是-Infinity 减 Infinity,则返回-Infinity。
如果是+0 减+0,则返回+0。
如果是+0 减-0,则返回-0。
如果是-0 减-0,则返回+0。
如果有任一操作数是字符串、布尔值、null 或 undefined,则先在后台使用 Number()将其转换为数值,然后再根据前面的规则执行数学运算。如果转换结果是 NaN,则减法计算的结果是NaN。
如果有任一操作数是对象,则调用其 valueOf()方法取得表示它的数值。如果该值是 NaN,则减法计算的结果是 NaN。如果对象没有 valueOf()方法,则调用其 toString()方法,然后再将得到的字符串转换为数值。
关系操作符执行比较两个值的操作,包括小于(<)、大于(>)、小于等于(<=)和大于等于(>=),用法跟数学课上学的一样。这几个操作符都返回布尔值。与 ECMAScript 中的其他操作符一样,在将它们应用到不同数据类型时也会发生类型转换和其他行为。
如果操作数都是数值,则执行数值比较。
如果操作数都是字符串,则逐个比较字符串中对应字符的编码。
如果有任一操作数是数值,则将另一个操作数转换为数值,执行数值比较。
如果有任一操作数是对象,则调用其 valueOf()方法,取得结果后再根据前面的规则执行比较。如果没有 valueOf()操作符,则调用 toString()方法,取得结果后再根据前面的规则执行比较。
如果有任一操作数是布尔值,则将其转换为数值再执行比较。
对字符串而言,关系操作符会比较字符串中对应字符的编码,而这些编码是数值。比较完之后,会返回布尔值。问题的关键在于,大写字母的编码都小于小写字母的编码。要得到确实按字母顺序比较的结果,就必须把两者都转换为相同的大小写形式(全大写或全小写),然后再比较。只要是数值和字符串比较,字符串就会先被转换为数值,然后进行数值比较。对于数值字符串而言,这样能保证结果正确。同时任何关系操作符在涉及比较 NaN 时都返回 false,在比较 NaN 时,无论是小于还是大于等于,比较的结果都会返回 false。
相等操作符
ECMAScript 中的相等和不相等操作符,原本在比较之前会执行类型转换,但很快就有人质疑这种转换是否应该发生。第一组是等于和不等于,它们在比较之前执行转换。第二组是全等和不全等,它们在比较之前不执行转换。
ECMAScript 中的等于操作符用两个等于号(==)表示,如果操作数相等,则会返回 true。不等于操作符用叹号和等于号(!=)表示,如果两个操作数不相等,则会返回 true。这两个操作符都会先进行类型转换(通常称为强制类型转换)再确定操作数是否相等。在转换操作数的类型时,相等和不相等操作符遵循如下则。
如果任一操作数是布尔值,则将其转换为数值再比较是否相等。false 转换为 0,true 转换为 1。
如果一个操作数是字符串,另一个操作数是数值,则尝试将字符串转换为数值,再比较是否相等。
如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()方法取得其原始值,再根据前面的规则进行比较。
在进行比较时,这两个操作符会遵循如下规则。
null 和 undefined 相等。
null 和 undefined 不能转换为其他类型的值再进行比较。
如果有任一操作数是 NaN,则相等操作符返回 false,不相等操作符返回 true。记住:即使两个操作数都是 NaN,相等操作符也返回 false,因为按照规则,NaN 不等于 NaN。
如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回 true。否则,两者不相等。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1v8rsEIZ-1613819376591)(image-20210220180128691.png)]
全等和不全等操作符与相等和不相等操作符类似,只不过它们在比较相等时不转换操作数。全等操
作符由 3 个等于号(===)表示,只有两个操作数在不转换的前提下相等才返回 true
注意 由于相等和不相等操作符存在类型转换问题,因此推荐使用全等和不全等操作符。这样有助于在代码中保持数据类型的完整性
条件操作符是 ECMAScript 中用途最为广泛的操作符之一,语法跟 Java 中一样
简单赋值用等于号(=)表示,将右手边的值赋给左手边的变量
逗号操作符可以用来在一条语句中执行多个操作
if 语句是使用最频繁的语句之一
if (condition) statement1 else statement2 这里的条件(condition)可以是任何表达式,并且求值结果不一定是布尔值。ECMAScript 会自动调用 Boolean()函数将这个表达式的值转换为布尔值。如果条件求值为 true,则执行语句statement1;如果条件求值为 false,则执行语句 statement2。这里的语句可能是一行代码,也可能是一个代码块(即包含在一对花括号中的多行代码)。使只有一行代码要执行也要选择用句块可以避免对什么条件下执行什么产生困惑。
do-while 语句是一种后测试循环语句,即循环体中的代码执行后才会对退出条件进行求值。换句话说,循环体内的代码至少执行一次。do-while 的语法如下:
do {
statement
} while (expression);
while 语句是一种先测试循环语句,即先检测退出条件,再执行循环体内的代码。因此,while 循
环体内的代码有可能不会执行。下面是 while 循环的语法:
while(expression) statement
for 语句也是先测试语句,只不过增加了进入循环之前的初始化代码,以及循环执行后要执行的表
达式,语法如下:
for (initialization; expression; post-loop-expression) statement
无法通过 while 循环实现的逻辑,同样也无法使用 for 循环实现。因此 for 循环只是将循环相关的代码封装在了一起而已。在 for 循环的初始化代码中,其实是可以不使用变量声明关键字的。不过,初始化定义的迭代器变量在循环执行完成后几乎不可能再用到了。因此,最清晰的写法是使用 let 声明迭代器变量,这样就可以将这个变量的作用域限定在循环中。如果只包含条件表达式,那么 for 循环实际上就变成了 while 循环。
for-in 语句是一种严格的迭代语句,用于枚举对象中的非符号键属性,语法如下:
for (property in expression) statement
ECMAScript 中对象的属性是无序的,因此 for-in 语句不能保证返回对象属性的顺序。所有可枚举的属性都会返回一次,但返回的顺序可能会因浏览器而异。如果 for-in 循环要迭代的变量是 null 或 undefined,则不执行循环体。
for-of 语句是一种严格的迭代语句,用于遍历可迭代对象的元素:
for ( const property of expression) statement 与for 循环一样,这里控制语句中的 const 也不是必需的。但为了确保这个局部变量不被修改,推荐使用 const。fo-of 循环会按照可迭代对象的 next()方法产生值的顺序迭代元素。
注意 ES2018 对 for-of 语句进行了扩展,增加了 for-await-of 循环,以支持生成期约(promise)的异步可迭代对象。
标签语句用于给语句加标签,语法如下:
label: statement 是个标签,可以在后面通过 break 或 continue 语句引用。标签语句的典型应用场景是嵌套循环。
break 和 continue 语句为执行循环代码提供了更严格的控制手段。其中,break 语句用于立即退出循环,强制执行循环后的下一条语句。而 continue 语句也用于立即退出循环,但会再次从循环顶部开始执行。
break 和 continue 都可以与标签语句一起使用,返回代码中特定的位置。这通常是在嵌套循环中。组合使用标签语句和 break、continue 能实现复杂的逻辑,但也容易出错。注意标签要使用描述性强的文本,而嵌套也不要太深。
with 语句的用途是将代码作用域设置为特定的对象,其语法是:
with (expression) statement; 使用 with 语句的主要场景是针对一个对象反复操作,这时候将代码作用域设置为该对象能提供便利。严格模式不允许使用 with 语句,否则会抛出错误。警告由于 with 语句影响性能且难于调试其中的代码,通常不推荐在产品代码中使用 with语句。
switch 语句是与 if 语句紧密相关的一种流控制语句,从其他语言借鉴而来。
这里的每个 case(条件/分支)相当于:“如果表达式等于后面的值,则执行下面的语句。”break关键字会导致代码执行跳出 switch 语句。如果没有 break,则代码会继续匹配下一个条件。default关键字用于在任何条件都没有满足时指定默认执行的语句(相当于 else 语句)。为避免不必要的条件判断,最好给每个条件后面都加上 break 语句。如果确实需要连续匹配几个条件,那么推荐写个注释表明是故意忽略了 break。switch 语句可以用于所有数据类型。switch 语句在比较每个条件的值时会使用全等操作符,因此不会强制转换数据类型。
函数对任何语言来说都是核心组件,因为它们可以封装语句,然后在任何地方、任何时间执行。
ECMAScript 中的函数使用 function 关键字声明,后跟一组参数,然后是函数体。
ECMAScript 中的函数不需要指定是否返回值。任何函数在任何时间都可以使用 return 语句来返回函数的值,用法是后跟要返回的值。
只要碰到 return 语句,函数就会立即停止执行并退出。因此,return 语句后面的代码不会被执行。return 语句也可以不带返回值。这时候,函数会立即停止执行并返回 undefined。这种用法最常用于提前终止函数执行,并不是为了返回值。
严格模式对函数也有一些限制:
函数不能以 eval 或 arguments 作为名称;
函数的参数不能叫 eval 或 arguments;
两个命名参数不能拥有同一个名称。
如果违反上述规则,则会导致语法错误,代码也不会执行。
小结
JavaScript 的核心语言特性在 ECMA-262 中以伪语言 ECMAScript 的形式来定义。ECMAScript 包含
所有基本语法、操作符、数据类型和对象,能完成基本的计算任务,但没有提供获得输入和产生输出的
机制。理解 ECMAScript 及其复杂的细节是完全理解浏览器中 JavaScript 的关键。下面总结一下ECMAScript 中的基本元素。
ECMAScript 中的基本数据类型包括 Undefined、Null、Boolean、Number、String 和 Symbol。
与其他语言不同,ECMAScript 不区分整数和浮点值,只有 Number 一种数值数据类型。
Object 是一种复杂数据类型,它是这门语言中所有对象的基类。
严格模式为这门语言中某些容易出错的部分施加了限制。
ECMAScript 提供了 C 语言和类 C 语言中常见的很多基本操作符,包括数学操作符、布尔操作符、
关系操作符、相等操作符和赋值操作符等。
这门语言中的流控制语句大多是从其他语言中借鉴而来的,比如 if 语句、for 语句和 switch
语句等。
ECMAScript 中的函数与其他语言中的函数不一样。
不需要指定函数的返回值,因为任何函数可以在任何时候返回任何值。
不指定返回值的函数实际上会返回特殊值 undefined。