Skip to content

ES6 ​

作者:青见春山
发表于:2026-07-24
字数统计:10802 字
预计阅读37分钟

ECMAScript 6 新特性举例:let/const、解构赋值、字符串扩展、正则扩展、数值扩展、函数扩展、数组扩展、对象扩展等。

1.简介 ​

  1. 弥补 ES5 的不足,比如变量提升、内置对象的方法不灵活等问题
  2. 为框架做准备,适用性广
  3. Babel 是 JavaScript 编译工具,可转换配置范围内的新语法。语法转换不等于补齐所有新 API;Promise、Map 等运行时能力仍可能需要 polyfill。

2.let 和 const ​

2.1 let 用于声明变量 ​

javascript
{
  let a = 10;
  var b = 20;
}
console.log(a); // a is not defined
console.log(b); // 20

var 会有变量提升:

javascript
var a; // 这个会被提升到上面
// 所以 console.log(a) 的结果是 undefined,不会报错。
var a = 2;

2.1.1 let 声明存在暂时性死区 ​

let 声明的变量绑定会在进入作用域时被创建,可以理解为声明发生了提升;但在执行到声明语句之前,变量不会被初始化,这段区域称为暂时性死区(TDZ)。在暂时性死区内访问该变量会抛出 ReferenceError。

javascript
console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 10;

2.1.2 let 是一个块级作用域 ​

javascript
if (1 === 1) {
  let b = 10;
}
console.log(b); // 会报错
// b 只在 {} 中可用

2.1.3 不可重复声明 ​

javascript
let a = 1;
let a = 3;
console.log(a); // 会报错

// 而 var a = 1; var a = 3; 会覆盖

2.2 const 声明常量 ​

const 创建不能重新赋值的绑定;如果绑定的是对象,对象内容仍然可以修改,除非另外冻结freeze它。

javascript
const max = 30;
max = 40;
console.log(max); // 会报错,max 不能再重新赋值

const 一旦声明变量,就必须立即初始化,不能留到以后赋值。注意这个是和let不同的地方!

const 与 let 一样具有块级作用域,绑定在进入作用域时已经创建,但初始化前处于暂时性死区;因此不能简单说“没有提升”。同一作用域内也不能重复声明。

javascript
const person = {
  name: "小小",
};
person.name = "alex"; // 这样修改是可以的,因为是对象的属性
person = {
  age: 20,
}; // 这样是不行的,不能修改常量对象,但是对象内部的属性可以修改

// 常量 person 储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把 person 指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。

// 又例如:
const a = [];
a.push("Hello"); // 可执行
a.length = 0; // 可执行
a = ["Dave"]; // 报错

经典案例:for 循环中的 var 和 let ​

javascript
// 经典 for 循环问题
var arr = [];
for (var i = 0; i < 10; i++) {
  arr[i] = function () {
    return i;
  };
}
console.log(arr[5]()); // 输出结果是 10
// 因为 for 循环之后 i 为 10
// 注意代码执行顺序,这里循环先执行,等执行console的时候for循环早跑完八百年了

// 修正:
const arr = [];
for (let i = 0; i < 10; i++) {
  arr[i] = function () {
    return i;
  };
}
console.log(arr[5]());
// 结果是 5

为什么输出是 10?

因为 arr[1]、arr[2] 都是 return i 的函数,不论哪个调用,都是 return i,而在 for 循环之外,i 是 10。所以最后输出是 10。

不是 arr[1] 就 return 1,arr[2] 就 return 2,只是把 return i 放在了 arr 的每个元素里面而已。【注意这句话,品鉴】

而let的情况是块级作用域,所以每次循环都会创建一个新的作用域,i 的值会被保存在这个作用域中,所以输出是 5。

作用:不会污染全局变量 ​

在浏览器中,顶层用 let 声明的变量不会变成 window 对象的属性。

javascript
let RegExp = 10;
console.log(RegExp); // 10
console.log(window.RegExp); // 一个函数正则表达式

当代码直接访问 RegExp 时,JavaScript 会优先找到当前作用域中的 let RegExp,所以输出 10。这叫作遮蔽(shadowing)。 换行而var如果像这样在顶层的话,默认会成为window的属性的。

注意:在默认情况下用 const,只有在知道变量值需要被修改时用 let

2.3 暂时性死区 ​

使用 let和const 声明变量时,只要变量在还没有声明完成前使用,就会报错.

总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。也就是说存在但不可用。

2.4 块级作用域 ​

ES5 的 var 主要有全局作用域和函数作用域,不会为普通 {} 创建块级绑定(catch 参数等语法有特殊块级行为)。

ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。
块级作用域意思就是只在当前的代码块中有效,出了这个代码块就不再有效了。

javascript
{
  let a = 10;
  var b = 20;
}
console.log(a); // ReferenceError: a is not defined
console.log(b); // 20

2.5 顶层对象的属性 ​

在浏览器主线程中全局对象可通过 window 访问,在 Node.js 中传统名称是 global,跨环境应使用 globalThis。只有经典浏览器 script 等特定宿主/脚本类型中,顶层 var/函数声明才会形成全局对象属性;Node.js 模块和 ES Module 都不能套用这条规则。

javascript
window.a = 1;
a; // 1

a = 2;
window.a; // 2

上面的双向访问现象发生在经典浏览器 script 的全局作用域中:通过 window 创建的属性可以作为全局标识符访问,给该全局标识符赋值也会更新对应的 window 属性。

在经典浏览器 script 中,为保持兼容性,顶层 var 和函数声明通常会创建全局对象属性,而顶层 let、const、class 不会。该结论不适用于 ES Module 或 Node.js 的 CommonJS 模块包装。

javascript
// 经典浏览器 script
var a = 1;
window.a; // 1,也可以写成 globalThis.a

let b = 1;
window.b; // undefined,globalThis.b 也是 undefined

