「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」
语言基础
任何语言的核心所描述的都是这门语言在最基本的成名上如何工作,涉及语法、操作符、数据类型以及内置功能,在此基础上才可以构建复杂的解决方案。
语法(SYNTAX)
ECMAScript的语法很大程度上借鉴了C语言和其他类C语言,如Java和Perl。
标识符(Identifiers)
所谓 标识符 ,就是变量、函数、属性或函数参数的名称。
标识符可以由一个或多个下列字符组成
- 第一个字符必须是字母、下划线(_)或者美元符号($)
- 剩下的还可以为数字
标识符中的字母可以是扩展 ASCII(Extended ASCII)中的字母,也可以是 Unicode 的字母字符, 如 À 和 Æ(但不推荐使用)。
按照惯例,ECMAScript 标识符使用驼峰大小写形式,
语句(Statements)
ECMAScript 中的语句以分号结尾。省略分号意味着由解析器确定语句在哪里结尾。
关键字与保留字(Keywords and Reserved Words)
ECMA-262 描述了一组保留的 关键字,这些关键字有特殊用途,比如表示控制语句的开始和结束, 或者执行特定的操作。按照规定,保留的关键字不能用作标识符或属性名。
break case catch class const continue debugger default delete
do else export extends finally for function if import
in instanceof new return super switch this throw try
typeof var void while with yield
规范中也描述了一组未来的 保留字,同样不能用作标识符或属性名。它们是保留给将来做关键字用的。
变量(Variables)
ECMAScript 变量是松散类型的,意思是变量可以用于保存任何类型的数据。
var/const/let关键字可以声明变量
与 var 关键字不同,使用 let / const 在全局作用域中声明的变量不会成为 window 对象的属性. 也不会表现出声明提升(Declaration Hoisting)。 在 let /const 声明之前的执行瞬间被称为“暂时性死区”(temporal dead zone),在此 阶段引用任何后面才声明的变量都会抛出 ReferenceError。
提升(Hoist): 把所有变量声明/ 函数定义都拉到其当前作用域的顶部。
数据类型(Data Types)
ECMAScript 有 7 种简单数据类型(也称为原始类型):``Undefined、Null、Boolean、Number、 String 、 Symbo、 BigIntl`。
复杂数据类型:Object
typeof 操作符
操作符(operator):也叫算子或運算符(operator)在電腦編程上是指一種程式語言裡面行為類似函數的東西,他們可以進行諸如「加減」(+和-)或者「比較兩個數的大小」(<和>)以及逻辑运算等運算。編程语言通常内置一組运算符,某些情况下用户可以為现有的运算符添加新的含义,甚至定义全新的运算符。
由于 JavaScript 保存数值的方式,实际中可能存在正零(+0)和负零(-0)。正零和负零在所有情况下都被认为是等同的. 若要判断+-0,可以使用 Object.is(value1, value2)
String 类型
字符字面量(Character Literals)
字符串数据类型包含一些字符字面量,用于表示非打印字符或有其他用途的字符.
| 字面量(Literal) | 含义(Meaning) |
|---|---|
| \n | New line 换行 |
| \t | Tab 制表 |
| \b | Backspace 退格 |
| \r | Carriage return 回车 |
| \f | Form feed 换页 |
| \\ | Backslash() 反斜杠 |
| \' | Single quote(') 单引号,在字符串以单引号标示时使用,例如'He said, 'hey.'' |
| \" | Double quote(") 双引号,在字符串以双引号标示时使用,例如"He said, \"hey.\"" |
| \` | Backtick(`)反引号,同上 |
| \xnn | A character represented by hexadecimal code nn。以十六进制编码 nn 表示的字符(其中 n 是十六进制数字 0~F),例如\x41 等于"A" |
| \unnnn | A Unicode character represented by the hexadecimal code nnnn。以十六进制编码 nnnn 表示的 Unicode 字符(其中 n 是十六进制数字 0~F),例如\u03a3 等于希腊字 符"Σ" |
模板字面量标签函数(tag function)
标签函数本身是一个常规函数,通过前缀到模板字面量来应用自定义行为,如下例所示。标签函数 接收到的参数依次是原始字符串数组和对每个表达式求值的结果。这个函数的返回值是对模板字面量求 值得到的字符串。
详情见 本书 3.4.5 String类型 中 模板字面量标签函数(Template Literal Tag Functions)
let a = 6;
let b = 9;
function simpleTag(strings, aValExpression, bValExpression, sumExpression) { console.log(strings); console.log(aValExpression); console.log(bValExpression); console.log(sumExpression);
return 'foobar';
}
let untaggedResult = `${ a } + ${ b } = ${ a + b }`;
let taggedResult = simpleTag`${ a } + ${ b } = ${ a + b }`;
// ["", " + ", " = ", ""]
// 6
// 9
// 15
console.log(untaggedResult);
// "6 + 9 = 15"
console.log(taggedResult);
// "foobar"
原始字符串(Raw Strings)
使用模板字面量也可以直接获取原始的模板字面量内容(如换行符或 Unicode 字符),而不是被转 换后的字符表示。为此,可以使用默认的 String.raw 标签函数:
// Unicode 示例
// \u00A9 是版权符号
console.log(`\u00A9`); // ©
console.log(String.raw`\u00A9`);// \u00A9
Symbol 类型 TODO
Symbol(符号)是 ECMAScript 6 新增的数据类型。符号是原始值,且符号实例是唯一、不可变的。 符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。
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()对每个字符串键都执行幂等操作。第一次使用某个字符串调用时,它会检查全局运行时注册表,发现不存在对应的符号,于是就会生成一个新符号实例并添加到注册表中。后续使用相同 字符串的调用同样会检查注册表,发现存在与该字符串对应的符号,然后就会返回该符号实例。
let fooGlobalSymbol = Symbol.for('foo');
let otherFooGlobalSymbol = Symbol.for('foo');
console.log(fooGlobalSymbol === otherFooGlobalSymbol);
// true
// 即使采用相同的符号描述,在全局注册表中定义的符号跟使用 Symbol()定义的符号也并不等同:
let localSymbol = Symbol('foo');
let globalSymbol = Symbol.for('foo');
console.log(localSymbol === globalSymbol); // false
// 还可以使用 Symbol.keyFor()来查询全局注册表,这个方法接收符号(Symbol),返回该全局符号对应的字 符串键。如果查询的不是全局符号,则返回 undefined。
// 创建全局符号
let s = Symbol.for('foo');
console.log(Symbol.keyFor(s)); // foo
// 创建普通符号
let s2 = Symbol('bar');
console.log(Symbol.keyFor(s2)); // undefined
Symbol.keyFor(123); // TypeError: 123 is not a symbol
使用符号作为属性
凡是可以使用字符串或数值作为属性的地方, 都可以使用符号 Symbol。
常用内置符号(well-known symbol)
ECMAScript 6 也引入了一批常用内置符号(well-known symbol),用于暴露语言内部行为,开发者 可以直接访问、重写或模拟这些行为。这些内置符号都以 Symbol 工厂函数字符串属性的形式存在。
我们知道 for-of 循环会在相关对象上使用 Symbol.iterator 属性,那么就可以通过在自定义对象上重新定义 Symbol.iterator 的值,来改变 for-of 在迭代该对象时的行为。
这些内置符号也没有什么特别之处,它们就是全局函数 Symbol 的普通字符串属性,指向一个符号 的实例。所有内置符号属性都是不可写、不可枚举、不可配置的。
注意
在提到 ECMAScript 规范时,经常会引用符号在规范中的名称,前缀为@@。比如,@@iterator 指的就是 Symbol.iterator。
Symbol.asyncIterator
根据 ECMAScript 规范,这个符号作为一个属性表示“一个方法,该方法返回对象默认的 AsyncIterator。 由 for-await-of 语句使用”。换句话说,这个符号表示实现异步迭代器 API 的函数。
Symbol.hasInstance
根据 ECMAScript 规范,这个符号作为一个属性表示“一个方法,该方法决定一个构造器对象是否认可一个对象是它的实例。由 instanceof 操作符使用”。
Object 类型
每个 Object 实例都有如下属性和方法。
- constructor :用于创建当前对象的函数。 在前面的例子中, 这个属性的值就是 Object() 函数。
- hasOwnProperty(propertyName):用于判断当前对象实例(不是原型)上是否存在给定的属 性。要检查的属性名必须是字符串(如 o.hasOwnProperty("name"))或符号。
- isPrototypeOf(object):用于判断当前对象是否为另一个对象的原型。
- propertyIsEnumerable(propertyName):用于判断给定的属性是否可以使用(本章稍后讨 论的)for-in 语句枚举。与 hasOwnProperty()一样,属性名必须是字符串。
- toLocaleString():返回对象的字符串表示,该字符串反映对象所在的本地化执行环境。
- toString():返回对象的字符串表示。
- valueOf():返回对象对应的字符串、数值或布尔值表示。通常与 toString()的返回值相同。
操作符 (Operators)
ECMA-262 描述了一组可用于操作数据值的操作符,包括数学操作符(如加、减)、位操作符、关系操作符和相等操作符等。ECMAScript 中的操作符是独特的,因为它们可用于各种值,包括字符串、 数值、布尔值,甚至还有对象。在应用给对象时,操作符通常会调用 valueOf()和/或 toString()方 法来取得可以计算的值。
一元操作符(unary operator)
- 递增/递减操作符
- 一元加和减
位操作符(Bitwise Operators)
- 按位非 (Bitwise NOT
~ - 按位与(Bitwise ADD)
& - 按位或(Bitwise OR)
| - 按位异或(Bitwise XOR)
^ - 左移(Left Shift)
<< - 有符号右移(Signed Right Shift)
>> - 无符号右移(Unsigned Right Shift)
>>>
布尔操作符(Boolean Operators)
- 逻辑非(Logical NOT)
! - 逻辑与(Logical AND)
&& - 逻辑或(Logical OR)
||
乘性操作符(Multiplicative Operators)
ECMAScript 定义了 3 个乘性操作符:乘法、除法和取模。
- 乘法操作符(Multiply)
* - 除法操作符 (Divide)
/ - 取模操作符 (Modulus)
%
指数操作符( Exponentiation Operator)**
加性操作符 (Additive Operators)
加性操作符,即加法和减法操作符,一般都是编程语言中最简单的操作符。
- 加法操作符 (ADD )
- 减法操作符 (Subtract)
关系操作符(Relational Operators)
关系操作符执行比较两个值的操作,包括小于(<)、大于(>)、小于等于(<=)和大于等于(>=), 用法跟数学课上学的一样。这几个操作符都返回布尔值
The less-than (<), greater-than (>), less-than-or-equal-to (<=), and greater-than-or-equal-to (>=) relational operators perform comparisons between values in the same way that you learned in math class. Each of these operators returns a Boolean value.
相等操作符(Equality Operators)
- 等于和不等于(Equal and Not Equal )
== - 全等和不全等(Identically Equal and Not Identically Equal)
===
条件操作符(Conditional Operator)
variable = boolean_expression ? true_value : false_value;
赋值操作符 (Assignment Operator) =
逗号操作符(Comma Operator)
语句 (Statements)
- if 语句
if (condition) statement1 else statement2
- do-while 语句
do { statement } while (expression);
- while 语句
while(expression) statement
- for 语句
for (initialization; expression; post-loop-expression) statement
- for-in 语句
-
for-in 语句是一种严格的迭代语句,用于枚举对象中的非符号键(non-Symbol)属性
-
如果 for-in 循环要迭代的变量是 null 或 undefined,则不执行循环体。
-
for-in 语句不能保证返回对象属性的顺序
for (property in expression) statement
- for-of 语句
-
for-of 语句是一种严格的迭代语句,用于遍历可迭代对象的元素
-
for-of 循环会按照可迭代对象的 next()方法产生值的顺序迭代元素。
-
如果尝试迭代的变量不支持迭代,则 for-of 语句会抛出错误
-
ES2018 对 for-of 语句进行了扩展,增加了 for-await-of 循环,以支持生成期
约(promise)的异步可迭代对象。
for (property of expression) statement
-
The labeled statement can be used with
breakorcontinuestatements. It is prefixing a statement with an identifier which you can refer to.label: statementlet num = 0; outermost: for (let i = 0; i < 10; i++) { for (let j = 0; j < 10; j++) { if (i == 5 && j == 5) { break outermost; } num++; } } console.log(num); // 55 -
break 和 continue 语句
- break 和 continue 语句为执行循环代码提供了更严格的控制手段。其中,break 语句用于立即退 出循环,强制执行循环后的下一条语句。而 continue 语句也用于立即退出循环,但会再次从循环顶部 开始执行。
- break 和 continue 都可以与标签语句一起使用,返回代码中特定的位置。
- break 和 continue 在嵌套中若不适用 label标签,只会退出当前循环。
- 组合使用标签语句和 break、continue 能实现复杂的逻辑,但也容易出错。注意标签要使用描述 性强的文本,而嵌套也不要太深。
- return 语句会直接退出所有的循环,如果是在函数中则退出函数执行,如果循环在全局环境中则会报错(因为js中for是没有局部作用域的概念的,所以只有把for循环放在函数中时,才可以在for循环中使用return语句);
-
with 语句
- with 语句的用途是将代码作用域设置为特定的对象
- 严格模式不允许使用 with 语句,否则会抛出错误。
with (expression) statement; -
switch 语句
语法:Syntax
switch (expression) { case value1: //Statements executed when the //result of expression matches value1 [break;] case value2: //Statements executed when the //result of expression matches value2 [break;] ... case valueN: //Statements executed when the //result of expression matches valueN [break;] [default: //Statements executed when none of //the values match the value of the expression [break;]] }
函数(Function)
函数对任何语言来说都是核心组件,因为它们可以封装语句,然后在任何地方、任何时间执行。 ECMAScript 中的函数使用 function 关键字声明,后跟一组参数,然后是函数体。
严格模式对函数也有一些限制:
- 函数不能以 eval 或 arguments 作为名称;
- 函数的参数不能叫 eval 或 arguments;
- 两个命名参数不能拥有同一个名称。
如果违反上述规则,则会导致语法错误,代码也不会执行。