探索-JavaScript-ES2025-版--十二-

探索 JavaScript(ES2025 版)(十二)

原文:exploringjs.com/js/book/index.html

译者:飞龙

协议:CC BY-NC-SA 4.0

47 日期(Date)

原文:exploringjs.com/js/book/ch_dates.html

  1. 47.1 最佳实践:避免使用内置的 Date

    1. 47.1.1 在选择日期库时要注意的事项
  2. 47.2 时间标准

    1. 47.2.1 背景:UTC 与 Z 与 GMT

    2. 47.2.2 日期不支持时区

  3. 47.3 背景:日期时间格式(ISO)

    1. 47.3.1 技巧:在日期解析时附加一个 Z 以确保确定性
  4. 47.4 时间值

    1. 47.4.1 创建时间值

    2. 47.4.2 获取和设置时间值

  5. 47.5 创建日期

    1. 47.5.1 通过数字创建日期

    2. 47.5.2 从字符串中解析日期

    3. 47.5.3 创建日期的其他方法

  6. 47.6 获取器和设置器

    1. 47.6.1 时间单位获取器和设置器
  7. 47.7 将日期转换为字符串

    1. 47.7.1 包含时间的字符串

    2. 47.7.2 包含日期的字符串

    3. 47.7.3 包含日期和时间的字符串

    4. 47.7.4 其他方法

本章介绍了 JavaScript 用于处理日期的 API —— Date 类。

47.1 最佳实践:避免使用内置的 Date

JavaScript 的 Date API 使用起来比较繁琐。因此,对于任何与日期相关的功能,最好依赖于库。以下是一些已经实现或即将内置的功能:

  • Temporal 是一个即将推出的 ECMAScript 日期时间 API,可以通过 polyfills 使用。

  • Intl API 被大多数 JavaScript 平台支持,并提供了有用的日期时间相关功能:

流行库包括:

47.1.1 在选择日期库时要注意的事项

有两点需要注意:

  • 摇树干 可以显著减少库的大小。这是一种只部署库中在某个地方导入的导出项到 Web 服务器的技术。函数比类更容易进行摇树干。

  • 时区支持:如后面所述,Date不支持时区,这引入了许多陷阱,是其关键弱点。确保您的日期库支持它们。

47.2 时间标准

47.2.1 背景:UTC vs. Z vs. GMT

UTC、Z 和 GMT 是表示时间的方式,它们相似但略有不同:

  • UTC(协调世界时)是所有时区都基于的时间标准。它们相对于它进行指定。也就是说,没有国家或地区将 UTC 作为其本地时区。如果一个时区有夏令时,其相对于 UTC 的偏移量在一年中会发生变化。

  • Z(非洲时间区)是一个军事时间区,常在航空和军事中作为 UTC+0 的另一个名称使用。

  • GMT(格林威治标准时间)是某些欧洲和非洲国家使用的时间区。它是 UTC 加零小时,因此与 UTC 时间相同。

来源:

47.2.2 日期不支持时区

日期支持以下时间标准:

  • 本地时区(取决于当前位置)

  • UTC

  • 时间偏移(相对于 UTC)

根据操作,只有一些选项可用。例如,当将日期转换为字符串或提取时间单位(如月份的日期)时,我们只能在本地时区和 UTC 之间选择。

内部,日期以 UTC 存储。在转换到或从本地时区时,通过日期确定必要的偏移量。以下示例中,本地时区为欧洲/巴黎:

// CEST (Central European Summer Time)
assert.equal(
  new Date('2122-06-29').getTimezoneOffset(), -120);

// CET (Central European Time)
assert.equal(
  new Date('2122-12-29').getTimezoneOffset(), -60);

每当我们创建或转换日期时,都需要注意所使用的时间标准——例如:new Date()使用本地时区,而.toISOString()使用 UTC。

> new Date(2077, 0, 27).toISOString()
'2077-01-26T23:00:00.000Z'

日期将 0 解释为 1 月。在本地时区中,月份的日期是 27 日,但在 UTC 中是 26 日。

图标“提示”记录每个操作支持的时间标准

在本章的剩余部分,每个操作都注明了支持的时间标准。

47.2.2.1 无法指定时区的不利影响

无法指定时区有两个不利影响:

  • 它使得支持多个时区变得不可能。

  • 这可能导致特定位置的 bug。例如,前面的示例根据执行位置产生不同的结果。为了安全起见:

    • 尽可能使用基于 UTC 的操作

    • 解析字符串时使用Z或时差(有关更多信息,请参阅下一节)。

47.3 背景:日期时间格式(ISO)

日期时间格式描述:

  • 以下字符串被接受:

    • Date.parse()

    • new Date()

  • 由以下返回的字符串(总是最长格式):

    • Date.prototype.toISOString()

以下是一个由.toISOString()返回的日期时间字符串的示例:

'2033-05-28T15:59:59.123Z'