在 Node.js 中还要区分代码的运行方式:

javascript
// Node REPL(Read-Eval-Print Loop,交互式命令行环境)
var a = 1;
global.a; // 1,也可以写成 globalThis.a

Node REPL 会在共享的交互上下文中执行输入,因此这里的顶层 var 会成为全局对象的属性。但在普通的 CommonJS 文件中,Node.js 会用模块函数包装文件,顶层声明属于当前模块:

javascript
// test.cjs(或被当作 CommonJS 加载的 .js 文件)
var a = 1;
global.a; // undefined,globalThis.a 也是 undefined
this.a; // undefined;CommonJS 文件顶层的 this 是 module.exports,不是全局对象

因此,不能把 Node REPL 中的结果套用到 Node.js 模块文件。this 也不是访问全局对象的通用方法;跨环境应使用 globalThis。ES Module 的顶层 this 还是 undefined。

2.7 globalThis 对象 ​

ES2020 引入了一个新的全局对象 globalThis,它是一个标准化的顶层对象引用,浏览器中是 window,Node 中是 global。

javascript
// 浏览器环境
globalThis === window; // true

// Node 环境
globalThis === global; // true

3.变量的解构赋值 ​

3.1 数组的解构赋值 ​

将数组元素(单元值)快速批量赋值给一系列变量

3.1.1 基本语法 ​

  1. 赋值运算符 = 左侧的 [] 用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量。

本质是:模式匹配

  1. 变量的顺序对应数组单元值的位置进行赋值操作
javascript
let [a, b, c] = [1, 2, 3];

3.1.2 典型应用:交换两个变量 ​

javascript
let a = 1;
let b = 2;
// 若上一条语句可能与 `[` 连续解析,应加分号;统一使用分号可避免 ASI 歧义
[b, a] = [a, b];
console.log(a, b); // 2 1

3.1.3 必须加分号的两种情况 ​

javascript
// 1. 立即执行函数
(function () {})();

// 2. 使用数组的时候
// 如果 [] 前面有代码,[] 前要加分号,不然会认为和前面的代码连在一起,加一个分号表示代码结束。

3.1.4 变量多,单元值少(变量 undefined) ​

javascript
let [a, b, c] = [1, 2];
console.log(c); // undefined

3.1.5 变量少,单元值多(利用剩余参数解决) ​

javascript
const [a, b, ...c] = [1, 2, 3, 4];
console.log(c); // [3, 4] 真数组

3.1.6 防止 undefined 传递 ​

javascript
const [a = 0, b = 0] = [1, 2];
// 不传值则结果是默认的 0, 0

注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于 undefined,默认值才会生效。

javascript
let [x = 1] = [undefined];
x; // 1

let [x = 1] = [null];
x; // null

上面代码中,如果一个数组成员是 null,默认值就不会生效,因为 null 不严格等于 undefined。

如果默认值是一个表达式,比如说函数,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。

javascript
function f() {
  console.log("aaa");
}

let [x = f()] = [1];
// 上面代码中,因为 x 能取到值,所以函数 f 根本不会执行

默认值可以引用解构赋值的其他变量,但该变量必须已经声明。

3.1.7 按需传入赋值 ​

javascript
const [a, b, , d] = [1, 2, 3, 4];
console.log(a); // 1
console.log(b); // 2
console.log(d); // 4

3.1.8 多维数组运用解构 ​

javascript
const [a, b, c] = [1, 2, [3, 4]];
// 改进:
const [a, b, [c, d]] = [1, 2, [3, 4]];
console.log(c); // 3
console.log(d); // 4

3.2 对象的解构赋值 ​

将对象属性和方法快速批量赋值给一系列变量的简介语法

3.2.1 基本语法 ​

赋值运算符 = 左侧的 {} 用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量

javascript
const obj = {
  uname: "小白",
  age: 10,
};
const { uname, age } = { uname: "小白", age: 10 };
// 等价于 const uname = obj.uname
console.log(uname);

// 对象中属性的值将被赋值给与属性名相同的变量,对象的属性没有次序要求
// 注意解构的变量名不要和外面的变量名冲突,否则报错
// 对象中找不到与变量名一致的属性时变量值为 undefined

3.2.2 对象解构的变量名可以重新改名 ​

旧变量名:新变量名

冒号表示把什么值赋值给谁

javascript
const { uname: username, age } = { uname: "小白", age: 10 };

就是变量名和属性名不一致的时候,一定要写成这样:

javascript
let { foo: baz } = { foo: "aaa", bar: "bbb" };
baz; // "aaa"

let obj = { first: "hello", last: "world" };
let { first: f, last: l } = obj;
f; // 'hello'
l; // 'world'

注意:最终被赋值的是变量

3.2.3 数组对象的解构 ​

javascript
const pig = [{ uname: "佩奇", age: 6 }];
const [{ uname, age }] = pig;
console.log(uname); // 佩奇
console.log(age); // 6

3.2.4 多级对象解构 ​

javascript
const pig = {
  name: "佩奇",
  family: {
    mother: "猪妈妈",
    father: "猪爸爸",
    brother: "乔治",
  },
  age: 6,
};
const {
  name,
  family: { mother, father, brother },
} = pig;
console.log(name); // 佩奇

前后端交互应用:

对象解构常用于接口返回数据的提取。

对象的解构也可以指定默认值:

javascript
var { x = 3 } = {};
x; // 3

var { x, y = 5 } = { x: 1 };
x; // 1
y; // 5

var { x: y = 3 } = {};
y; // 3

var { x: y = 3 } = { x: 5 };
y; // 5

var { message: msg = "Something went wrong" } = {};
msg; // "Something went wrong"

3.3 注意点 ​

3.3.1 已经声明的变量用于解构赋值 ​

javascript
// 正确的写法
let x;
({ x } = { x: 1 });

