13、杂项


sidebarDepth: 2 tags:

  • js

现代的 javaScript

1、简介

  • JavaScript 最开始是专门为浏览器设计的一门语言,但是现在也被用于很多其他的环境。

  • JavaScript 作为被应用最广泛的浏览器语言,且与 HTML/CSS 完全集成,具有独特的地位。

  • 有很多其他的语言可以被“编译”成 JavaScript,这些语言还提供了更多的功能。

规范

  • ECMA-262 规范 包含了大部分深入的、详细的、规范化的关于 JavaScript 的信息。这份规范明确地定义了这门语言。

  • 每年都会发布一个新版本的规范。 新的规范草案请见open in new window

手册

兼容性表

2、JavaScript 基础知识

  • 2.1 Hello, world!

    提示

    本文章是关于 JavaScript 的,但是为了演示效果,会用到一些浏览器的命令,如:alert

    “script” 标签

    我们几乎可以使用 script 标签将 JavaScript 程序插入到 HTML 文档的任何位置。

    Demo

    也可 F12 查看 控制台

    <!DOCTYPE HTML>
    <html>
      <body>
        <p>script 标签之前...</p>
    
        <script>
          alert('Hello, world!');
          console.log('你好,VuePress!')
        </script>
    
        <p>...script 标签之后</p>
      </body>
    </html>
    





     
     
     





    你可以通过点击“运行”按钮来运行这个例子。

    script 标签中包裹了 JavaScript 代码,当浏览器遇到 script 标签,代码会自动运行。

    外部脚本

    如果有大量的 JavaScript 代码,我们可以将它放入一个单独的文件。

    脚本文件可以通过 src 特性(attribute)添加到 HTML 文件中。

    <script src="/path/to/script.js"></script>
    

    这里,/path/to/script.js 是脚本文件从网站根目录开始的绝对路径。当然也可以提供当前页面的相对路径。例如,src ="script.js",就像 src="./script.js",表示当前文件夹中的 "script.js" 文件。

    我们也可以提供一个完整的 URL 地址,例如:

    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
    

    要附加多个脚本,请使用多个标签:

    <script src="/js/script1.js"></script>
    <script src="/js/script2.js"></script>

    Tips:

    一般来说,只有最简单的脚本才嵌入到 HTML 中。更复杂的脚本存放在单独的文件中。

    使用独立文件的好处是浏览器会下载它,然后将它保存到浏览器的 缓存 中。

    之后,其他页面想要相同的脚本就会从缓存中获取,而不是下载它。所以文件实际上只会下载一次。

    这可以节省流量,并使得页面(加载)更快。

    Tips:如果设置了 src 特性,script 标签内容将会被忽略。

    一个单独的 script 标签不能同时有 src 特性和内部包裹的代码。

    这将不会工作:

    <script src="file.js">alert(1); </script>
    // 此内容会被忽略,因为设定了 src
    

    我们必须进行选择,要么使用外部的 script src="…",要么使用正常包裹代码的 script。

    为了让上面的例子工作,我们可以将它分成两个 script 标签。

    <script src="file.js"></script>
    <script>
      alert(1);
    </script>
    
  • 2.2 代码结构

    语句

    语句是执行行为(action)的语法结构和命令。

    我们已经见过了 alert('Hello, world!') 这样可以用来显示消息的语句。

    我们可以在代码中编写任意数量的语句。语句之间可以使用分号进行分割。

    例如,我们将 “Hello World” 这条信息一分为二:

    Demo

    也可 F12 查看 控制台

    alert('Hello');alert('World!')
    

    通常,每条语句独占一行,以提高代码的可读性:

    alert('Hello');
    alert('World!');
    

    分号

    当存在换行符(line break)时,在大多数情况下可以省略分号。

    下面的代码也是可以运行的:

    alert('Hello')
    alert('World!')
    

    在这,JavaScript 将换行符理解成“隐式”的分号。这也被称为 自动分号插入。

    在大多数情况下,换行意味着一个分号。但是“大多数情况”并不意味着“总是”!

    有很多换行并不是分号的例子,例如:

    Demo

    也可 F12 查看 控制台

    alert(3 +
    1 +
    2);
    

    代码输出 6,因为 JavaScript 并没有在这里插入分号。显而易见的是,如果一行以加号 "+" 结尾,那么这是一个“不完整的表达式”,不需要分号。所以,这个例子得到了预期的结果。

    但存在 JavaScript 无法确定是否真的需要自动插入分号的情况。

    这种情况下发生的错误是很难被找到和解决的。

    Tips:一个错误的例子

    正常的例子

    Demo

    也可 F12 查看 控制台

    alert("Hello");
    [1, 2].forEach(alert);
    

    我们必须进行选择,要么使用外部的 script src="…",要么使用正常包裹代码的 script。

    为了让上面的例子工作,我们可以将它分成两个 script 标签。

    <script src="file.js"></script>
    <script>
      alert(1);
    </script>
    

    :::

  • 2.3 现代模式,"use strict"

  • 2.4 变量

  • 2.5 数据类型

  • 2.6 交互:alert、prompt 和 confirm

  • 2.7 类型转换

  • 2.8 基础运算符,数学运算

  • 2.9 值的比较

  • 2.10 条件分支:if 和 '?'

  • 2.11 逻辑运算符

  • 2.12 空值合并运算符 '??'

  • 2.13 循环:while 和 for

  • 2.14 "switch" 语句

  • 2.15 函数

  • 2.16 函数表达式

  • 2.17 箭头函数,基础知识

  • 2.18 JavaScript 特性