日期时间格式有以下结构:

  • 日期格式:Y=年;M=月;D=日

    YYYY-MM-DD
    YYYY-MM
    YYYY
    
    
  • 时间格式:T=分隔符(字符串'T');H=小时;m=分钟;s=秒和毫秒;Z=Zulu 时区(字符串'Z'

    THH:mm:ss.sss
    THH:mm:ss.sssZ
    
    THH:mm:ss
    THH:mm:ssZ
    
    THH:mm
    THH:mmZ
    
    
  • 日期时间格式:是日期格式后跟时间格式。

    • 例如(最长):YYYY-MM-DDTHH:mm:ss.sssZ

除了Z(即 UTC+0)之外,我们还可以指定相对于 UTC 的时差

  • THH:mm+HH:mm(等等)

  • THH:mm-HH:mm(等等)

47.3.1 小贴士:在日期解析中添加Z以使其确定性

如果我们在字符串末尾添加Z,日期解析在不同位置不会产生不同的结果。这就是我们在巴黎编写代码时会发生的情况:

  • 不带Z:输入是 1 月 27 日(隐含在欧洲/巴黎时区),输出是 1 月 26 日(UTC)。

    > new Date('2077-01-27T00:00').toISOString()
    '2077-01-26T23:00:00.000Z'
    
    
  • 带有Z:输入是 1 月 27 日,输出也是 1 月 27 日。

    > new Date('2077-01-27T00:00Z').toISOString()
    '2077-01-27T00:00:00.000Z'
    
    

47.4 时间值

时间值通过自 1970 年 1 月 1 日 00:00:00 UTC 以来的毫秒数表示日期。

时间值可以用来创建日期:

const timeValue = 0;
assert.equal(
  new Date(timeValue).toISOString(),
  '1970-01-01T00:00:00.000Z');

将日期强制转换为数字会返回其时间值:

> Number(new Date(123))
123

排序运算符将它们的操作数转换为数字。因此,我们可以使用这些运算符来比较日期:

assert.equal(
  new Date('1972-05-03') < new Date('2001-12-23'), true);

// Internally:
assert.equal(73699200000 < 1009065600000, true);

47.4.1 创建时间值

以下方法创建时间值:

  • Date.now(): number (UTC)

    返回当前时间作为时间值。

  • Date.parse(dateTimeStr: string): number (本地时区,UTC,时差)

    解析dateTimeStr并返回对应的时间值。

  • Date.UTC(year, month, date?, hours?, minutes?, seconds?, milliseconds?): number (UTC)

    返回指定 UTC 日期时间的时值。

47.4.2 获取和设置时间值

  • Date.prototype.getTime(): number (UTC)

    返回与 Date 对应的时值。

  • Date.prototype.setTime(timeValue) (UTC)

    this设置为timeValue编码的日期。

47.5 创建日期

47.5.1 通过数字创建日期

new Date(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, milliseconds?: number) (本地时区)

两个参数有陷阱:

  • 对于month,0 是 1 月,1 是 2 月,等等。

  • 如果 0 ≤ year ≤ 99,则添加 1900:

    > new Date(12, 1, 22, 19, 11).getFullYear()
    1912
    
    

    正是因为这个原因,在本章的其他地方,我们避免使用时间单位,而始终使用fullYear。但在这种情况下,我们别无选择。

例如:

> new Date(2077,0,27, 21,49).toISOString() // CET (UTC+1)
'2077-01-27T20:49:00.000Z'

注意,输入的小时(21)与输出的小时(20)不同。前者指的是本地时区,后者指的是 UTC。

47.5.2 从字符串解析日期

new Date(dateTimeStr: string)(本地时区,UTC,时间偏移)

如果末尾有 Z,则使用 UTC:

> new Date('2077-01-27T00:00Z').toISOString()
'2077-01-27T00:00:00.000Z'

如果末尾没有 Z 或时间偏移,则使用本地时区:

> new Date('2077-01-27T00:00').toISOString() // CET (UTC+1)
'2077-01-26T23:00:00.000Z'

如果字符串只包含日期,则将其解释为 UTC:

> new Date('2077-01-27').toISOString()
'2077-01-27T00:00:00.000Z'

47.5.3 创建日期的其他方式

  • new Date(timeValue: number)(UTC)

    > new Date(0).toISOString()
    '1970-01-01T00:00:00.000Z'
    
    
  • new Date()(UTC)

    new Date(Date.now()) 相同。

47.6 获取器和设置器

47.6.1 时间单位获取器和设置器

日期有用于时间单位的选择器和设置器 – 例如:

  • Date.prototype.getFullYear()

  • Date.prototype.setFullYear(num)

这些获取器和设置器符合以下模式:

  • 本地时区:

    • Date.prototype.get«Unit»()

    • Date.prototype.set«Unit»(num)

  • UTC:

    • Date.prototype.getUTC«Unit»()

    • Date.prototype.setUTC«Unit»(num)

这些是支持的时间单位:

  • 日期

    • FullYear

    • Month:月份(0–11)。陷阱:0 代表一月,等等。

    • Date:月份中的天数(1–31)

    • Day(仅获取器):星期中的天数(0–6,0 代表星期日)

  • 时间

    • Hours:小时(0–23)

    • Minutes:分钟(0–59)

    • Seconds:秒(0–59)

    • Milliseconds:毫秒(0–999)

还有一个不符合之前提到的模式的获取器:

  • Date.prototype.getTimezoneOffset()

    返回本地时区与 UTC 之间的时间差(以分钟为单位)。例如,对于欧洲/巴黎,它返回 -120(CEST,中欧夏令时)或 -60(CET,中欧时间):

    > new Date('2122-06-29').getTimezoneOffset()
    -120
    > new Date('2122-12-29').getTimezoneOffset()
    -60
    
    

47.7 将日期转换为字符串

示例日期:

const d = new Date(0);

47.7.1 包含时间的字符串

  • Date.prototype.toTimeString()(本地时区)

    > d.toTimeString()
    '01:00:00 GMT+0100 (Central European Standard Time)'
    
    

47.7.2 包含日期的字符串

  • Date.prototype.toDateString()(本地时区)

    > d.toDateString()
    'Thu Jan 01 1970'
    
    

47.7.3 包含日期和时间的字符串

  • Date.prototype.toString()(本地时区)

    > d.toString()
    'Thu Jan 01 1970 01:00:00 GMT+0100 (Central European Standard Time)'
    
    
  • Date.prototype.toUTCString()(UTC)

    > d.toUTCString()
    'Thu, 01 Jan 1970 00:00:00 GMT'
    
    
  • Date.prototype.toISOString()(UTC)

    > d.toISOString()
    '1970-01-01T00:00:00.000Z'
    
    

47.7.4 其他方法

以下三个方法实际上不是 ECMAScript 的一部分,而是ECMAScript 国际化 API的一部分。该 API 具有许多用于格式化日期的功能(包括对时区的支持),但不用于解析。

  • Date.prototype.toLocaleTimeString()

  • Date.prototype.toLocaleDateString()

  • Date.prototype.toLocaleString()

练习图标“exercise”练习:创建日期字符串

exercises/dates/create_date_string_test.mjs

48 创建和解析 JSON(JSON)

原文:exploringjs.com/js/book/ch_json.html

  1. 48.1 JSON 的发现和标准化

    1. 48.1.1 JSON 的语法已冻结
  2. 48.2 JSON 语法

  3. 48.3 JSON API

    1. 48.3.1 JSON.stringify(data, replacer?, space?)

    2. 48.3.2 JSON.parse(text, reviver?)

    3. 48.3.3 示例:转换为和从 JSON 转换

  4. 48.4 自定义序列化和解析(高级)

    1. 48.4.1 .stringify(): 指定要序列化对象的哪些属性

    2. 48.4.2 .stringify().parse(): 值访问者

    3. 48.4.3 示例:访问值

    4. 48.4.4 示例:序列化不受支持的价值

    5. 48.4.5 示例:解析不受支持的价值

  5. 48.5 常见问题解答

    1. 48.5.1 为什么 JSON 不支持注释?

JSON(“JavaScript 对象表示法”)是一种使用文本编码数据的存储格式。其语法是 JavaScript 表达式的子集。例如,考虑以下存储在文件jane.json中的文本:

{
  "first": "Jane",
  "last": "Porter",
  "married": true,
  "born": 1890,
  "friends": [ "Tarzan", "Cheeta" ]
}

JavaScript 有一个全局命名空间对象JSON,它提供了创建和解析 JSON 的方法。

48.1 JSON 的发现和标准化

2001 年,Douglas Crockford 发布了 JSON 规范,在json.org。他解释说:

我发现了 JSON。我并不声称发明了 JSON,因为它在自然界中已经存在。我所做的是我发现它,我为它命名,我描述了它的用途。我不声称自己是第一个发现它的人;我知道至少有其他人在我之前一年就发现了它。我找到的最早记录是,早在 1996 年,有人在 Netscape 使用 JavaScript 数组字面量进行数据通信,这至少比我偶然想到这个想法早五年。

后来,JSON 被标准化为ECMA-404

  • 第 1 版:2013 年 10 月

  • 第 2 版:2017 年 12 月

48.1.1 JSON 的语法已冻结

引用 ECMA-404 标准:

由于它非常简单,因此预计 JSON 语法永远不会改变。这使得作为基础符号的 JSON 具有巨大的稳定性。

因此,JSON 永远不会得到像可选尾随逗号、注释或未引用键这样的改进——无论它们是否被认为是可取的。然而,这仍然为创建编译为纯 JSON 的超集留出了空间。

48.2 JSON 语法

JSON 由以下 JavaScript 部分组成:

  • 复合:

    • 对象字面量:

      • 属性键是双引号字符串。

      • 属性值是 JSON 值。

      • 不允许有尾随逗号。

    • 数组字面量:

      • 元素是 JSON 值。

      • 不允许有孔或尾随逗号。

  • 原子:

    • null(但不是 undefined

    • 布尔值

    • 数字(不包括 NaN+Infinity-Infinity

    • 字符串(必须使用双引号)

因此,我们无法(直接)在 JSON 中表示循环结构。

48.3 JSON API

全局命名空间对象 JSON 包含两个用于处理 JSON 数据的方法:

  • JSON.stringify(data, replacer?, space?)

  • JSON.parse(text, reviver?)

下面的章节将解释这些方法是如何工作的。

48.3.1 JSON.stringify(data, replacer?, space?)

此方法将 JavaScript data 转换为 JSON 字符串。在本节中,我们忽略参数 replacer;它将在“自定义序列化和解析(高级)”中解释(§48.4)。

48.3.1.1 结果:单行文本

如果我们只提供第一个参数,.stringify() 返回单行文本:

assert.equal(
  JSON.stringify({prop: ['a', 'b']}),
  '{"prop":["a","b"]}'
);

48.3.1.2 结果:缩进行树

如果你提供一个非负整数作为 space,那么 .stringify() 会返回一行或多行,并且每层嵌套缩进 space 个空格:

assert.equal(
JSON.stringify({prop: ['a', 'b']}, null, 2),
`{
 "prop": [
 "a",
 "b"
 ]
}`
);

48.3.1.3 数据转换为 JSON 的细节

原始值

  • 支持的基本值会按预期序列化:

    > JSON.stringify(null)
    'null'
    > JSON.stringify(true)
    'true'
    > JSON.stringify(123)
    '123'
    > JSON.stringify('abc')
    '"abc"'
    
    
  • 不支持的数字:字符串 'null'

    > JSON.stringify(NaN)
    'null'
    > JSON.stringify(Infinity)
    'null'
    
    
  • 大整数(无论它们出现在哪里):TypeError

    > JSON.stringify(123n)
    TypeError: Do not know how to serialize a BigInt
    
    
  • 其他不支持的原始值不会被序列化;它们产生结果 undefined

    > JSON.stringify(undefined)
    undefined
    > JSON.stringify(Symbol())
    undefined
    
    

对象

  • 如果一个对象有 .toJSON() 方法,那么该方法的结果会被序列化:

    > JSON.stringify({toJSON() {return true}})
    'true' 
    

    日期有一个 .toJSON() 方法,它返回一个字符串:

    > JSON.stringify(new Date(2999, 11, 31))
    '"2999-12-30T23:00:00.000Z"'
    
    
  • 包装的基本值会被解包并序列化:

    > JSON.stringify(new Boolean(true))
    'true'
    > JSON.stringify(new Number(123))
    '123'
    
    
  • 产生 undefined 的不支持的值在数组内部会被序列化为 null

    > JSON.stringify([undefined, 123, Symbol()])
    '[null,123,null]'
    
    
  • 所有其他对象(除了函数)都会被序列化为对象字面量。如果属性有产生 undefined 的不支持的值,则该属性会被省略:

    > JSON.stringify({a: undefined, b: true, c: Symbol()})
    '{"b":true}'
    
    
  • 函数不会被序列化:

    > JSON.stringify(() => {})
    undefined
    
    

循环数据:如果数据是循环的,JSON.stringify() 会抛出异常:

const cycle = {};
cycle.prop = cycle;
assert.throws(
  () => JSON.stringify(cycle),
  /^TypeError: Converting circular structure to JSON/
);

48.3.2 JSON.parse(text, reviver?)

此方法将 JSON text 转换为 JavaScript 值。在本节中,我们忽略参数 reviver;它将在“自定义序列化和解析(高级)”中解释(§48.4)。

示例:

> JSON.parse('{"prop":["a","b"]}')
{ prop: [ 'a', 'b' ] }

48.3.3 示例:转换为和从 JSON

以下类实现了从(行 A)到(行 B)JSON 的转换。

class Point {
  static fromJson(jsonObj) { // (A)
    return new Point(jsonObj.x, jsonObj.y);
  }

  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toJSON() { // (B)
 return {x: this.x, y: this.y};
 }
}

  • 将 JSON 转换为点:我们使用静态方法 Point.fromJson() 解析 JSON 并创建 Point 实例。

    assert.deepEqual(
      Point.fromJson(JSON.parse('{"x":3,"y":5}')),
      new Point(3, 5) );
    
    
  • 将点转换为 JSON:JSON.stringify() 内部调用之前提到的 .toJSON() 方法(#json-stringify-details)。

    assert.equal(
      JSON.stringify(new Point(3, 5)),
      '{"x":3,"y":5}' );
    
    

图标“练习” 练习:将对象转换为 JSON 并从 JSON 转换

exercises/json/to_from_json_test.mjs

48.4 自定义字符串化和解析(高级)

字符串化和解析可以按以下方式自定义:

  • JSON.stringify(data, replacer?, space?)

    可选参数 replacer 包含以下内容之一:

    • 一个包含属性名称的数组。如果 data 中的值被转换为对象字面量,则仅考虑提到的属性。所有其他属性都将被忽略。

    • 一个 值访问者,一个可以在数据被转换为字符串之前对其进行转换的函数。

  • JSON.parse(text, reviver?)

    可选参数 reviver 包含一个值访问者,可以在返回之前转换解析后的 JSON 数据。

48.4.1 .stringfy():指定要转换为字符串的对象属性

如果 .stringify() 的第二个参数是一个数组,那么只有名称在该数组中提到的对象属性会被包含在结果中:

const obj = {
  a: 1,
  b: {
    c: 2,
    d: 3,
  }
};
assert.equal(
  JSON.stringify(obj, ['b', 'c']),
  '{"b":{"c":2}}');

48.4.2 .stringify().parse():值访问者

我所说的 值访问者 是一个转换 JavaScript 数据的函数:

  • JSON.stringify() 允许其参数 replacer 中的值访问者在数据被转换为字符串之前对其进行转换。

  • JSON.parse() 允许其参数 reviver 中的值访问者在返回之前转换解析后的 JavaScript 数据。

在本节中,JavaScript 数据被视为值的树。如果数据是原子的,它只有一个根的树。树中的所有值都依次传递给值访问者。根据访问者返回的内容,当前值将被省略、更改或保留。

一个值访问者有以下类型签名:

type ValueVisitor = (key: string, value: any) => any;

参数包括:

  • value: 当前值。

  • this: 当前值的父级。根值 r 的父级是 {'': r}

    • 注意:this 是一个隐式参数,仅在值访问者是一个普通函数时才可用。
  • key: 当前值在其父级中的键或索引。根值的键是 ''

值访问者可以返回:

  • value: 表示不会有任何变化。

  • 一个不同的值 x:导致输出树中的 value 被替换为 x

  • undefined: 导致输出树中省略 value

48.4.3 示例:访问值

以下代码显示了值访问者看到值的顺序:

const log = [];
function valueVisitor(key, value) {
  log.push({this: this, key, value});
  return value; // no change
}

const root = {
  a: 1,
  b: {
    c: 2,
    d: 3,
  }
};
JSON.stringify(root, valueVisitor);
assert.deepEqual(log, [
  { this: { '': root }, key: '',  value: root   },
  { this: root        , key: 'a', value: 1      },
  { this: root        , key: 'b', value: root.b },
  { this: root.b      , key: 'c', value: 2      },
  { this: root.b      , key: 'd', value: 3      },
]);

如我们所见,JSON.stringify() 的替换器是自顶向下访问值(先根后叶)。这样做的原因是我们正在将 JavaScript 值转换为 JSON 值。一个单一的 JavaScript 对象可能扩展为一个 JSON 兼容值的树。

与之相反,JSON.parse() 的恢复者是从下往上访问值的(先访问叶子节点,后访问根节点)。选择这种方向的逻辑是我们正在将 JSON 值组装成 JavaScript 值。因此,我们需要在转换整体之前先转换部分。

48.4.4 示例:将不支持的数据类型转换为字符串

JSON.stringify() 对正则表达式对象没有特殊支持——它将它们当作普通对象来序列化:

const obj = {
  name: 'abc',
  regex: /abc/ui,
};
assert.equal(
  JSON.stringify(obj),
  '{"name":"abc","regex":{}}');

我们可以通过一个替换者来修复这个问题:

function replacer(key, value) {
  if (value instanceof RegExp) {
    return {
      __type__: 'RegExp',
      source: value.source,
      flags: value.flags,
    };
  } else {
    return value; // no change
  }
}
assert.equal(
JSON.stringify(obj, replacer, 2),
`{
 "name": "abc",
 "regex": {
 "__type__": "RegExp",
 "source": "abc",
 "flags": "iu"
 }
}`);

48.4.5 示例:解析不支持的数据类型

JSON.parse() 上一个节点的结果,我们需要一个恢复者:

function reviver(key, value) {
  // Very simple check
  if (value && value.__type__ === 'RegExp') {
    return new RegExp(value.source, value.flags);
  } else {
    return value;
  }
}
const str = `{
 "name": "abc",
 "regex": {
 "__type__": "RegExp",
 "source": "abc",
 "flags": "iu"
 }
}`;
assert.deepEqual(
  JSON.parse(str, reviver),
  {
    name: 'abc',
    regex: /abc/ui,
  });

48.5 常见问题解答

48.5.1 为什么 JSON 不支持注释?

Douglas Crockford 在 2012 年 5 月 1 日的一篇 Google+ 帖子中解释了原因:

我从 JSON 中移除了注释,因为我看到人们正在使用它们来保存解析指令,这种做法会破坏互操作性。我知道缺少注释会让一些人感到难过,但这不应该。

假设你正在使用 JSON 来保存配置文件,你希望对其进行注释。你可以插入所有喜欢的注释。然后通过 JSMin [一个 JavaScript 压缩器] 处理它,然后再将其交给你的 JSON 解析器。

X 杂项主题

原文:exploringjs.com/js/book/pt_misc.html

49 下一步:Web 开发概览

原文:exploringjs.com/js/book/ch_next-steps.html

  1. 49.1 防止感到不知所措的技巧

  2. 49.2 值得学习的 Web 开发知识

    1. 49.2.1 关注 WebAssembly(Wasm)!
  3. 49.3 JavaScript 工具概览

    1. 49.3.1 构建:从我们编写的 JavaScript 到我们部署的 JavaScript

    2. 49.3.2 静态检查

    3. 49.3.3 测试

    4. 49.3.4 包管理器

    5. 49.3.5 库

  4. 49.4 与 JavaScript 无关的工具

你现在已经了解了大多数 JavaScript 语言。本章概述了 Web 开发并描述了下一步。它回答了以下问题:

  • 我接下来应该学习什么来进行 Web 开发?

  • 我应该了解哪些与 JavaScript 相关的工具?

49.1 防止感到不知所措的技巧

Web 开发已经成为一个庞大的领域:在 JavaScript、Web 浏览器、服务器端 JavaScript、JavaScript 库和 JavaScript 工具之间,有很多东西需要了解。此外,一切都在不断变化:有些东西过时了,新的东西被发明了等等。

当面对这个不断变化的知识浩瀚时,如何避免感到不知所措?

  • 专注于你最常使用的 Web 技术,并深入学习它们。如果你做前端开发,那可能是 JavaScript、CSS、SVG 或其他东西。

  • 对于 JavaScript:了解语言,但也要尝试以下每个类别中的一个工具(这些将在稍后详细介绍)。

    • 编译器:将未来的 JavaScript 或 JavaScript 的超集编译成普通 JavaScript。

    • 打包器:将 Web 应用程序使用的所有模块合并成一个文件(脚本或模块)。这使加载更快,并能够消除死代码。

    • 静态检查器。例如:

      • Linters:检查反模式、样式违规等问题。

      • 类型检查器:静态类型检查 JavaScript 并报告错误。

    • 测试库和工具

    • 版本控制(通常是 git)

图标“提示”相信你按需学习的本领

出于纯粹的好奇心学习某件事是值得赞扬的。但我对试图学习一切并过于分散精力持谨慎态度。这也会引起对知识不足的焦虑(因为你永远不会知道足够多)。相反,相信你按需学习的本领!

49.2 值得学习的 Web 开发知识

这些是值得学习的一些 Web 开发知识:

  • 浏览器 API,如 文档对象模型(DOM),浏览器在内存中对 HTML 的表示。它们是任何前端开发的基础。

  • 与 JavaScript 相关的技术,如 HTML 和 CSS。

  • 前端框架:当你开始学习网页开发时,编写没有库的用户界面可能会有所帮助。一旦你更有信心,前端框架可以使许多事情变得更容易,特别是对于大型应用程序。流行的框架包括:Alpine.js, Angular, Ember, Lit, Preact, Qwik, React, Solid, Stencil, Svelte, Vue.js。

  • JavaScript 运行时:一些 JavaScript 平台用于在服务器上运行代码。但它们也用于运行命令行工具。最受欢迎的运行时是 Node.js。大多数与 JavaScript 相关的工具(甚至编译器!)都是用基于 Node.js 的 JavaScript 实现的,并通过 npm 安装。开始使用 Node.js 的一个好方法是 将其用于 shell 脚本

    • 其他 JavaScript 运行时包括:Deno, Bun。

    • 该领域的重要标准组织:WinterCG(Web-interoperable Runtimes Community Group)。“该社区组旨在为 JavaScript 运行时提供一个协作 API 互操作性的空间。我们专注于记录和改进跨运行时(尤其是非浏览器运行时)的 Web 平台 API 的互操作性。”

  • JavaScript 工具:现代 Web 开发涉及许多工具。在本章的后面部分,将概述当前的工具生态系统。

  • 渐进式 Web 应用(PWAs):渐进式 Web 应用的核心思想是赋予 Web 应用传统上只有原生应用才有的功能——例如:在移动和桌面操作系统上的原生安装;离线操作;向用户显示通知。Google 发布了 一个清单,详细说明了什么使 Web 应用成为 渐进式。最低要求包括:

    • 应用程序必须通过 HTTPS(而不是不安全的 HTTP)提供服务。

    • 应用程序必须有一个 Web App Manifest 文件,指定应用程序名称和图标(通常以多个分辨率)等元数据。图标的文件也必须存在。

    • 应用程序必须有一个 服务工作者:应用程序在后台运行的基础层,在单独的进程中(独立于网页)。其一项职责是在没有互联网连接时保持应用程序运行。其中,两种机制帮助它做到这一点:它是一个本地代理,监督应用程序的所有 Web 资源请求。它还可以访问浏览器的缓存。因此,当应用程序离线时,它可以使用缓存来满足请求——在最初缓存所有关键资源之后。服务工作者还包括在后台同步数据;接收服务器发送的推送消息;以及上述向用户显示通知的功能。

学习 Web 开发(包括 JavaScript)的一个好资源是 MDN Web 文档

49.2.1 关注 WebAssembly (Wasm)!

WebAssembly 是一个内置在大多数 JavaScript 引擎中的通用虚拟机。我们通常得到以下的工作分配:

  • JavaScript 用于动态、高级代码。

  • WebAssembly 用于静态、底层代码。

对于静态代码,WebAssembly 相当快:将 C/C++代码编译到 WebAssembly,其速度大约是相同代码编译为本地代码的 50% (source)。用例包括支持新的视频格式、机器学习、游戏等。它有助于它相对容易地将现有的代码库(例如用 C 编写的代码)编译到 WebAssembly。

WebAssembly 作为各种语言的编译目标工作得很好。这意味着 JavaScript 会被编译到 WebAssembly 或被另一种语言取代吗?

49.2.1.1 JavaScript 会被编译到 WebAssembly 吗?

JavaScript 引擎为 JavaScript 的高度动态特性执行了许多优化。如果我们想将 JavaScript 编译到 WebAssembly,我们不得不在 WebAssembly 之上实现这些优化。结果将比当前引擎慢,并且具有类似的代码库。因此,我们不会得到任何好处。

49.2.1.2 JavaScript 会被另一种语言取代吗?

WebAssembly 意味着 JavaScript 即将被另一种语言取代吗?WebAssembly 确实使得在 Web 浏览器中支持除 JavaScript 之外的语言变得更加容易。但那些语言在该平台上面临几个挑战:

  • 所有浏览器 API 都是基于 JavaScript 的。

  • 其他语言的运行时(标准库等)会产生额外的内存开销,而 JavaScript 的运行时已经内置在 Web 浏览器中。

  • JavaScript 广为人知,拥有许多库和工具等。

对于动态代码,JavaScript 相对较快。因此,在可预见的未来,它可能仍然是高级代码最受欢迎的选择。对于底层代码,将更多静态语言(如 Rust)编译到 WebAssembly 是一个有趣的选择。

由于它只是一个虚拟机,关于 WebAssembly 实际上相关的学习内容并不多。但值得注意它在 Web 开发中不断发展的角色。它也作为独立的虚拟机变得越来越受欢迎:

  • 该领域的重要标准组织:“字节码联盟 是一个非营利组织,致力于创建基于 WebAssembly 和 WebAssembly 系统接口 (WASI) 等标准的安全新软件基础。”

  • WebAssembly 系统接口 (WASI): “一组针对编译为 W3C WebAssembly (Wasm) 标准的软件的标准 API 规范。WASI 旨在为可以从任何语言编译为 Wasm 并在任何地方运行的应用程序提供安全的标准接口——从浏览器到云到嵌入式设备。”

  • WebAssembly 组件模型 (WCM): “用于构建可互操作 Wasm 库、应用程序和环境的广泛架构。”

  • warg – Wasm 包的安全注册协议:WebAssembly 包管理器的重要基础。

49.3 JavaScript 工具概述

在本节中,我们来看看:

  • 工具类别

  • 那些类别中特定工具的名称

前者更为重要。随着工具的流行与淘汰,名称会发生变化,但我希望你能看到其中的一些。

49.3.1 构建:从我们编写的 JavaScript 到我们部署的 JavaScript

构建 JavaScript 意味着将我们编写的 JavaScript 转换为我们部署的 JavaScript。以下工具通常涉及此过程。

49.3.1.1 转译器

转译器是一种将源代码编译成源代码的编译器。在 JavaScript 社区中流行的两个转译器是:

  • TypeScript 是 JavaScript 的超集。大致上,它是 JavaScript 的最新版本加上静态类型。TypeScript 编译器 tsc 执行两个任务:

    • 它会检查 JavaScript 代码的类型。

    • 它将 TypeScript 编译成 JavaScript(看起来仍然非常相似)。

    一个重要的趋势是使用更快的工具(如下面的打包器)来处理相对简单的将 TypeScript 编译成 JavaScript 的任务,并且只在开发期间使用 TypeScript 编译器进行类型检查——这是一个复杂的任务(到目前为止,还没有出现 tsc 的实际替代品)。

  • Babel 将即将到来的和现代 JavaScript 功能编译成较旧的语言版本。这意味着我们可以在代码中使用新功能,并且仍然能在较旧的浏览器上运行它。

    • 大多数浏览器现在都是“持续更新”的,并且经常更新自己。在那之前,Babel 是必不可少的。现在它被使用的较少,这也得益于 TypeScript 的流行。
49.3.1.2 压缩

压缩意味着将 JavaScript 编译成等效的、更小的(即字符更少)JavaScript。它通过重命名变量、删除注释、删除空白等来实现。

例如,给定以下输入:

let numberOfOccurrences = 5;
if (Math.random()) {
  // Math.random() is not zero
  numberOfOccurrences++
}

压缩工具可能会生成:

let a=5;Math.random()&&a++;

打包器(见下文)通常支持压缩。

49.3.1.3 打包器

打包器 编译和优化 JavaScript 应用的代码。打包器的输入是许多文件——应用的所有代码以及它使用的库。打包器将这些输入文件组合起来,生成更少的输出文件(这通常可以提高性能)。

打包器通过摇树优化等技术最小化其输出的大小:只有那些在某个地方(跨所有代码,考虑传递性导入)被导入的模块导出会被放入输出中。

在打包的同时执行编译步骤,如转译和压缩,也是很常见的。

流行的打包工具包括:

  • 更像是一个框架(工具掌握控制权):

    • Vite
  • 更像是一个库(我们控制工具):

    • esbuild

    • Rollup

    • Rolldown

  • 在以下两者之间:

    • Parcel

    • Rspack

    • TurboPack

    • webpack

49.3.1.4 任务运行器

有时会有涉及多个工具或调用具有特定参数的工具的构建任务。任务运行器(遵循 Unix make的传统)让我们为这样的任务定义更简单的名称,并且通常还帮助连接工具和处理文件。有以下几种:

  • 专用任务运行器:grunt, gulp, broccoli 等。

  • 可以用作简单任务运行器的工具:

    • 大多数包管理器都可以运行任务,例如通过package.json中的"scripts"

    • 大多数运行时也自带了运行任务的内建支持。

49.3.2 静态检查

静态检查意味着对源代码进行静态分析(不运行它)。它可以用来检测各种问题。工具有:

  • Linters:检查源代码中的问题模式、未使用变量等。如果你还在学习语言,linters 特别有用,因为它们会指出你是否做错了什么。

    • 流行的 linters 包括 ESLint, Biome, oxlint, quick-lint-js
  • 代码风格检查器:检查代码是否格式正确。它们考虑缩进、括号后的空格、逗号后的空格等。

    • 示例:JSCS(JavaScript 代码风格检查器)
  • 代码格式化工具:根据我们可以自定义的规则自动格式化我们的代码。

    • 示例:Prettier, Biome
  • 类型检查器:为 JavaScript 添加静态类型检查。

    • 最受欢迎的类型检查器:TypeScript(它也是一个转译器)

49.3.3 测试

JavaScript 有许多测试框架——例如:

  • 单元测试:AVA, Jasmine, Jest, Mocha, QUnit, Vitest 等。

    • JavaScript 运行时 Node.js、Deno 和 Bun 都内置了测试运行器。
  • 集成测试:Jenkins, Cypress 等。

  • 用户界面测试:CasperJS, Nightwatch.js, TestCafé, Cypress, Playwright, Web Test Runner 等。

  • 自动化浏览器(例如用于测试):Puppeteer, Selenium 等。

49.3.4 包管理器

最受欢迎的 JavaScript 包管理器是 npm。它最初是 Node.js 的包管理器,但后来也成为了客户端 Web 开发和各种工具的领导者。

以下是对 npm 的替代方案,它们使用 npm 的包注册表(想想在线数据库):

  • Yarn对 npm 有另一种看法;它所开创的一些功能现在也被 npm 支持。

  • pnpm专注于在本地安装包时节省空间。

此外,还有 JSR(JavaScript 注册):

  • 它专注于 TypeScript,但也支持 JavaScript。

  • 它可以被所有上述的包管理器(包括 npm)使用。

  • 对它的支持内置在 Deno 中。JSR 是由 Deno 背后的团队创建的,旨在与之良好匹配。

49.3.5 库

  • 各种助手:lodash(最初基于 Underscore.js 库)是 JavaScript 最受欢迎的通用助手库之一。

  • 数据结构:以下库是众多例子中的两个。

    • Immutable.js为 JavaScript 提供了不可变的数据结构。

    • Immer是 Immutable.js 的一个有趣的轻量级替代品。它也不改变它操作的数据,但它与普通对象、数组、集合和映射一起工作。

  • 日期库:JavaScript 对日期的内置支持有限,且充满了陷阱。关于日期的章节列出了我们可以使用的库。

  • 国际化:在这个领域,ECMAScript 的标准库通过ECMAScript 国际化 API (ECMA-402)得到补充。它通过全局变量Intl访问,并在大多数现代浏览器中可用。

  • 实现和访问服务:以下是由各种库和工具支持的两种流行选项。

    • REST(代表状态传输)是服务中一个流行的选项,基于 HTTP(S)。

    • RPC(远程过程调用)是与服务器通信的另一种流行范式。对于 RPC 有许多标准和方法——两个例子:

      • JSON-RPC 标准: “一种轻量级的远程过程调用协议。它被设计得非常简单!”

      • gRPC 框架: “gRPC 是一个现代的开源高性能远程过程调用(RPC)框架,可以在任何环境中运行。它可以高效地连接数据中心内和跨数据中心的多个服务,并具有可插拔的支持,包括负载均衡、跟踪、健康检查和身份验证。它也适用于分布式计算的最后一英里,以连接设备、移动应用程序和浏览器到后端服务。”

    • GraphQL更复杂(例如,它可以组合多个数据源)并支持查询语言。

49.4 工具(与 JavaScript 无关)

由于 JavaScript 只是网络开发中涉及的多种工件之一,因此存在更多工具。这些只是其中的一些例子:

  • CSS:

    • 压缩器:通过删除注释等来减小 CSS 的大小。

    • 预处理器:允许我们编写紧凑的 CSS(有时还增加了控制流结构等),这些 CSS 在部署时会被展开成更冗长的 CSS。

    • 框架:提供布局、外观良好的用户界面组件等方面的帮助。

  • 图片:自动优化位图图像的大小等。

XI 附录

原文:exploringjs.com/js/book/pt_appendices.html

A 索引

原文:exploringjs.com/js/book/ch_index.html

  • __proto__, #1

  • --x, #1

  • x--, #1

  • -x, #1

  • ,(逗号运算符), #1

  • !x, #1

  • c ? t : e, #1

  • func?.(«arg0», «arg1», ···), #1

  • obj?.[«expr»], #1

  • obj?.prop, #1

  • x - y, #1

  • x ??= y, #1

  • x ?? d, #1

  • x ** y, #1

  • x * y, #1

  • x / y, #1

  • x && y, #1

  • x & y, #1

  • x ٪ y, #1

  • x ^ y, #1

  • x + y, #1

  • x << y, #1

  • x === y, #1

  • x >>> y, #1

  • x >> y, #1

  • x ¦¦ y, #1

  • x ¦ y, #1

  • ++x, #1

  • x++, #1

  • +x, #1

  • =, #1

  • ~x, #1

  • 访问器(对象字面量), #1

  • 加法, #1

  • AggregateError, #1

  • AMD 模块, #1

  • 匿名函数表达式, #1

  • 参数, #1

  • 参数与形参, #1

  • Array, #1

  • 数组空洞, #1

  • 数组索引, #1

  • 数组字面量, #1

  • 数组解构, #1

  • 类数组对象, #1

  • 数组,密集, #1

  • 数组,多维, #1

  • new Array(), #1

  • 数组,稀疏, #1

  • Array.from(), #1

  • Array.of(), #1

  • Array.prototype.at(), #1

  • Array.prototype.concat(), #1

  • Array.prototype.copyWithin(), #1

  • Array.prototype.entries(), #1

  • Array.prototype.every(), #1

  • Array.prototype.fill(), #1

  • Array.prototype.filter(), #1

  • Array.prototype.find(), #1

  • Array.prototype.findIndex(), #1

  • Array.prototype.findLast(), #1

  • Array.prototype.findLastIndex(), #1

  • Array.prototype.flat(), #1

  • Array.prototype.flatMap(), #1

  • Array.prototype.forEach(), #1

  • Array.prototype.includes(), #1

  • Array.prototype.indexOf(), #1

  • Array.prototype.join(), #1

  • Array.prototype.keys(), #1

  • Array.prototype.lastIndexOf(), #1

  • Array.prototype.map(), #1

  • Array.prototype.pop(), #1

  • Array.prototype.push(), #1

  • Array.prototype.reduce(), #1

  • Array.prototype.reduceRight(), #1

  • Array.prototype.reverse(), #1

  • Array.prototype.shift(), #1

  • Array.prototype.slice(), #1

  • Array.prototype.some(), #1

  • Array.prototype.sort(), #1

  • Array.prototype.splice(), #1

  • Array.prototype.toLocaleString(), #1

  • Array.prototype.toReversed(), #1

  • Array.prototype.toSorted(), #1

  • Array.prototype.toSpliced(), #1

  • Array.prototype.toString(), #1

  • Array.prototype.unshift(), #1

  • Array.prototype.values(), #1

  • Array.prototype.with(), #1

  • ArrayBuffer,#1

  • new ArrayBuffer(),#1

  • ArrayBuffer.isView(),#1

  • get ArrayBuffer.prototype.byteLength,#1

  • get ArrayBuffer.prototype.maxByteLength,#1

  • get ArrayBuffer.prototype.resizable,#1

  • ArrayBuffer.prototype.resize(),#1

  • ArrayBuffer.prototype.slice(),#1

  • 数组,固定布局,#1

  • 数组,序列,#1

  • 箭头函数,#1

  • ASCII 转义,#1

  • ASI(自动分号插入),#1

  • assert(模块),#1

  • assert.deepEqual(),#1

  • assert.equal(),#1

  • assert.fail(),#1

  • assert.notDeepEqual(),#1

  • assert.notEqual(),#1

  • assert.throws(),#1

  • 断言,#1

  • 赋值运算符,#1

  • async,#1

  • 异步函数,#1

  • async function*,#1

  • async-await,#1

  • 异步生成器,#1

  • 异步可迭代对象,#1

  • 异步迭代,#1

  • 异步迭代器,#1

  • 异步编程,#1

  • 属性的属性,#1

  • 自动分号插入(ASI),#1

  • await(异步函数),#1

  • await(异步生成器),#1

  • 基类,#1

  • 大端序,#1

  • 大整数,#1

  • BigInt64Array,#1

  • BigUint64Array,#1

  • 二进制整数字面量,#1

  • 绑定(变量),#1

  • 按位与,#1

  • 按位非,#1

  • 按位或,#1

  • 按位异或,#1

  • 布尔值,#1

  • Boolean(),#1

  • 绑定变量,#1

  • break,#1

  • 打包器,#1

  • 打包,#1

  • 回调(异步模式),#1

  • 回调函数,#1

  • 驼峰式命名,#1, #2

  • case,驼峰,#1

  • case,破折号,#1

  • case,短横线,#1

  • case,蛇形,#1

  • case,下划线,#1

  • catch,#1

  • 类,#1, #2

  • 类声明,#1

  • 类定义,#1

  • 类表达式,#1

  • 类字符串析取,#1

  • 类,基类,#1

  • 类,派生,#1

  • 类,混入,#1

  • 类,扩展,#1

  • 闭包,#1

  • 代码点,#1

  • 代码单元,#1

  • 强制转换,#1

  • 组合函数,Promise,#1

  • 逗号操作符,#1

  • CommonJS 模块,#1

  • 按身份比较,#1

  • 按值比较,#1

  • 计算属性键,#1

  • 字符串连接,#1

  • 条件运算符,#1

  • 控制台,#1

  • console.error(), #1

  • console.log(), #1

  • const, #1

  • 常量,#1

  • 构造函数(普通函数的作用),#1

  • continue, #1

  • 转换为 [类型],#1

  • 转换为原始类型,#1

  • 协调世界时 (UTC),#1

  • 连字符命名法,#1, #2

  • DataView, #1

  • new DataView(), #1

  • get DataView.prototype.buffer, #1

  • get DataView.prototype.byteLength, #1

  • get DataView.prototype.byteOffset, #1

  • DataView.prototype.get(), #1

  • DataView.prototype.set(), #1

  • 日期,#1

  • 日期时间格式,#1

  • 十进制浮点字面量,#1

  • 十进制整数字面量,#1

  • 减量运算符(前缀),#1

  • 减量运算符(后缀),#1

  • 默认导出,#1

  • 默认值(解构),#1

  • 默认值(参数),#1

  • 默认值运算符 (??), #1

  • delete, #1

  • 删除属性,#1

  • 稠密数组,#1

  • 派生类,#1

  • 属性描述符,#1

  • 摧毁性操作,#1

  • 解构,#1

  • 解构数组,#1

  • 对象解构,解构赋值

  • 字典对象,对象操作

  • 直接方法调用,类方法调用

  • 分发方法调用,类方法调用

  • 除法运算符,数字操作

  • 除法,数字操作

  • do-while 循环,循环控制

  • 动态导入,模块导出

  • 动态 this,可调用对象

  • 动态与静态,变量赋值

  • 提前激活,变量赋值

  • Ecma,历史

  • ECMA-262,历史

  • ECMAScript,历史

  • ECMAScript 模块,模块导出

  • ECMAScript 提案,历史

  • Eich, Brendan,历史

  • 字节序(类型化数组),类型化数组

  • 可枚举性,对象属性

  • 可枚举(属性属性),对象属性

  • 等于运算符,运算符

  • ES 模块,模块导出

  • 转义,ASCII,字符串操作

  • 转义,Unicode 码点,字符串操作

  • 转义,Unicode 码位,字符串操作, 字符串操作

  • 转义 HTML,模板字符串

  • eval(),动态代码评估

  • 评估表达式,语法

  • 事件(异步模式),异步 JavaScript

  • 事件循环,异步 JavaScript

  • 异常,异常处理

  • 练习,入门,练习

  • 幂运算,数字操作

  • export,模块导出

  • export default,模块导出和默认导出

  • export, default, 模块导出和默认导出

  • export, 命名导出,模块导出和默认导出

  • 表达式,语法

  • 扩展类,类扩展

  • extends,类扩展

  • 外部迭代,同步生成器

  • 提取方法, #1

  • false, #1

  • 假值, #1

  • 假值, #1

  • finally, #1

  • 固定布局数组, #1

  • 固定布局对象, #1

  • 标志(正则表达式),#1

  • Float16Array, #1

  • Float32Array, #1

  • Float64Array, #1

  • 浮点字面量, #1

  • for, #1

  • for-await-of, #1

  • for-in, #1

  • for-of, #1

  • 自由变量, #1

  • 已满足(Promise 状态), #1

  • 函数声明, #1

  • 匿名函数表达式, #1

  • 命名函数表达式, #1

  • 函数,箭头函数, #1

  • 函数,普通函数, #1

  • 函数,普通函数的角色, #1

  • 函数,特殊函数, #1

  • function*, #1

  • 垃圾回收, #1

  • 生成器,异步生成器, #1

  • 生成器,同步生成器, #1

  • 属性获取器(对象字面量), #1

  • global, #1

  • 全局对象, #1

  • 全局作用域, #1

  • 全局变量, #1

  • globalThis, #1

  • GMT(格林威治标准时间), #1

  • 图形群组, #1

  • 格林威治标准时间(GMT), #1

  • 十六进制整数字面量, #1

  • 变量提升, #1

  • 数组中的空位,#1

  • 标识符,#1

  • 对象的标识,#1

  • if,#1

  • 立即调用的函数表达式(IIFE),#1

  • 立即调用的函数表达式(IIFE),#1

  • import,#1

  • 导入,命名,#1

  • 导入,命名空间,#1

  • import.meta,#1

  • import.meta.url,#1

  • import(),#1

  • 导入,动态,#1

  • in,#1

  • 增量运算符(前缀),#1

  • 增量运算符(后缀),#1

  • 数组的索引,#1

  • Infinity,#1

  • 继承,多重,#1

  • 继承,单一,#1

  • instanceof,#1,#2

  • Int8Array,#1

  • Int16Array,#1

  • Int32Array,#1

  • 整数数字,#1

  • 整数,安全,#1

  • 内部迭代,#1

  • 可迭代(异步),#1

  • 可迭代(同步),#1

  • 迭代,异步,#1

  • 迭代,外部,#1

  • 迭代,内部,#1

  • 迭代,同步,#1

  • 迭代器(异步),#1

  • 迭代器(同步),#1

  • Iterator.prototype.drop(),#1

  • Iterator.prototype.every(),#1

  • Iterator.prototype.filter(),#1

  • Iterator.prototype.find(),#1

  • Iterator.prototype.flatMap(),#1

  • Iterator.prototype.forEach(),#1

  • Iterator.prototype.map(),#1

  • Iterator.prototype.reduce(),#1

  • Iterator.prototype.some(),#1

  • Iterator.prototype.take(),#1

  • Iterator.prototype.toArray(),#1

  • JSON (数据格式),#1

  • JSON (命名空间对象),#1

  • 蛇形命名法,#1,#2

  • 关键字,#1

  • 标签,#1

  • 领先代理,#1

  • 左移运算符,#1

  • let,#1

  • 词法 this,#1

  • 列出属性,#1

  • 小端序,#1

  • 逻辑与,#1

  • 逻辑非,#1

  • 逻辑或,#1

  • 独立代理,#1

  • Map,#1

  • Map 与对象,#1

  • new Map(),#1

  • Map.groupBy(),#1

  • Map.prototype.clear(),#1

  • Map.prototype.delete(),#1

  • Map.prototype.entries(),#1

  • Map.prototype.forEach(),#1

  • Map.prototype.get(),#1

  • Map.prototype.has(),#1

  • Map.prototype.keys(),#1

  • Map.prototype.set(),#1

  • get Map.prototype.size,#1

  • Map.prototype.values(),#1

  • Map.prototype[Symbol.iterator](),#1

  • Math (命名空间对象),#1

  • Math.abs(),#1

  • Math.acos(),#1

  • Math.acosh(),#1

  • Math.asin(),#1

  • Math.asinh(), #1

  • Math.atan(), #1

  • Math.atan2(), #1

  • Math.atanh(), #1

  • Math.cbrt(), #1

  • Math.ceil(), #1

  • Math.clz32(), #1

  • Math.cos(), #1

  • Math.cosh(), #1

  • Math.E(), #1

  • Math.exp(), #1

  • Math.expm1(), #1

  • Math.f16round(), #1

  • Math.floor(), #1

  • Math.fround(), #1

  • Math.hypot(), #1

  • Math.LN2(), #1

  • Math.LN10(), #1

  • Math.log(), #1

  • Math.log1p(), #1

  • Math.log2(), #1

  • Math.LOG2E(), #1

  • Math.log10(), #1

  • Math.LOG10E(), #1

  • Math.max(), #1

  • Math.min(), #1

  • Math.PI(), #1

  • Math.pow(), #1

  • Math.random(), #1

  • Math.round(), #1

  • Math.sign(), #1

  • Math.sin(), #1

  • Math.sinh(), #1

  • Math.sqrt(), #1

  • Math.SQRT1_2(), #1

  • Math.SQRT2(), #1

  • Math.tan(), #1

  • Math.tanh(), #1

  • Math.trunc(), #1

  • 方法, #1

  • 方法(对象字面量),#1

  • 方法(普通函数的作用),#1

  • 方法调用,直接,#1

  • 方法调用,分派,#1

  • 方法,提取 a,#1

  • 压缩,#1

  • 压缩器, #1

  • 减号运算符(二元),#1

  • 减号运算符(一元), #1

  • 混合类, #1

  • 模块指定符,#1

  • 模块,AMD,#1

  • 模块,CommonJS,#1

  • 多维数组, #1

  • 多重继承, #1

  • 多重返回值, #1

  • 乘法, #1

  • 命名导出, #1

  • 命名函数表达式, #1

  • 命名导入, #1

  • 命名参数, #1

  • 命名空间导入, #1

  • NaN, #1

  • node_modules, #1

  • 非破坏性操作, #1

  • npm, #1

  • npm 包, #1

  • null, #1

  • null 原型对象, #1

  • 空值合并赋值运算符 (??=), #1

  • 空值合并运算符 (??), #1

  • 数字, #1

  • Number.EPSILON(), #1

  • Number.isFinite(), #1

  • Number.isInteger(), #1

  • Number.isNaN(), #1

  • Number.isSafeInteger(), #1

  • Number.MAX_SAFE_INTEGER(), #1

  • Number.MAX_VALUE(), #1

  • Number.MIN_SAFE_INTEGER(), #1

  • Number.MIN_VALUE(), #1

  • Number.NaN(), #1

  • Number.NEGATIVE_INFINITY(), #1

  • Number.parseFloat(), #1

  • Number.parseInt(), #1

  • Number.POSITIVE_INFINITY(), #1

  • Number.prototype.toExponential(), #1

  • Number.prototype.toFixed(), #1

  • Number.prototype.toPrecision(), #1

  • Number.prototype.toString(), #1

  • Number(), #1

  • 对象, #1

  • 对象字面量, #1

  • 对象与 Map 的比较, #1

  • 对象与原始值,#1

  • 对象解构,#1

  • 对象,字典,#1

  • 对象,固定布局,#1

  • 对象,一个的身份,#1

  • 对象,null 原型,#1

  • Object.assign(), #1

  • Object.create(), #1

  • Object.defineProperties(), #1

  • Object.defineProperty(), #1

  • Object.entries(), #1

  • Object.freeze(), #1

  • Object.fromEntries(), #1

  • Object.getOwnPropertyDescriptor(), #1

  • Object.getOwnPropertyDescriptors(), #1

  • Object.getOwnPropertyNames(), #1

  • Object.getOwnPropertySymbols(), #1

  • Object.getPrototypeOf(), #1

  • Object.groupBy(), #1

  • Object.hasOwn(), #1

  • Object.is(), #1, #2

  • Object.isExtensible(), #1

  • Object.isFrozen(), #1

  • Object.isSealed(), #1

  • Object.keys(), #1

  • Object.preventExtensions(), #1

  • Object.prototype 方法,#1

  • Object.prototype.__proto__, #1

  • get Object.prototype.__proto__, #1

  • set Object.prototype.__proto__, #1

  • Object.prototype.hasOwnProperty(), #1, #2

  • Object.prototype.isPrototypeOf(), #1, #2

  • Object.prototype.propertyIsEnumerable(), #1, #2

  • Object.prototype.toLocaleString(), #1, #2

  • Object.prototype.toString(),#1, #2

  • Object.prototype.valueOf(),#1, #2

  • Object.seal(),#1

  • Object.setPrototypeOf(),#1

  • Object.values(),#1

  • Object(),#1

  • 八进制整型字面量,#1

  • 一的补码,#1

  • 运算符,赋值,#1

  • 运算符,逗号,#1

  • 运算符,默认值 (??),#1

  • 运算符,相等,#1

  • 运算符,空值合并 (??),#1

  • 运算符,空值合并赋值 (??=),#1

  • 运算符,void,#1

  • 可选链 (?.),#1

  • 普通函数,#1

  • 普通函数,一个的作用,#1

  • 覆盖属性,#1

  • 包,#1

  • 包管理器,#1

  • 包,npm,#1

  • package.json,#1

  • 参数,#1

  • 参数默认值,#1

  • 参数与参数,#1

  • 通过身份传递,#1

  • 通过值传递,#1

  • 模式(正则表达式),#1

  • 挂起(Promise 状态),#1

  • 加号运算符(二元),#1

  • 加号运算符(一元),#1

  • polyfill,#1

  • polyfill,推测性,#1

  • ponyfill,#1

  • 原始值,#1

  • 原始值与对象,#1

  • 原始值,转换为,#1

  • 私有名称,编号 1

  • 私有插槽,编号 1

  • 渐进式 Web 应用(PWA),编号 1

  • Prollyfill,编号 1

  • Promise,编号 1,编号 2

  • Promise 组合函数,编号 1

  • new Promise(),编号 1

  • Promise,状态,编号 1

  • Promise.all(),编号 1,编号 2

  • Promise.allSettled(),编号 1,编号 2

  • Promise.any(),编号 1,编号 2

  • Promise.prototype.catch(),编号 1

  • Promise.prototype.finally(),编号 1

  • Promise.prototype.then(),编号 1

  • Promise.race(),编号 1,编号 2

  • Promise.reject(),编号 1

  • Promise.resolve(),编号 1

  • Promise.try(),编号 1

  • Promise.withResolvers(),编号 1

  • 属性,编号 1

  • 属性,编号 1

  • 属性(对象),编号 1

  • 属性属性,编号 1

  • 属性描述符,编号 1

  • 属性键,编号 1

  • 属性键,计算,编号 1

  • 属性键,引用,编号 1

  • 属性名称,编号 1

  • 属性符号,编号 1

  • 属性值简写,编号 1

  • 属性,删除,编号 1

  • __proto__,编号 1

  • 原型,编号 1

  • 原型链,编号 1

  • 公共插槽,编号 1

  • 公共已知符号,编号 1

  • PWA(渐进式 Web 应用),编号 1

  • 引用属性键,编号 1

  • 真实函数(普通函数的角色), #1

  • 接收者, #1

  • Reflect.apply(), #1

  • Reflect.construct(), #1

  • Reflect.defineProperty(), #1

  • Reflect.deleteProperty(), #1

  • Reflect.get(), #1

  • Reflect.getOwnPropertyDescriptor(), #1

  • Reflect.getPrototypeOf(), #1

  • Reflect.has(), #1

  • Reflect.isExtensible(), #1

  • Reflect.ownKeys(), #1

  • Reflect.preventExtensions(), #1

  • Reflect.set(), #1

  • Reflect.setPrototypeOf(), #1

  • RegExp, #1

  • RegExp.prototype.exec(), #1

  • RegExp.prototype.test(), #1

  • 正则表达式, #1

  • 正则表达式字面量, #1

  • 正则表达式字符串字面量, #1

  • 被拒绝(Promise 状态), #1

  • 余数运算符, #1

  • REPL, #1

  • 复制品, #1

  • RequireJS, #1

  • 保留字, #1

  • 剩余元素(数组解构), #1

  • 剩余参数(函数调用), #1

  • 剩余属性(对象解构), #1

  • 返回值,多个, #1

  • 揭示模块模式, #1

  • 普通函数的角色, #1

  • 完成运行语义, #1

  • 安全整数, #1

  • 变量的作用域, #1

  • 脚本, #1

  • self, #1

  • 序列数组, #1

  • Set,#1,#2

  • new Set(), #1

  • Set.prototype.add(), #1

  • Set.prototype.clear(), #1

  • Set.prototype.delete(), #1

  • Set.prototype.difference(), #1

  • Set.prototype.entries(), #1

  • Set.prototype.forEach(), #1

  • Set.prototype.has(), #1

  • Set.prototype.intersection(), #1

  • Set.prototype.isDisjointFrom(), #1

  • Set.prototype.isSubsetOf(), #1

  • Set.prototype.isSupersetOf(), #1

  • Set.prototype.keys(), #1

  • get Set.prototype.size, #1

  • Set.prototype.symmetricDifference(), #1

  • Set.prototype.union(), #1

  • Set.prototype.values(), #1

  • Set.prototype[Symbol.iterator](), #1

  • 设置器(对象字面量),#1

  • 已解决(Promise 状态),#1

  • 遮蔽,#1

  • 模拟器,#1

  • 短路,#1

  • 符号右移运算符,#1

  • 单继承,#1

  • 松散模式,#1

  • 插槽,私有,#1

  • 插槽,公共,#1

  • 蛇形命名法,#1,#2

  • 稀疏数组,#1

  • 专用函数,#1

  • 指定符,模块,#1

  • 投机式 polyfill, #1

  • 将扩展(...)应用到函数调用中,#1

  • 将值扩展到数组字面量中,#1

  • 将值扩展到对象字面量中,#1

  • 堆栈跟踪,#1

  • 语句,#1

  • Promise 的状态, #1

  • static, #1

  • 静态与动态, #1

  • 严格模式, #1

  • 字符串, #1

  • 字符串字面量,正则表达式, #1

  • String.prototype.at(), #1

  • String.prototype.concat(), #1

  • String.prototype.endsWith(), #1

  • String.prototype.includes(), #1

  • String.prototype.indexOf(), #1

  • String.prototype.isWellFormed(), #1

  • String.prototype.lastIndexOf(), #1

  • String.prototype.match(), #1

  • String.prototype.matchAll(), #1

  • String.prototype.normalize(), #1

  • String.prototype.padEnd(), #1

  • String.prototype.padStart(), #1

  • String.prototype.repeat(), #1

  • String.prototype.replace(), #1

  • String.prototype.replaceAll(), #1

  • String.prototype.search(), #1

  • String.prototype.slice(), #1

  • String.prototype.split(), #1

  • String.prototype.startsWith(), #1

  • String.prototype.substring(), #1

  • String.prototype.toLowerCase(), #1

  • String.prototype.toUpperCase(), #1

  • String.prototype.toWellFormed(), #1

  • String.prototype.trim(), #1

  • String.prototype.trimEnd(), #1

  • String.prototype.trimStart(), #1

  • String.raw, #1

  • structuredClone(), #1

  • 子类, #1

  • 子类化, #1

  • 减法, #1

  • 父类, #1

  • 代理, 领先, #1

  • 代理, 单独, #1

  • 代理, 后缀, #1

  • switch, #1

  • 符号, #1

  • 符号, 公共知名, #1

  • 同步发电机, #1

  • 同步可迭代对象, #1

  • 同步迭代, #1

  • 同步迭代器, #1

  • 语法, #1

  • 标签模板, #1

  • 任务队列, #1

  • 任务运行器, #1

  • TC39, #1

  • TC39 流程, #1

  • TDZ (时间死区), #1

  • 技术委员会 39, #1

  • 模板字面量, #1

  • 时间死区, #1

  • 三元运算符, #1

  • this, #1

  • this, 动态, #1

  • this, 词法, #1

  • this, 值, #1

  • throw, #1

  • 时间值, #1

  • 乘法运算符, #1

  • 幂运算符, #1

  • ToPrimitive, #1

  • 数组字面量中的尾逗号, #1

  • 对象字面量中的尾逗号, #1

  • 参数列表中的尾逗号, #1

  • 尾部代理, #1

  • 转译, #1

  • 转译器, #1

  • 树摇, #1

  • true, #1

  • 真值性, #1

  • 真值, #1

  • try, #1

  • 类型, #1

  • 类型层次, #1

  • 类型签名, #1

  • 类型化数组, #1

  • TypedArray.from(), #1

  • TypedArray.of(), #1

  • get TypedArray.prototype.buffer, #1

  • get TypedArray.prototype.byteLength, #1

  • get TypedArray.prototype.byteOffset, #1

  • get TypedArray.prototype.length, #1

  • TypedArray.prototype.set(), #1

  • TypedArray.prototype.subarray(), #1

  • typeof, #1

  • TypeScript, #1

  • Uint8Array, #1

  • Uint8ClampedArray, #1

  • Uint16Array, #1

  • Uint32Array, #1

  • undefined, #1

  • 下划线命名法, #1, #2

  • Unicode, #1

  • Unicode 代码点转义, #1

  • Unicode 代码单元转义, #1, #2

  • Unicode 标量, #1, #2

  • Unicode Transformation Format (UTF), #1

  • 单元测试, #1

  • 无符号右移操作符, #1

  • UTC (协调世界时), #1

  • UTF (Unicode Transformation Format), #1

  • UTF-8, #1

  • UTF-16, #1

  • UTF-32, #1

  • 值保留, #1

  • 变量,绑定, #1

  • 变量,自由, #1

  • 变量,作用域, #1

  • void 操作符, #1

  • Wasm (WebAssembly), #1

  • WeakMap, #1, #2

  • WeakSet, #1, #2

  • Web Worker, #1

  • WebAssembly, #1

  • while, #1

  • window, #1

  • 包装类(用于原始值),#1

  • 包装对象(用于原始值),#1

  • yield(异步生成器),#1

  • yield(同步生成器),#1

  • yield*(异步生成器),#1

  • yield*(同步生成器),#1

  • Z (Zulu Time Zone), #1

  • Zulu Time Zone (Z), #1

posted @ 2025-12-12 18:01  绝不原创的飞龙  阅读(2)  评论(0)    收藏  举报