上面代码将整个解构赋值语句,放在一个圆括号里面,就可以正确执行。

3.3.2 数组本质是特殊的对象 ​

可以对数组进行对象属性的解构。

javascript
let arr = [1, 2, 3];
let { 0: first, [arr.length - 1]: last } = arr;
first; // 1
last; // 3

3.4 字符串的解构赋值 ​

javascript
const [a, b, c, d, e] = "hello";
a; // "h"
b; // "e"
c; // "l"
d; // "l"
e; // "o"

let { length: len } = "hello";
len; // 5

3.5 数值和布尔值的解构赋值 ​

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。

javascript
let { toString: s } = 123;
s === Number.prototype.toString; // true

let { toString: s } = true;
s === Boolean.prototype.toString; // true

3.6 函数参数的解构赋值 ​

javascript
function add([x, y]) {
  return x + y;
}
add([1, 2]); // 3

函数 add 的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解构成变量 x 和 y。

函数参数的解构也可以使用默认值:

javascript
function move({ x = 0, y = 0 } = {}) {
  return [x, y];
}

move({ x: 3, y: 8 }); // [3, 8]
move({ x: 3 }); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

下面的代码是为函数 move 的参数指定默认值,而不是为变量 x 和 y 指定默认值,所以会得到与前一种写法不同的结果。

javascript
function move({ x, y } = { x: 0, y: 0 }) {
  return [x, y];
}

move({ x: 3, y: 8 }); // [3, 8]
move({ x: 3 }); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]

3.7 圆括号问题 ​

变量声明语句,函数参数,赋值语句的模式不得使用圆括号。

赋值语句的非模式部分,可以使用圆括号:

javascript
[b] = [3]; // 正确
({ p: d } = {}); // 正确
[parseInt.prop] = [3]; // 正确

首先它们都是赋值语句,而不是声明语句;其次它们的圆括号都不属于模式的一部分。

4.字符串的扩展 ​

4.1 字符的 Unicode 表示法 ​

JavaScript 支持 \uXXXX 形式的 Unicode 转义,其中 XXXX 是一个 UTF-16 码元的四位十六进制值。

javascript
"a"; // "a"

传统的 \\uXXXX 转义一次表示一个 16 位 UTF-16 码元(U+0000 到 U+FFFF)。BMP 之外的码点可写成一对代理项,ES6 也可直接使用码点转义 \\u{1F680}。

4.2 字符串的遍历器接口 ​

ES6 为字符串添加了遍历器接口,使得字符串可以被 for...of 循环遍历。

javascript
for (let codePoint of "foo") {
  console.log(codePoint);
}
// 'f'
// 'o'
// 'o'

4.3 模板字符串 ​