3、Object(对象):基础知识

4、数据类型

5、函数进阶内容

6、对象属性配置

7、原型,继承

8、类

9、错误处理

10、Promise,async/await

11、Generator,高级 iteration

12、模块

13、杂项

数据类型

JavaScript 中有八种基本的数据类型(译注:前七种为基本数据类型,也称为原始数据类型,而 object 为复杂数据类型)。

七种原始数据类型:

  • number 用于任何类型的数字:整数或浮点数,在 ±(253-1) 范围内的整数。
  • bigint 用于任意长度的整数。
  • string 用于字符串:一个字符串可以包含 0 个或多个字符,所以没有单独的单字符类型。
  • boolean 用于 true 和 false。
  • null 用于未知的值 —— 只有一个 null 值的独立类型。
  • undefined 用于未定义的值 —— 只有一个 undefined 值的独立类型。
  • symbol 用于唯一的标识符。

以及一种非原始数据类型:

  • object 用于更复杂的数据结构。 我们可以通过 typeof 运算符查看存储在变量中的数据类型。

通常用作 typeof x,但 typeof(x) 也可行。

以字符串的形式返回类型名称,例如 "string"。

typeof null 会返回 "object" —— 这是 JavaScript 编程语言的一个错误,实际上它并不是一个 object。

类型转换

有三种常用的类型转换:转换为 string 类型、转换为 number 类型和转换为 boolean 类型。

字符串转换

转换发生在输出内容的时候,也可以通过 String(value) 进行显式转换。原始类型值的 string 类型转换通常是很明显的。

数字型转换

转换发生在进行算术操作时,也可以通过 Number(value) 进行显式转换。

数字型转换遵循以下规则:

变成……
undefinedNaN
null0
true / false1 / 0
string“按原样读取”字符串,两端的空白字符(空格、换行符 \n、制表符 \t 等)会被忽略。空字符串变成 0。转换出错则输出 NaN。

布尔型转换

转换发生在进行逻辑操作时,也可以通过 Boolean(value) 进行显式转换。

布尔型转换遵循以下规则:

变成……
0, null, undefined, NaN, ""false
其他值true

上述的大多数规则都容易理解和记忆。人们通常会犯错误的值得注意的例子有以下几个:

  • 对 undefined 进行数字型转换时,输出结果为 NaN,而非 0。

  • 对 "0" 和只有空格的字符串(比如:" ")进行布尔型转换时,输出结果为 true。

值的比较

在 JavaScript 中,它们的编写方式如下:

  • 大于 / 小于:a > b,a < b。

  • 大于等于 / 小于等于:a >= b,a <= b。

  • 检查两个值的相等:a == b,请注意双等号 == 表示相等性检查,而单等号 a = b 表示赋值。

  • 检查两个值不相等:不相等在数学中的符号是 ≠,但在 JavaScript 中写成 a != b。

总结

  • 比较运算符始终返回布尔值。

  • 字符串的比较,会按照“词典”顺序逐字符地比较大小。

  • 当对不同类型的值进行比较时,它们会先被转化为数字(不包括严格相等检查)再进行比较。

  • 在非严格相等 == 下,null 和 undefined 相等且各自不等于任何其他的值。

  • 在使用 > 或 < 进行比较时,需要注意变量可能为 null/undefined 的情况。比较好的方法是单独检查变量是否等于 null/undefined。

空值合并运算符 '??'

  • 空值合并运算符 ?? 提供了一种从列表中选择第一个“已定义的”值的简便方式。

  • 它被用于为变量分配默认值:

    // 当 height 的值为 null 或 undefined 时,将 height 的值设置为 100 height = height ?? 100;

  • ?? 运算符的优先级非常低,仅略高于 ? 和 =,因此在表达式中使用它时请考虑添加括号。

  • 如果没有明确添加括号,不能将其与 || 或 && 一起使用。

与 || 比较

或运算符 || 可以以与 ?? 运算符相同的方式使用。

例如,在上面的代码中,我们可以用 || 替换掉 ??,也可以获得相同的结果:

let firstName = null
let lastName = null
let nickName = 'Supercoder'

// 显示第一个已定义的值:
alert(firstName ?? lastName ?? nickName ?? '匿名') // Supercoder

// 显示第一个真值:
alert(firstName || lastName || nickName || 'Anonymous') // Supercoder

它们之间重要的区别是:

  • || 返回第一个 真 值。

  • ?? 返回第一个 已定义的 值。

换句话说,|| 无法区分 false、0、空字符串 "" 和 null/undefined。它们都一样 —— 假值(falsy values)。如果其中任何一个是 || 的第一个参数,那么我们将得到第二个参数作为结果。

不过在实际中,我们可能只想在变量的值为 null/undefined 时使用默认值。也就是说,当该值确实未知或未被设置时。

例如,考虑下面这种情况:

let height = 0;

alert(height || 100); // 100

alert(height ?? 100); // 0

height || 100 首先会检查 height 是否为一个假值,它是 0,确实是假值。
所以,|| 运算的结果为第二个参数,100。
height ?? 100 首先会检查 height 是否为 null/undefined,发现它不是。
所以,结果为 height 的原始值,0。
实际上,高度 0 通常是一个有效值,它不应该被替换为默认值。所以 ?? 运算得到的是正确的结果。

Object ---- 可选链 "?."

可选链 ?. 是一种访问嵌套对象属性的安全的方式。即使中间的属性不存在,也不会出现错误。

  • 如果可选链 ?. 前面的值为 undefined 或者 null,它会停止运算并返回 undefined。

例:

let user = {} // 一个没有 "address" 属性的 user 对象

alert(user.address.street) // Error!

alert(user?.address?.street) // undefined(不报错)

alert(user?.address) // undefined

alert(user?.address.street) // undefined

其它变体:?.(),?.[]

可选链 ?. 不是一个运算符,而是一个特殊的语法结构。它还可以与函数和方括号一起使用。

例如,将 ?.() 用于调用一个可能不存在的函数。

在下面这段代码中,有些用户具有 admin 方法,而有些没有:

let userAdmin = {
  admin() {
    alert('I am admin')
  },
}

let userGuest = {}

userAdmin.admin?.() // I am admin

userGuest.admin?.() // 啥都没发生(没有这样的方法)

此外,我们还可以将 ?. 跟 delete 一起使用:

delete user?.name // 如果 user 存在,则删除 user.name

我们可以使用 ?. 来安全地读取或删除,但不能写入

  • 可选链 ?. 不能用在赋值语句的左侧。

例如:

let user = null;

user?.name = "John"; // Error,不起作用
// 因为它在计算的是:undefined = "John"

总结

可选链 ?. 语法有三种形式:

obj?.prop —— 如果 obj 存在则返回 obj.prop,否则返回 undefined。
obj?.[prop] —— 如果 obj 存在则返回 obj[prop],否则返回 undefined。
obj.method?.() —— 如果 obj.method 存在则调用 obj.method(),否则返回 undefined

正如我们所看到的,这些语法形式用起来都很简单直接。?. 检查左边部分是否为 null/undefined,如果不是则继续运算。

?. 链使我们能够安全地访问嵌套属性。