使用 tab 键上面的反引号(`)标识,

通常用来拼接字符串和变量。

javascript
// 普通字符串
`In JavaScript '\n' is a line-feed.`
// 多行字符串
`In JavaScript this is
 not legal.`;

console.log(`string text line 1
string text line 2`);

// 字符串中嵌入变量
let name = "Bob",
  time = "today";
`Hello ${name}, how are you ${time}?`;

注意:

  • 如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
  • 如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。

插入变量时,使用 ${变量名}

javascript
function authorize(user, action) {
  if (!user.hasPrivilege(action)) {
    throw new Error(`User ${user.name} is not authorized to do ${action}.`);
  }
}

也可以这样用:

{} 内可以放任意的表达式,可以进行运算以及引用对象属性。

javascript
let x = 1;
let y = 2;

`${x} + ${y} = ${x + y}` // "1 + 2 = 3"
`${x} + ${y * 2} = ${x + y * 2}`; // "1 + 4 = 5"

let obj = { x: 1, y: 2 };
`${obj.x + obj.y}`; // "3"

{} 还可以调用函数:

javascript
function fn() {
  return "Hello World";
}

`foo ${fn()} bar`; // "foo Hello World bar"

模板字符串嵌套:

javascript
const tmpl = (addrs) => `
  <table>
  ${addrs
    .map(
      (addr) => `
    <tr><td>${addr.first}</td></tr>
    <tr><td>${addr.last}</td></tr>
  `,
    )
    .join("")}
  </table>
`;

4.4 标签模板 ​

模板字符串可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。

javascript
alert`hello`; // 等同于
alert(["hello"]);

"标签"指的就是函数,紧跟在后面的模板字符串就是它的参数。

如果模板字符串里面有变量:

javascript
let a = 5;
let b = 10;

tag`Hello ${a + b} world ${a * b}`;
// 等同于
tag(["Hello ", " world ", ""], 15, 50);

应用:过滤 HTML 字符串,防止用户输入恶意内容

javascript
let message = SaferHTML`<p>${sender} has sent you a message.</p>`;

function SaferHTML(templateData) {
  let s = templateData[0];
  for (let i = 1; i < arguments.length; i++) {
    let arg = String(arguments[i]);
    // Escape special characters in the substitution.
    s += arg.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
    s += templateData[i];
  }
  return s;
}

5.字符串的新增方法 ​

5.1 String.raw() ​

该方法返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,往往用于模板字符串的处理方法。

javascript
String.raw`Hi\\n`;
// 返回 "Hi\\\\n"

String.raw`Hi\\n` === "Hi\\\\n"; // true

本质是函数,它的第一个参数,应该是一个具有 raw 属性的对象,且 raw 属性的值应该是一个数组,对应模板字符串解析后的值。

javascript
// `foo${1 + 2}bar`
// 等同于
String.raw({ raw: ["foo", "bar"] }, 1 + 2); // "foo3bar"

5.2 includes(), startsWith(), endsWith() ​

传统上,JavaScript 只有 indexOf 方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。

  • includes():返回布尔值,表示是否找到了参数字符串。
  • startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
  • endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
javascript
let s = "Hello world!";

s.startsWith("world", 6); // true
s.endsWith("Hello", 5); // true
s.includes("Hello", 6); // false

第二个参数表示开始搜索的位置

endsWith 从后往前找

5.3 repeat() ​

返回一个新字符串,表示将原字符串重复 n 次。

javascript
"x".repeat(3); // "xxx"
"hello".repeat(2); // "hellohello"
"na".repeat(0); // ""

小数取整;负数或者 Infinity 报错;0 到 -1 的小数取为 0;NaN 等同于 0;参数是字符串,会先转换成数字。

5.4 padStart(),padEnd() ​

如果某个字符串不够指定长度,会在头部或尾部补全。padStart() 用于头部补全,padEnd() 用于尾部补全。

javascript
"x".padStart(5, "ab"); // 'ababx'
"x".padStart(4, "ab"); // 'abax'
"x".padEnd(5, "ab"); // 'xabab'
"x".padEnd(4, "ab"); // 'xaba'

5.5 trimStart(),trimEnd() ​

它们的行为与 trim() 一致,trimStart() 消除字符串头部的空格,trimEnd() 消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。

javascript
const s = "  abc  ";
s.trim(); // 'abc'
s.trimStart(); // 'abc  '
s.trimEnd(); // '  abc'

5.6 replaceAll() ​

可以一次性替换所有匹配。

当第一个参数是字符串或非全局正则时,String.prototype.replace 只替换第一个匹配;传入带 g 标志的正则时也可替换全部。replaceAll 对字符串模式直接替换全部,若传正则则要求它带 g 标志。

javascript
"aabbcc".replaceAll("b", "_"); // 'aa__cc'

6.正则的扩展 ​

6.1 RegExp 构造函数 ​

RegExp 构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。

而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。

javascript
new RegExp(/abc/gi, "i").flags;
// "i"

原有正则对象的修饰符是 ig,它会被第二个参数 i 覆盖。

6.2 y 修饰符 ​

全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始

g 修饰符只要剩余位置中存在匹配就可,而 y 修饰符确保匹配必须从剩余的第一个位置开始

javascript
var s = "aaa_aa_a";
var r1 = /a+/g;
var r2 = /a+/y;

r1.exec(s); // ["aaa"]
r2.exec(s); // ["aaa"]

r1.exec(s); // ["aa"]
r2.exec(s); // null

这两个正则表达式各执行了两次,第一次执行的时候,两者行为相同,剩余字符串都是 _aa_a。由于 g 修饰没有位置要求,所以第二次执行会返回结果,而 y 修饰符要求匹配必须从头部开始,所以返回 null。

6.3 s 修饰符:dotAll 模式 ​

使得 . 可以匹配任意单个字符

javascript
/foo.bar/s.test("foo\nbar"); // true

dotAll 模式

javascript
const re = /foo.bar/s;
// 另一种写法
// const re = new RegExp('foo.bar', 's');

re.test("foo\nbar"); // true
re.dotAll; // true
re.flags; // 's'

6.4 具名组匹配 ​

javascript
const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;

const matchObj = RE_DATE.exec("1999-12-31");
const year = matchObj.groups.year; // "1999"
const month = matchObj.groups.month; // "12"
const day = matchObj.groups.day; // "31"

"具名组匹配"在圆括号内部,模式的头部添加"问号 + 尖括号 + 组名"(?<year>),然后就可以在 exec 方法返回结果的 groups 属性上引用该组名。同时,数字序号(matchObj[1])依然有效。

具名组匹配等于为每一组匹配加上了 ID,便于描述匹配的目的。如果组的顺序变了,也不用改变匹配后的处理代码。

如果具名组没有匹配,那么对应的 groups 对象属性会是 undefined。

6.5 解构赋值和替换 ​

字符串替换时,使用 $<组名> 引用具名组。

javascript
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;

"2015-01-02".replace(re, "$<day>/$<month>/$<year>");
// '02/01/2015'

6.6 引用 ​

如果要在正则表达式内部引用某个"具名组匹配",可以使用 \k<组名> 的写法。

javascript
const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/;
RE_TWICE.test("abc!abc"); // true
RE_TWICE.test("abc!ab"); // false

6.7 d 修饰符:正则匹配索引 ​

这个修饰符可以让 exec()、match() 的返回结果添加 indices 属性,在该属性上面可以拿到匹配的开始位置和结束位置。

6.8 String.prototype.matchAll() ​

可以一次性取出所有匹配。不过,它返回的是一个遍历器(Iterator),而不是数组。

javascript
const string = "test1test2test3";
const regex = /t(e)(st(\d?))/g;

for (const match of string.matchAll(regex)) {
  console.log(match);
}
// ["test1", "e", "st1", "1", index: 0, input: 'test1test2test3']
// ["test2", "e", "st2", "2", index: 5, input: 'test1test2test3']
// ["test3", "e", "st3", "3", index: 10, input: 'test1test2test3']

7.数值的扩展 ​

7.1 数值分隔符 ​

允许 JavaScript 的数值使用下划线(_)作为分隔符。

javascript
let budget = 1_000_000_000_000;
budget === 10 ** 12; // true
  • 不能放在数值的最前面(leading)或最后面(trailing)
  • 不能两个或两个以上的分隔符连在一起
  • 小数点的前后不能有分隔符
  • 科学计数法里面,表示指数的 e 或 E 前后不能有分隔符

7.2 Number.isFinite(), Number.isNaN() ​

Number.isFinite() 用来检查一个数值是否为有限的(finite),即不是 Infinity。

Number.isNaN() 用来检查一个值是否为 NaN。

这两个新方法只对数值有效,Number.isFinite() 对于非数值一律返回 false,Number.isNaN() 只有对于 NaN 才返回 true,非 NaN 一律返回 false。

7.3 Number.parseInt(), Number.parseFloat() ​

将全局方法 parseInt() 和 parseFloat(),移植到 Number 对象上面,行为完全保持不变。

javascript
Number.parseInt("12.34"); // 12
Number.parseFloat("123.45#"); // 123.45

7.4 Number.isInteger() ​

用来判断一个数值是否为整数。

如果对数据精度的要求较高,不建议使用 Number.isInteger() 判断一个数值是否为整数。

7.5 Math 对象的扩展 ​

Math.trunc 方法用于去除一个数的小数部分,返回整数部分。

javascript
Math.trunc(4.1); // 4
Math.trunc(4.9); // 4
Math.trunc(-4.1); // -4

Math.sign 方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。

它会返回五种值:

  • 参数为正数,返回 +1
  • 参数为负数,返回 -1
  • 参数为 0,返回 0
  • 参数为 -0,返回 -0
  • 其他值,返回 NaN

Math.cbrt() 方法用于计算一个数的立方根。

Math.imul 方法返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。

Math.hypot 方法返回所有参数的平方和的平方根。

四个对数方法:

  • Math.expm1(x):返回 e^x - 1
  • Math.log1p(x):返回 ln(1 + x)
  • Math.log10(x):返回以 10 为底的 x 的对数
  • Math.log2(x):返回以 2 为底的 x 的对数

六个双曲函数方法。

7.6 BigInt 数据类型 ​

大整数

BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。

javascript
const a = 2172141653n;
const b = 15346349309n;
a * b; // 33334444555566667777n

8.函数的扩展 ​

8.1 带参数默认值的函数 ​

只传一个值,会赋值给第一个形参

具有默认值的参数,一般位置要靠后

参数变量是默认声明的,所以不能用 let 或 const 再次声明。

javascript
function foo(x = 5) {
  let x = 1; // error
  const x = 2; // error
}

8.2 与解构赋值结合使用 ​

javascript
function foo({ x, y = 5 } = {}) {
  console.log(x, y);
}

foo(); // undefined 5

上面代码指定,如果没有提供参数,函数 foo 的参数默认为一个空对象。

注意:

javascript
function m2({ x, y } = { x: 0, y: 0 }) {
  return [x, y];
}

m2({ z: 3 }); // [undefined, undefined]
// 由于传入的对象没有 x 和 y 属性,参数对象解构将使用默认对象 { x: 0, y: 0 } 中的值初始化 x 和 y。
// 然而,由于参数对象解构赋值的语法特性,如果传入的对象没有匹配的属性,解构的变量将被赋值为 undefined,即使存在默认值。
javascript
function m1({ x = 0, y = 0 } = {}) {
  return [x, y];
}
// 调用 m1({ z: 3 }) 返回 [0, 0]
javascript
function m2({ x, y } = { x: 0, y: 0 }) {
  return [x, y];
}
// 调用 m2({ z: 3 }) 返回 [undefined, undefined]

8.3 默认的表达式也可以是一个函数 ​

javascript
function getValue() {
  return 10;
}

function foo(x = getValue()) {
  console.log(x);
}

foo(); // 10

8.4 动态参数 ​

arguments 动态获取函数的实参

ES6 之前,函数内部可以通过 arguments 对象获取所有实参。

8.5 rest 剩余参数 ​

由三个点 ... 和一个紧跟着的具名参数指定(**...变量名**)。

javascript
function add(...values) {
  let sum = 0;
  for (var val of values) {
    sum += val;
  }
  return sum;
}

add(2, 5, 3); // 10

rest 参数是真数组,拥有 pop 等方法。

rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。

javascript
// 报错
function f(a, ...b, c) { ... }

函数的 length 属性,不包括 rest 参数。

8.6 函数的 length 属性 ​

指定了默认值以后,函数的 length 属性,将返回没有指定默认值的参数个数

从前往后数,如果设置了默认值的参数不是尾参数,那么 length 属性也不再计入后面的参数了。

javascript
(function (a = 0, b, c) {}).length(
  // 0
  function (a, b = 1, c) {},
).length; // 1

8.7 作用域问题 ​

javascript
var x = 1;

function f(x, y = x) {
  console.log(y);
}

f(2); // 2

上面代码中,参数 y 的默认值等于变量 x。调用函数 f 时,参数形成一个单独的作用域。在这个作用域里面,默认值变量 x 指向第一个参数 x,而不是全局变量 x,所以输出是 2。

javascript
let x = 1;

function f(y = x) {
  let x = 2;
  console.log(y);
}

f(); // 1

上面代码中,函数 f 调用时,参数 y = x 形成一个单独的作用域。这个作用域里面,变量 x 本身没有定义,所以指向外层的全局变量 x。函数调用时,函数体内部的局部变量 x 影响不到默认值变量 x。

8.8 name 属性 ​

函数的 name 属性,返回该函数的函数名。

javascript
function foo() {}
foo.name; // "foo"
javascript
var f = function () {};

// ES5
f.name; // ""

// ES6
f.name; // "f"
javascript
const bar = function baz() {};

// ES5
bar.name; // "baz"

// ES6
bar.name; // "baz"

8.9 箭头函数 ​

用 => 来定义函数

声明一个函数:

javascript
let fn = (a, b) => {
  return a + b;
};
// 调用函数
let result = fn(1, 2);
console.log(result);

箭头函数特性:

8.9.1 没有自己的 this ​

箭头函数没有自己的 this 绑定,执行时按词法环境读取外层 this。它不是在声明时绑定某个“所在对象”,因为对象字面量本身不会创建 this 作用域。

javascript
function foo() {
  setTimeout(() => {
    console.log("id:", this.id);
  }, 100);
}

var id = 21;

foo.call({ id: 42 });
// id: 42

foo.call({ id: 42 }) 执行期间创建箭头函数,箭头函数捕获本次调用中 foo 的 this,所以稍后输出 42。普通定时器回调的 this 还会因宿主而异:浏览器中通常是 window,Node.js 中通常是 Timeout 对象,跨环境代码不应依赖它。

箭头函数也没有自己的 arguments、super 和 new.target 绑定;若语法上下文允许,它们沿用外层环境的绑定。需要参数数组时优先使用剩余参数。

8.9.2 不能作为构造实例化对象 ​

javascript
let Person = (name) => {
  this.name = name;
};

let p = new Person("张三"); // 报错:Person is not a constructor

8.9.3 没有自己的 arguments 绑定 ​

javascript
function outer(...args) {
  const foo = () => console.log(args); // 显式使用剩余参数
  foo();
}

8.9.4 箭头函数的简写 ​

  • 当形参只有一个时,可以省略小括号
  • 当函数体只有一条语句时,可以省略大括号,并且 return 关键字也必须省略
  • 当函数体只有一条 return 语句时,return 关键字也必须省略
javascript
// 完整写法
let fn = (a, b) => {
  return a + b;
};

// 简写 1:省略大括号和 return(隐式返回)
let fn = (a, b) => a + b;

// 简写 2:只有一个形参时省略小括号
let fn = (a) => a;

// 简写 3:返回对象字面量需要用圆括号包起来
let fn = (id) => ({ id: id, name: "Temp" });

8.9.5 其他 ​

如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用 return 语句返回。

javascript
// 报错
let getTempItem = id => { id: id, name: "Temp" };

// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });

8.9.6 结合变量解构使用 ​

javascript
const full = ({ first, last }) => first + " " + last;

// 等同于
function full(person) {
  return person.first + " " + person.last;
}

8.9.7 结合 rest 参数 ​

javascript
const numbers = (...nums) => nums;

numbers(1, 2, 3, 4, 5);
// [1, 2, 3, 4, 5]

const headAndTail = (head, ...tail) => [head, tail];

headAndTail(1, 2, 3, 4, 5);
// [1, [2, 3, 4, 5]]

8.9.8 不适用场合 ​

  1. 定义对象的方法,且该方法内部包括 this。

对象的属性建议使用传统的写法定义,不要用箭头函数定义。

javascript
const cat = {
  lives: 9,
  jumps: () => {
    this.lives--;
  },
};

上例需要动态接收者,却把方法写成箭头函数,因此 cat.jumps() 不会把 cat 绑定为 this。经典浏览器 script 的顶层 this 常是 window,ES Module 顶层 this 则是 undefined,不能一概说成“指向全局对象”。

  1. 需要动态 this 的时候,也不应使用箭头函数。
javascript
var button = document.getElementById("press");
button.addEventListener("click", () => {
  this.classList.toggle("on");
});

箭头监听器会沿用外层 this,所以这里不能依赖 this.classList。可改用普通函数读取 this/event.currentTarget,或在箭头函数里直接引用 button。

8.10 尾调用 / 尾递归 ​

函数 f 的最后一步是调用函数 g

javascript
function f(x) {
  return g(x);
}

8.11 尾逗号 ​

javascript
function clownsEverywhere(param1, param2) {
  /* ... */
}

clownsEverywhere("foo", "bar");

不会报错。最后一个参数加逗号

8.12 Function.prototype.toString() ​

修改后的 toString() 方法,明确要求返回一模一样的原始代码。

javascript
function /* foo comment */ foo() {}

foo.toString();
// "function /* foo comment */ foo () {}"

允许 catch 语句省略参数。

javascript
try {
  // ...
} catch {
  // ...
}

9.数组的扩展 ​

9.1 扩展运算符(...) ​

将一个数组展开

javascript
const arr = [1, 5, 3, 8, 2];
console.log(...arr); // 1 5 3 8 2

注意:

  • 不会修改原数组
  • 一组数据变成三条
  • 数组转化为参数序列

扩展运算符应用:

9.1.1 数组的合并 ​

javascript
const a1 = [1, 2];
const a2 = [3, 4];
const a3 = [...a1, ...a2]; // [1, 2, 3, 4]

9.1.2 数组的克隆 ​

javascript
const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;

9.1.3 将伪数组转为真正的数组 ​

javascript
[..."hello"];
// ["h", "e", "l", "l", "o"]

9.1.4 该运算符主要用于函数调用 ​

javascript
function add(x, y) {
  return x + y;
}

const numbers = [4, 38];
add(...numbers); // 42

如果扩展运算符后面是一个空数组,则不产生任何效果。

只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错。

9.1.5 结合解构赋值 ​

javascript
const [first, ...rest] = [1, 2, 3, 4, 5];
first; // 1
rest; // [2, 3, 4, 5]

const [first, ...rest] = [];
first; // undefined
rest; // []

const [first, ...rest] = ["foo"];
first; // 'foo'
rest; // []

9.2 map 方法 ​

遍历数组处理数据,并且返回新的数组

javascript
const arr = [1, 2, 3, 4];
const arr2 = arr.map((item) => item * 2);
console.log(arr2); // [2, 4, 6, 8]

9.3 forEach 方法 ​

用于调用数组的每个元素,并将元素传递给回调函数。

遍历数组,只遍历,不返回

适合于遍历数组对象

javascript
const arr = [1, 2, 3];
arr.forEach((item) => console.log(item));

9.4 from 方法 ​

用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。

javascript
const obj = { 0: "a", 1: "b", 2: "c", length: 3 };
const arr = Array.from(obj); // ['a', 'b', 'c']

9.5 of 方法 ​

用于将一组值,转换为数组。

不存在由于参数不同而导致的重载

如果没有参数,就返回一个空数组。

javascript
Array.of(1, 2, 3); // [1, 2, 3]
Array.of(); // []

9.6 copyWithin 方法 ​

复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组长度

javascript
Array.prototype.copyWithin(target, (start = 0), (end = this.length));

它接受三个参数:

  • target(必需):从该位置开始替换数据。如果为负值,表示倒数
  • start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算
  • end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算

把从 3 位置开始的三个数字替换从 0 位置开始的三个数字:

javascript
// 将 3 号位复制到 0 号位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4);
// [4, 2, 3, 4, 5]

9.7 find 方法 ​

find() 方法的参数是一个回调函数。

所有数组成员依次执行该回调函数,直到找出第一个返回值为 true 的成员,然后返回该成员。

javascript
[1, 5, 10, 15].find(function (value, index, arr) {
  return value > 9;
}); // 10

上面代码中,find() 方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。

绑定回调函数的 this 对象

find() 方法的第二个参数可以绑定回调函数的 this 对象。

findLast() 和 findLastIndex(),从数组的最后一个成员开始,依次向前检查,其他都保持不变。

9.8 entries() keys() values() ​

用于获取数组的键值对、键名、键值。

javascript
const arr = ["a", "b", "c"];

for (let index of arr.keys()) {
  console.log(index);
}
// 0 1 2

for (let elem of arr.values()) {
  console.log(elem);
}
// a b c

for (let [index, elem] of arr.entries()) {
  console.log(index, elem);
}
// 0 a
// 1 b
// 2 c

9.9 includes() 方法 ​

该方法的第二个参数表示搜索的起始位置,默认为 0。

javascript
[1, 2, 3].includes(3, 3); // false

9.10 fill() 方法 ​

使用给定值,填充一个数组

javascript
["a", "b", "c"].fill(7);
// [7, 7, 7]

new Array(3).fill(7)[
  // [7, 7, 7]

  ("a", "b", "c")
].fill(7, 1, 2);
// ['a', 7, 'c']
// 从 1 号位开始,向原数组填充 7,到 2 号位之前结束

初始化空数组

注意:fill 方法填充的是同一个引用,而不是对象的副本。

javascript
let arr = new Array(3).fill({ name: "Mike" });
arr[0].name = "Ben";
arr;
// [{name: "Ben"}, {name: "Ben"}, {name: "Ben"}]
// 为什么数组的第二个和第三个元素的 name 值都变成了 Ben?

这是因为 fill 方法填充的是同一个引用。这意味着 arr 数组中的三个元素都指向同一个对象:

javascript
// 可视化表示,实际上它们都是同一个对象的引用
[{ name: "Mike" }, { name: "Mike" }, { name: "Mike" }];

当修改第一个元素的 name 属性时:

javascript
arr[0].name = "Ben";

实际上修改的是所有元素引用的对象的 name 属性,因为它们都是同一个对象。

9.11 flat(),flatMap() ​

Array.prototype.flat() 用于将嵌套的数组"拉平",变成一维的数组。该方法返回一个新数组,对原数据没有影响。

javascript
[1, 2, [3, 4]].flat();
// [1, 2, 3, 4]

flatMap() 方法对原数组的每个成员执行一个函数(相当于执行 Array.prototype.map()),然后对返回值组成的数组执行 flat() 方法。该方法返回一个新数组,不改变原数组。

javascript
// 相当于 [[2, 4], [3, 6], [4, 8]].flat()
[2, 3, 4].flatMap((x) => [x, x * 2]);
// [2, 4, 3, 6, 4, 8]

9.12 at() ​

接受一个整数作为参数,返回参数指定位置的字符,支持负索引(即倒数的位置)。

javascript
const str = "hello";
str.at(1); // "e"
str.at(-1); // "o"

const arr = [5, 12, 8, 130, 44];
arr.at(2); // 8
arr.at(-2); // 130

9.13 toReversed(),toSorted(),toSpliced(),with() ​

  • toReversed() 对应 reverse(),用来颠倒数组成员的位置
  • toSorted() 对应 sort(),用来对数组成员排序
  • toSpliced() 对应 splice(),用来在指定位置,删除指定数量的成员,并插入新成员
  • with(index, value) 对应 splice(index, 1, value),用来将指定位置的成员替换为新的值

这四个方法分别是相关原地修改方法的非变异替代方案,但不能概括为“唯一差别只是是否修改”:它们对稀疏数组等边界情况也有差异。共同点是返回新数组而不改写原数组。

9.14 Object.groupBy(),Map.groupBy() ​

旧提案中的 Array.prototype.group() / groupToMap() 已更名并改为静态方法。当前标准使用 Object.groupBy(iterable, callback) 和 Map.groupBy(iterable, callback)。

javascript
const array = [1, 2, 3, 4, 5];

Object.groupBy(array, (num, index) => {
  return num % 2 === 0 ? "even" : "odd";
});
// { odd: [1, 3, 5], even: [2, 4] }

Map.groupBy() 返回 Map,分组键可以保持对象身份;Object.groupBy() 返回 null-prototype 对象,其键会成为字符串或 Symbol 属性键。

按字符串或 Symbol 键分组可用 Object.groupBy();需要对象键或任意键身份时用 Map.groupBy()。

9.15 空位 ​

数组空位和显式的 undefined 并不等价。不同数组方法对空位的处理并不统一:有些跳过回调,有些在结果中将其物化为 undefined。

由于空位的处理规则非常不统一,所以建议避免出现空位。

9.16 数组 filter 方法 ​

filter() 方法创建一个新数组,其包含通过所提供函数实现的测试的所有元素。

javascript
const words = ["spray", "limit", "elite", "exuberant", "destruction", "present"];
const result = words.filter((word) => word.length > 6);
console.log(result);
// ["exuberant", "destruction", "present"]

filter 只能比较运算符,不能使用 + -

搭配箭头函数:

javascript
const numbers = [1, 2, 3, 4, 5, 6];
const even = numbers.filter((num) => num % 2 === 0);
console.log(even); // [2, 4, 6]

10.对象的扩展 ​

10.1 属性的简写 ​

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。

javascript
let name = "哈哈";
let change = function () {
  console.log("we can go");
};

const school = {
  name, // 等价于 name: name
  change,
  improve() {
    console.log("we can change");
  },
  // 这个 improve() {} 是 improve: function() {} 的简写
};

简写的对象方法不能用作构造函数,会报错

10.1.1 实际应用:调用函数返回对象 ​

javascript
function createPerson(name, age) {
  return {
    name,
    age,
  };
}

10.1.2 简写运用: ​

常用于配置对象、模块导出等场景。

10.2 属性名表达式 ​

ES6 允许字面量定义对象时,用表达式作为对象的属性名,即把表达式放在方括号内。

还可以用于定义方法名。

javascript
let propKey = "foo";

let obj = {
  [propKey]: true,
  ["a" + "bc"]: 123,
};

let obj = {
  ["h" + "ello"]() {
    return "hi";
  },
};

10.3 方法的 name 属性 ​

javascript
const person = {
  sayName() {
    console.log("我的名字");
  },
};

person.sayName.name; // "sayName"

10.4 属性的可枚举性 ​

对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor 方法可以获取该属性的描述对象。

javascript
let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, "foo");
// { value: 123, writable: true, enumerable: true, configurable: true }

ES6 规定,所有 Class 的原型的方法都是不可枚举的。

10.5 对象的扩展运算符 ​

ES2018 引入了对象的扩展运算符 ...,可以用于解构赋值和合并对象。

对象的解构赋值:

javascript
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x; // 1
y; // 2
z; // { a: 3, b: 4 }

对象的扩展运算符:

javascript
let z = { a: 3, b: 4 };
let n = { ...z };
n; // { a: 3, b: 4 }

合并两个对象:

javascript
let ab = { ...a, ...b };
// 等同于
let ab = Object.assign({}, a, b);

10.6 对象的新增方法 ​

10.6.1 Object.is() ​

ES5 比较两个值是否相等,只有两个运算符:相等运算符 == 和严格相等运算符 ===。它们都有缺点,前者会自动转换数据类型,后者的 NaN 不等于自身,以及 +0 等于 -0。

ES6 提出"Same-value equality"(同值相等)算法,用来解决这个问题。Object.is 就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

javascript
Object.is("foo", "foo"); // true
Object.is({}, {}); // false

// 不同之处:+0 不等于 -0,NaN 等于 NaN
Object.is(+0, -0); // false
Object.is(NaN, NaN); // true

10.6.2 Object.assign() ​

Object.assign 方法用于对象的合并,将源对象的所有可枚举属性,复制到目标对象。

javascript
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target; // { a: 1, b: 2, c: 3 }

注意:

  • Object.assign 方法实行的是浅拷贝
  • 同名属性会进行覆盖
  • Object.assign 拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性

10.6.3 Object.keys(),Object.values(),Object.entries() ​

ES5 引入了 Object.keys 方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。

ES2017 引入了 Object.values 和 Object.entries,作为 Object.keys 的补充。

javascript
const obj = { foo: "bar", baz: 42 };

Object.keys(obj); // ['foo', 'baz']
Object.values(obj); // ['bar', 42]
Object.entries(obj); // [['foo', 'bar'], ['baz', 42]]

10.6.4 Object.fromEntries() ​

Object.fromEntries() 方法是 Object.entries() 的逆操作,用于将一个键值对数组转为对象。

javascript
const entries = [
  ["foo", "bar"],
  ["baz", 42],
];
Object.fromEntries(entries);
// { foo: 'bar', baz: 42 }

特别适合将 Map 结构转为对象:

javascript
const map = new Map([
  ["foo", "bar"],
  ["baz", 42],
]);
Object.fromEntries(map);
// { foo: 'bar', baz: 42 }

11.Promise ​

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。

ES6 规定,Promise 对象是一个构造函数,用来生成 Promise 实例。

javascript
const promise = new Promise(function (resolve, reject) {
    // ... some code
    if (/* 异步操作成功 */) {
        resolve(value);
    } else {
        reject(error);
    }
});

Promise 实例具有 then 方法,也就是说,then 方法是定义在原型对象 Promise.prototype 上的。它的作用是为 Promise 实例添加状态改变时的回调函数。

javascript
promise.then(
  function (value) {
    // success
  },
  function (error) {
    // failure
  },
);

12.async / await ​

ES2017 标准引入了 async 函数,使得异步操作变得更加方便。

可以把 async/await 的控制流类比为 Generator 配合 Promise 执行器,但标准为 async 函数定义了独立语义,不应把它当作规范层面的简单语法替换。

javascript
async function getStockPriceByName(name) {
  const symbol = await getSymbol(name);
  const stockPrice = await getPrice(symbol);
  return stockPrice;
}

getStockPriceByName("goog").then(function (result) {
  console.log(result);
});

async 函数的返回值是 Promise 对象。

await 会把表达式结果按 Promise 语义同化,等待兑现后取得值;即使后面是普通值,async 函数的后续执行也会异步恢复。若等待的 Promise 被拒绝,await 会抛出该原因。

13.类(Class) ​

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。

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

  toString() {
    return "(" + this.x + ", " + this.y + ")";
  }
}

类的继承:

javascript
class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 调用父类的 constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + " " + super.toString(); // 调用父类的 toString()
  }
}

14.模块化(Module) ​

ES6 引入了模块化,其设计思想是尽量静态化,使编译时就能确定模块的依赖关系,以及输入和输出的变量。

export 命令:

javascript
// profile.js
export var firstName = "Michael";
export var lastName = "Jackson";
export var year = 1958;

import 命令:

javascript
// main.js
import { firstName, lastName, year } from "./profile.js";

export default 命令:

javascript
// export-default.js
export default function () {
  console.log("foo");
}

// import-default.js
import customName from "./export-default.js";
customName(); // 'foo'