但是,我们应该谨慎地使用 ?.,根据我们的代码逻辑,仅在当左侧部分不存在也可接受的情况下使用为宜。以保证在代码中有编程上的错误出现时,也不会对我们隐藏。

数据类型

要写有很多零的数字:

  • 将 "e" 和 0 的数量附加到数字后。就像:123e6 与 123 后面接 6 个 0 相同。
  • "e" 后面的负数将使数字除以 1 后面接着给定数量的零的数字。例如 123e-6 表示 0.000123(123 的百万分之一)。

对于不同的数字系统:

  • 可以直接在十六进制(0x),八进制(0o)和二进制(0b)系统中写入数字。
  • parseInt(str, base) 将字符串 str 解析为在给定的 base 数字系统中的整数,2 ≤ base ≤ 36。
  • num.toString(base) 将数字转换为在给定的 base 数字系统中的字符串。

对于常规数字检测:

  • isNaN(value) 将其参数转换为数字,然后检测它是否为 NaN
  • isFinite(value) 将其参数转换为数字,如果它是常规数字,则返回 true,而不是 NaN/Infinity/-Infinity

要将 12pt 和 100px 之类的值转换为数字:

  • 使用 parseInt/parseFloat 进行“软”转换,它从字符串中读取数字,然后返回在发生 error 前可以读取到的值。

小数:

  • 使用 Math.floor,Math.ceil,Math.trunc,Math.round 或 num.toFixed(precision) 进行舍入。
  • 请确保记住使用小数时会损失精度。

字符串

字符串的内部格式始终是 UTF-16,它不依赖于页面编码。

字符串可以包含在单引号、双引号或反引号中:

let single = 'single-quoted'
let double = 'double-quoted'

let backticks = `backticks` // 反引号允许我们通过 ${…} 将任何表达式嵌入到字符串中(包含函数)

function sum(a, b) {
  return a + b
}

alert(`1 + 2 = ${sum(1, 2)}.`) // 1 + 2 = 3.

字符串长度

length 属性表示字符串长度:

alert( `My\n`.length ); // 3
注意 \n 是一个单独的“特殊”字符,所以长度确实是 3
  • length 是一个属性

  • 请注意 str.length 是一个数字属性,而不是函数。后面不需要添加括号。

访问字符

要获取在 pos 位置的一个字符,可以使用方括号 [pos] 或者调用 str.charAt(pos) 方法。第一个字符从零位置开始:

let str = `Hello`

// 第一个字符
alert(str[0]) // H
alert(str.charAt(0)) // H

// 最后一个字符
alert(str[str.length - 1]) // o
  • 它们之间的唯一区别是,如果没有找到字符,[] 返回 undefined,而 charAt 返回一个空字符串:
let str = `Hello`

alert(str[1000]) // undefined
alert(str.charAt(1000)) // ''(空字符串)

我们也可以使用 for..of 遍历字符:

for (let char of 'Hello') {
  alert(char) // H,e,l,l,o(char 变为 "H",然后是 "e",然后是 "l" 等)
}

字符串是不可变的

在 JavaScript 中,字符串不可更改。改变字符是不可能的。

let str = 'Hi'

str[0] = 'h' // error

alert(str[0]) // 无法运行

通常的解决方法是创建一个新的字符串,并将其分配给 str 而不是以前的字符串。

例如:

let str = 'Hi'

str = 'h' + str[1] // 替换字符串

alert(str) // hi

改变大小写

toLowerCase() 和 toUpperCase() 方法可以改变大小写:

alert( 'Interface'.toUpperCase() ); // INTERFACE
alert( 'Interface'.toLowerCase() ); // interface

或者我们想要使一个字符变成小写:
alert( 'Interface'[0].toLowerCase() ); // 'i'

查找子字符串

在字符串中查找子字符串有很多种方法。

  • str.indexOf

第一个方法是 str.indexOf(substr, pos)。

它从给定位置 pos 开始,在 str 中查找 substr,如果没有找到,则返回 -1,否则返回匹配成功的位置。

例如:

let str = 'Widget with id'

alert(str.indexOf('Widget')) // 0,因为 'Widget' 一开始就被找到
alert(str.indexOf('widget')) // -1,没有找到,检索是大小写敏感的

alert(str.indexOf('id')) // 1,"id" 在位置 1 处(……idget 和 id)

可选的第二个参数允许我们从一个给定的位置开始检索。

例如,"id" 第一次出现的位置是 1。查询下一个存在位置时,我们从 2 开始检索:

let str = 'Widget with id'

alert(str.indexOf('id', 2)) // 12

如果我们对所有存在位置都感兴趣,可以在一个循环中使用 indexOf。每一次新的调用都发生在上一匹配位置之后:

let str = 'As sly as a fox, as strong as an ox'

let target = 'as' // 这是我们要查找的目标

let pos = 0
while (true) {
  let foundPos = str.indexOf(target, pos)
  if (foundPos == -1) break

  alert(`Found at ${foundPos}`)
  pos = foundPos + 1 // 继续从下一个位置查找
}

相同的算法可以简写:

let str = 'As sly as a fox, as strong as an ox'
let target = 'as'

let pos = -1
while ((pos = str.indexOf(target, pos + 1)) != -1) {
  alert(pos)
}
  • str.lastIndexOf(substr, pos)

还有一个类似的方法 str.lastIndexOf(substr, position),它从字符串的末尾开始搜索到开头。

它会以相反的顺序列出这些事件。

在 if 测试中 indexOf 有一点不方便。我们不能像这样把它放在 if 中:

let str = "Widget with id";

if (str.indexOf("Widget")) {
  alert("We found it"); // 不工作!
}
上述示例中的 alert 不会显示,因为 str.indexOf("Widget") 返回 0
(意思是它在起始位置就查找到了匹配项)。是的,但是 if 认为 0 表示 false

因此我们应该检查 -1,像这样:

let str = 'Widget with id'

if (str.indexOf('Widget') != -1) {
  alert('We found it') // 现在工作了!
}

现在我们只会在旧的代码中看到这个技巧,因为现代 JavaScript 提供了 .includes 方法(见下文)。

  • includes,startsWith,endsWith

更现代的方法 str.includes(substr, pos) 根据 str 中是否包含 substr 来返回 true/false。

如果我们需要检测匹配,但不需要它的位置,那么这是正确的选择:

alert('Widget with id'.includes('Widget')) // true
alert('Hello'.includes('Bye')) // false

str.includes 的第二个可选参数是开始搜索的起始位置:

alert('Widget'.includes('id')) // true
alert('Widget'.includes('id', 3)) // false, 从位置 3 开始没有 "id"

方法 str.startsWith 和 str.endsWith 的功能与其名称所表示的意思相同:

alert('Widget'.startsWith('Wid')) // true,"Widget" 以 "Wid" 开始
alert('Widget'.endsWith('get')) // true,"Widget" 以 "get" 结束

获取子字符串

JavaScript 中有三种获取字符串的方法:substring、substr 和 slice。

  • str.slice(start [, end])

  • str.substring(start [, end])

  • str.substr(start [, length])

我们回顾一下这些方法,以免混淆:

方法选择方式……负值参数
slice(start, end)从 start 到 end(不含 end)允许
substring(start, end)从 start 到 end(不含 end)负值被视为 0
substr(start, length)从 start 开始获取长为 length 的字符串允许 start 为负数

总结

  • 有 3 种类型的引号。反引号允许字符串跨越多行并可以使用 ${…} 在字符串中嵌入表达式。

  • JavaScript 中的字符串使用的是 UTF-16 编码。

  • 我们可以使用像 \n 这样的特殊字符或通过使用 \u... 来操作它们的 Unicode 进行字符插入。

  • 获取字符时,使用 []。

  • 获取子字符串,使用 slice 或 substring。

  • 字符串的大/小写转换,使用:toLowerCase/toUpperCase。

  • 查找子字符串时,使用 indexOf 或 includes/startsWith/endsWith 进行简单检查。

  • 根据语言比较字符串时使用 localeCompare,否则将按字符代码进行比较。

  • 还有其他几种有用的字符串方法:

  • str.trim() —— 删除字符串前后的空格 (“trims”)。

  • str.repeat(n) —— 重复字符串 n 次。

数组

获取最后一个元素

  • arr.at(i)

    let fruits = ['Apple', 'Orange', 'Plum']
    
    alert(fruits[fruits.length - 1]) // Plum 有点麻烦,不是吗?我们需要写两次变量名。
    
    alert(fruits.at(-1)) // Plum
    

换句话说,arr.at(i):

如果 i >= 0,则与 arr[i] 完全相同。

对于 i 为负数的情况,它则从数组的尾部向前数。

pop/push, shift/unshift 方法

方法作用返回值是否改变原数组性能
arr.push()在末端添加一个元素数组长度改变
arr.pop()从末端取出一个元素返回移除的元素改变
arr.shift()取出数组的第一个元素并返回它取出的元素改变
arr.unshift()在数组的首端添加元素数组长度改变

循环

  • for 循环

    let arr = ['Apple', 'Orange', 'Pear']
    
    for (let i = 0; i < arr.length; i++) {
      alert(arr[i])
    }
    
  • for..of

    let fruits = ['Apple', 'Orange', 'Plum']
    
    // 遍历数组元素
    for (let fruit of fruits) {
      alert(fruit)
    }
    

for..of 不能获取当前元素的索引,只是获取元素值,

  • for..in

    — 永远不要用这个(比上面慢 10-100 倍)

    let arr = ['Apple', 'Orange', 'Pear']
    
    for (let key in arr) {
      alert(arr[key]) // Apple, Orange, Pear
    }
    

toString

数组有自己的 toString 方法的实现,会返回以逗号隔开的元素列表。

let arr = [123, [5, 6], 7]
console.log(String(arr)) // 123,5,6,7

数组方法备忘单:

  • 添加/删除元素:

    • push(...items) —— 向尾端添加元素,
    • pop() —— 从尾端提取一个元素,
    • shift() —— 从首端提取一个元素,
    • unshift(...items) —— 向首端添加元素,
    • splice(pos, deleteCount, ...items) —— 从 pos 开始删除 deleteCount 个元素,并插入 items。
    • slice(start, end) —— 创建一个新数组,将从索引 start 到索引 end(但不包括 end)的元素复制进去。
    • concat(...items) —— 返回一个新数组:复制当前数组的所有元素,并向其中添加 items。如果 items 中的任意一项是一个数组,那么就取其元素。
  • 搜索元素:

    • indexOf/lastIndexOf(item, pos) —— 从索引 pos 开始搜索 item,搜索到则返回该项的索引,否则返回 -1。
    • includes(value) —— 如果数组有 value,则返回 true,否则返回 false。
    • find/filter(func) —— 通过 func 过滤元素,返回使 func 返回 true 的第一个值/所有值。
    • findIndex 和 find 类似,但返回索引而不是值。
  • 遍历元素:

    • forEach(func) —— 对每个元素都调用 func,不返回任何内容。
  • 转换数组:

    • map(func) —— 根据对每个元素调用 func 的结果创建一个新数组。
    • sort(func) —— 对数组进行原位(in-place)排序,然后返回它。
    • reverse() —— 原位(in-place)反转数组,然后返回它。
    • split/join —— 将字符串转换为数组并返回。
    • reduce/reduceRight(func, initial) —— 通过对每个元素调用 func 计算数组上的单个值,并在调用之间传递中间结果。
  • 其他:

    • Array.isArray(value) 检查 value 是否是一个数组,如果是则返回 true,否则返回 false。

请注意,sort,reverse 和 splice 方法修改的是数组本身。

  • arr.some(fn)/arr.every(fn) 检查数组。

    与 map 类似,对数组的每个元素调用函数 fn。如果任何/所有结果为 true,则返回 true,否则返回 false。

这两个方法的行为类似于 || 和 && 运算符:如果 fn 返回一个真值,arr.some() 立即返回 true 并停止迭代其余数组项;如果 fn 返回一个假值,arr.every() 立即返回 false 并停止对其余数组项的迭代。

  • 我们可以使用 every 来比较数组:
function arraysEqual(arr1, arr2) {
  return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]);
}

alert( arraysEqual([1, 2], [1, 2])); // true
arr.fill(value, start, end) —— 从索引 start 到 end,用重复的 value 填充数组。

arr.copyWithin(target, start, end) —— 将从位置 start 到 end 的所有元素复制到 自身 的 target 位置(覆盖现有元素)。

arr.flat(depth)/arr.flatMap(fn) 从多维数组创建一个新的扁平数组。

Array.of(element0[, element1[,[, elementN]]]) 基于可变数量的参数创建一个新的 Array 实例,而不需要考虑参数的数量或类型。

上次更新:
作者: 洛埋名