ES6
2015年版的ECMAScript规范,现在是一个标准(ECMAScript 2015)。
查看更多相关内容
Promise 和 async/await 和 Callback 有什么区别?
Promise、async/await 和 Callback 都是在 JavaScript 中处理异步操作的机制。每种技术都有其特点和适用场景。
### Callback
Callback 是一种较老的异步编程技术,它是将一个函数作为参数传递给另一个函数,并在那个函数执行完毕后调用。它最常见的用途是在进行文件操作或者请求网络资源时。
**优点:**
- 简单易懂,易于实现。
**缺点:**
- 容易导致 "回调地狱"(Callback Hell),即多个嵌套的回调函数使代码难以阅读和维护。
- 错误处理不方便,需要在每个回调中处理错误。
**例子:**
```javascript
fs.readFile('example.txt', 'utf8', function(err, data) {
if (err) {
return console.error(err);
}
console.log(data);
});
```
### Promise
Promise 是异步编程的一种解决方案,比传统的解决方案 —— 回调函数和事件 —— 更合理和更强大。它表示一个尚未完成但预期将来会完成的操作的结果。
**优点:**
- 提供了更好的错误处理机制,通过 `.then()` 和 `.catch()` 方法链。
- 避免了回调地狱,代码更加清晰和易于维护。
- 支持并行执行异步操作。
**缺点:**
- 代码仍然有些冗长。
- 可能不够直观,特别是对于新手。
**例子:**
```javascript
const promise = new Promise((resolve, reject) => {
fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
promise.then(data => {
console.log(data);
}).catch(err => {
console.error(err);
});
```
### async/await
`async/await` 是建立在 Promise 上的语法糖,它允许我们以更同步的方式写异步代码。
**优点:**
- 代码更加简洁、直观。
- 更容易理解,特别是对于习惯了同步代码的开发者。
- 方便的错误处理,可以用传统的 `try/catch` 块。
**缺点:**
- 可能会导致性能问题,因为 `await` 会暂停函数的执行,直到 Promise 解决。
- 在某些情况下不够灵活,例如并行处理多个异步任务。
**例子:**
```javascript
async function readFileAsync() {
try {
const data = await fs.promises.readFile('example.txt', 'utf8');
console.log(data);
} catch (err) {
console.error(err);
}
}
readFileAsync();
```
总结来说,Callback 是最基本的异步处理形式,Promise 提供了更强大的控制能力和错误处理机制,而 async/await 是在 Promise 基础上提高代码可读性和减少样板代码的语法糖。
前端 · 6月24日 16:43
let 块作用域是怎么实现的?
`let` 关键字在JavaScript中被引入是为了提供块作用域(block scope)的功能。块作用域意味着由 `let` 声明的变量仅在声明它们的代码块内部是可见的。代码块是被花括号 `{}` 包围的一段代码,例如在 `if` 语句、`for` 和 `while` 循环以及函数定义中都会用到代码块。
在ES6之前,JavaScript主要依赖的是函数作用域(function scope),由 `var` 关键字声明的变量要么是全局的,要么是在函数内部局部的。这种设计有时会导致意料之外的问题,特别是在循环中。
下面是一个使用 `let` 的例子来说明块作用域是如何工作的:
```javascript
function runLoop() {
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 100 * i);
}
}
runLoop();
```
在这个例子中,变量 `i` 是用 `let` 在 `for` 循环的块中声明的。这意味着每次循环迭代时,变量 `i` 都是一个新的变量,并且它被限制在这个循环的块作用域中。所以当 `setTimeout` 的回调函数执行时,它能够访问到循环迭代时对应的 `i` 的值。
如果我们用 `var` 替换掉 `let`,结果将会不同:
```javascript
function runLoop() {
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 100 * i);
}
}
runLoop();
```
在这个例子中,由于 `var` 声明的变量 `i` 是函数作用域的,当 `setTimeout` 的回调函数执行时,它会打印出循环结束后变量 `i` 的最终值,即5,会打印五次5,而不是0到4。
总结来说,`let` 关键字允许开发者在更细粒度的级别控制变量的作用域。这样做提高了代码的可读性和可维护性,并且减少了由于作用域导致的常见错误。
前端 · 6月24日 16:43
some、every、find、filter、map、forEach 有什么区别?
JavaScript 中的 `some`、`every`、`find`、`filter`、`map` 和 `forEach` 都是数组的方法,它们各自有不同的用途。
### some
`some` 方法用于检查数组中是否至少有一个元素满足提供的测试函数。如果满足则返回 `true`,否则返回 `false`。这个方法对于检查数组是否包含至少一个符合条件的元素很有用。
**例子**:
```javascript
const hasNegativeNumbers = [1, 2, 3, -1, 4].some(num => num < 0); // true
```
### every
`every` 方法用来检查数组中的所有元素是否都满足提供的测试函数。如果全部满足则返回 `true`,否则返回 `false`。这个方法适用于验证数组所有元素是否符合某个条件。
**例子**:
```javascript
const allPositiveNumbers = [1, 2, 3].every(num => num > 0); // true
```
### find
`find` 方法用于找到数组中第一个满足提供的测试函数的元素。如果找到了这样的元素,`find` 就会返回这个元素,否则返回 `undefined`。
**例子**:
```javascript
const firstNegativeNumber = [1, 2, 3, -1, 4].find(num => num < 0); // -1
```
### filter
`filter` 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。这个方法用于根据条件筛选数组中的元素。
**例子**:
```javascript
const negativeNumbers = [1, 2, 3, -1, -2, 4].filter(num => num < 0); // [-1, -2]
```
### map
`map` 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后的返回值。这个方法用于转换数组中的每个元素。
**例子**:
```javascript
const squares = [1, 2, 3, 4].map(num => num * num); // [1, 4, 9, 16]
```
### forEach
`forEach` 方法对数组的每个元素执行一次提供的函数,但它不返回任何值(即 `undefined`)。这只是一个简单的遍历数组的办法,通常用于执行副作用(如打印日志、更新UI等)。
**例子**:
```javascript
[1, 2, 3, 4].forEach(num => console.log(num)); // 输出 1 2 3 4,但没有返回值
```
每一个这些方法都有其特定的用途,选择哪个取决于您要解决的特定问题。
前端 · 6月24日 16:43
ES5 和 ES6 有什么区别
ES5(即ECMAScript 5)和ES6(也称为ECMAScript 2015或ECMAScript 6)是JavaScript语言的两个版本,它们之间有许多重要的区别。ES6引入了一系列新特性和语法改进,使得编程更加简洁和强大。以下是一些主要的区别:
### 1. let 和 const
ES6引入了 `let`和 `const`关键字,用于声明变量。`let`提供了块级作用域,比ES5中的 `var`提供了更好的控制,尤其是在循环中。`const`用于声明常量,其值在设置之后不能改变。
**例子:**
在一个循环中使用 `let`可以避免常见的闭包问题。
```javascript
for (let i = 0; i < 5; i++) {
setTimeout(function() { console.log(i); }, 1000);
}
// 输出:0, 1, 2, 3, 4 (正确的顺序)
```
### 2. 箭头函数(Arrow Functions)
ES6引入了箭头函数,这是一种更简洁的函数写法,同时它也没有自己的 `this`,`arguments`,`super`或 `new.target`绑定。
**例子:**
```javascript
// ES5
var add = function(a, b) {
return a + b;
};
// ES6
const add = (a, b) => a + b;
```
### 3. 模板字符串(Template Literals)
在ES6中,模板字符串提供了一种构建字符串的新方法,可以使用反引号(` ` `)来定义,它支持多行字符串和字符串插值。
**例子:**
```javascript
// ES5
var name = "World";
var greeting = "Hello, " + name + "!";
// ES6
const name = "World";
const greeting = `Hello, ${name}!`;
```
### 4. 类(Classes)
ES6引入了类的概念,这是一种使用原型继承的语法糖。它使创建对象和继承更加直观和方便。
**例子:**
```javascript
// ES6
class Person {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, ${this.name}!`;
}
}
const person = new Person('Jane');
console.log(person.greet()); // "Hello, Jane!"
```
### 5. 默认参数值
ES6允许函数参数有默认值,这在ES5中通常需要在函数体内部进行处理。
**例子:**
```javascript
// ES6
function greet(name = "World") {
return `Hello, ${name}!`;
}
console.log(greet()); // "Hello, World!"
console.log(greet("Jane")); // "Hello, Jane!"
```
### 6. 解构赋值(Destructuring Assignment)
ES6引入了解构赋值,它允许在单个语句中从数组或对象中提取数据,并设置到新的变量中。
**例子:**
```javascript
// ES6
const [a, b] = [1, 2];
const { firstName, lastName } = { firstName: "John", lastName: "Doe" };
console.log(a); // 1
console.log(firstName); // "John"
```
### 7. 模块导入和导出
ES6标准化了模块系统,使用 `import`和 `export`语句来导入和导出模块成员。
**例子:**
```javascript
// ES6
// file: math.js
export const add = (a, b) => a + b;
// file: main.js
import { add } from './math';
console.log(add(2, 3)); // 5
```
### 8. Promises 和异步编程
ES6引入了Promise作为处理异步操作的一种机制,它比ES5中的回调函数更具可读性和效率。
前端 · 6月24日 16:43
ES6 中的 Map 和原生的对象有什么区别?
在 ES6 中,`Map` 是一种新的数据结构,它提供了一些原生对象(如普通的 JavaScript 对象)所不具备的特性。以下是 `Map` 和原生对象之间一些主要的区别:
1. **键的类型**:
- **Map**:可以使用任何类型的值(包括对象或原始值)作为键。
- **对象**:通常只能使用字符串或者 `Symbol` 作为键。虽然现代JavaScript引擎会自动将非字符串的键转换为字符串,但这可能导致键的冲突和预期之外的行为。
2. **键的顺序**:
- **Map**:键值对是有序的,`Map` 对象遍历时会根据元素的插入顺序进行。
- **对象**:在 ES2015 之前,对象的属性没有特定的顺序;但从 ES2015 开始,对象的属性遍历顺序是根据属性被添加到对象的顺序(对于字符串键)和整数键的大小来确定的,非整数键则按照创建顺序排列。
3. **大小可获取**:
- **Map**:可以直接获取到 `Map` 的大小,使用 `map.size` 属性。
- **对象**:通常需要手动计算属性的数量,例如通过 `Object.keys(obj).length`。
4. **性能**:
- **Map**:在频繁添加和删除键值对的场景下,`Map` 通常提供更优的性能。特别是当涉及到大量键值对时,`Map` 的性能通常更稳定。
- **对象**:当作为少量属性的集合时,原生对象也可能表现出良好的性能。
5. **默认键**:
- **Map**:不包含默认键,只包含显式插入的键。
- **对象**:原型链上的属性和方法可以被继承,对象默认会含有诸如 `toString` 或 `hasOwnProperty` 这样的方法,这可能会在某些使用场景中造成问题。
6. **迭代**:
- **Map**:`Map` 对象可以直接被迭代,提供了几个迭代方法,包括 `map.keys()`、`map.values()` 和 `map.entries()`,以及 `map.forEach()` 方法。
- **对象**:对象的属性需要使用 `for...in` 循环或 `Object.keys()`、`Object.values()`、`Object.entries()` 加上 `forEach` 方法等进行迭代。
7. **序列化**:
- **Map**:`Map` 对象不能直接使用 `JSON.stringify` 进行序列化。
- **对象**:对象可以直接被序列化为 JSON 字符串。
例如,如果我们需要一个键值对集合来记录用户的唯一标识符(这些标识符可能是数字、字符串、甚至是对象),并且希望保持插入顺序,那么 `Map` 就特别适合这种用例。使用 `Map` 我们可以这样实现:
```javascript
let userRoles = new Map();
let user1 = { name: "Alice" };
let user2 = { name: "Bob" };
// 添加用户角色
userRoles.set(user1, 'admin');
userRoles.set(user2, 'editor');
// 获取Map的大小
console.log(userRoles.size); // 2
// 按插入顺序遍历用户角色
for (let [user, role] of userRoles.entries()) {
console.log(`${user.name}: ${role}`);
}
```
在这个例子中,我们使用对象 `user1` 和 `user2` 作为键,这在普通的对象中是无法做到的,因为对象的键会被转换为字符串。
前端 · 6月24日 16:43
es6 类继承中 super 的作用
ES6中,`super`关键字在类继承中扮演着非常重要的角色。它有两个主要的作用:
1. **在子类构造函数中调用父类的构造函数**:在使用ES6类继承时,子类的构造函数需要调用父类的构造函数,这是通过`super()`实现的。这使得子类能够继承父类的属性。如果不调用`super()`,则子类的实例将无法正确构建,因为父类的一些初始化代码不会被执行。
例如,假设我们有一个`Person`类和一个继承自`Person`的`Student`类:
```javascript
class Person {
constructor(name) {
this.name = name;
}
}
class Student extends Person {
constructor(name, studentID) {
super(name); // 调用父类的构造函数来初始化父类中定义的属性
this.studentID = studentID;
}
}
let student = new Student('Alice', '12345');
console.log(student.name); // 输出: Alice
```
在这个例子中,`super(name)`调用了`Person`类的构造函数,初始化了`name`属性。
2. **在子类的方法中调用父类的方法**:`super`也可以用来在子类中调用父类的方法。这对于扩展和重写父类行为非常有用。在子类的方法中,你可以通过`super.methodName()`的方式调用父类的方法。
例如:
```javascript
class Person {
greet() {
console.log(`Hello, my name is ${this.name}.`);
}
}
class Student extends Person {
study() {
console.log(`${this.name} is studying with student ID ${this.studentID}.`);
}
greet() {
super.greet(); // 调用父类的greet方法
this.study(); // 然后调用子类的study方法
}
}
let student = new Student('Alice', '12345');
student.greet();
// 输出:
// Hello, my name is Alice.
// Alice is studying with student ID 12345.
```
在这个例子中,我们重写了`Student`类的`greet`方法,在其中首先通过`super.greet()`调用了父类`Person`中的`greet`方法,然后调用了`Student`自己的`study`方法,这样就可以保留父类的行为的同时扩展新的行为。
综上所述,`super`在ES6类继承中是极为重要的,它允许子类构造函数和方法访问和调用父类的构造函数和方法。
前端 · 6月24日 16:43
ES6 中有哪些解决异步的方法?
在ES6(ECMAScript 2015)及之后的版本中,引入了多种解决异步编程问题的方法。这些方法提高了代码的可读性、易维护性,并使得异步逻辑的处理变得更加直观。下面是几种主要的异步处理方法:
### 1. Promises
ES6正式引入了`Promise`对象,这是处理异步操作的一种方法。一个`Promise`代表了一个异步操作的最终完成 (或失败) 及其结果值。`Promise`有三种状态:`pending`(等待中), `fulfilled`(已成功), 和 `rejected`(已失败)。
**示例:**
```javascript
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('数据已获取');
}, 2000);
});
myPromise.then(
(value) => { console.log(value); }, // 成功处理函数
(error) => { console.error(error); } // 失败处理函数
);
```
### 2. Generators
虽然ES6的生成器(`Generator`)函数本身不是异步的,但它们可以用于控制异步调用的流程。生成器函数允许函数执行过程中暂停和恢复,这意味着可以在某个操作等待异步结果时“暂停”函数执行。
**示例:**
```javascript
function* generatorFunction() {
const result = yield myPromise;
console.log(result);
}
// 使用生成器控制异步流程
const iterator = generatorFunction();
const prom = iterator.next().value; // 获取由yield语句返回的Promise
prom.then((response) => iterator.next(response));
```
### 3. Async/Await
`async/await`是在ES7(ECMAScript 2017)中引入的,但它是基于ES6的`Promise`进一步发展的。这是一个通过更简洁的方式使用`Promise`的语法糖。任何一个标记为`async`的函数都会返回一个`Promise`。`await`关键字可以用来等待`Promise`的解决,并暂停函数的执行,直到`Promise`被解决或拒绝。
**示例:**
```javascript
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data'); // 等待Promise解决
const data = await response.json(); // 等待Promise解决
console.log(data);
} catch (error) {
console.error('请求失败:', error);
}
}
fetchData();
```
每一种方法都有它的用例和适用场景。`Promise`可以很好地解决单个或多个异步操作链的情况。当我们需要在异步操作中暂停和恢复函数执行时,`Generator`函数可以很好地帮助我们管理复杂的流程。而`async/await`提供了一种更加直观和简洁的方式去处理`Promise`,特别是在需要等待多个异步操作完成时,代码的可读性和可维护性大大提高。
前端 · 6月24日 16:43
weak-Set、weak-Map 和 Set、Map 之间的区别是什么?
WeakSet 和 WeakMap 是 JavaScript 中的集合类型,与 Set 和 Map 相似,但它们之间有一些重要的区别。以下分别是 WeakSet/WeakMap 与 Set/Map 之间的主要区别:
### WeakSet 与 Set 的区别:
1. **弱引用:**
- **WeakSet**:只能包含对象的弱引用。这意味着如果没有其他引用指向对象,这些对象是可以被垃圾回收机制回收的。
- **Set**:可以包含任意值的强引用,包括原始值或对象引用。只要 Set 存在,其中的元素就不会被垃圾回收机制回收。
2. **元素类型限制:**
- **WeakSet**:只能存储对象,不能存储原始数据类型(如字符串、数字、布尔值等)。
- **Set**:可以存储任意类型的值,无论是原始数据类型还是对象。
3. **可枚举性:**
- **WeakSet**:不能被迭代,也没有提供方法来获取大小(即没有 size 属性)或者清空集合的方法。
- **Set**:可迭代,且有 size 属性可以获取集合的大小,也提供了 clear 方法来清空集合。
4. **使用场景:**
- **WeakSet**:适合用于存储没有任何其他引用的对象集合,通常用于管理对象的生命周期,防止内存泄漏。
- **Set**:适合于需要存储唯一值的场景,特别是当需要迭代或者获取集合大小时。
### WeakMap 与 Map 的区别:
1. **弱引用键:**
- **WeakMap**:只接受对象作为键,并且这些键是弱引用的。如果没有其他引用指向键对象,那么这些键值对可以被垃圾回收机制回收。
- **Map**:可以接受任意类型的值作为键,包括原始数据类型和对象,这些键是强引用的。
2. **键类型限制:**
- **WeakMap**:键必须是对象,不能是原始数据类型。
- **Map**:键可以是任意类型的值,包括原始数据类型和对象。
3. **可枚举性:**
- **WeakMap**:同样不能被迭代,没有 size 属性,也不能清空整个集合。
- **Map**:可迭代,有 size 属性,并提供了 clear 方法。
4. **使用场景:**
- **WeakMap**:经常用于缓存或者存储对象与数据的关联,同时不影响对象的垃圾回收。
- **Map**:适用于需要明确地将键映射到值,并且需要键的枚举、大小统计或者清空映射。
### 示例:
假设我们正在开发一个应用程序,该应用程序需要跟踪一组 DOM 元素是否被点击。我们可以使用 WeakSet 来存储这些 DOM 元素,如下所示:
```javascript
let clickedElements = new WeakSet();
document.addEventListener('click', event => {
if (event.target.tagName === 'BUTTON') {
clickedElements.add(event.target);
// ...执行一些操作...
}
});
// 由于 WeakSet 对象的特性,当 DOM 元素被移除并且没有其他引用时,它将自动从 WeakSet 中移除,防止内存泄漏
```
在这个例子中,如果没有 WeakSet,而是使用 Set,那么即使 DOM 元素被移除,它们也不会从集合中删除,这可能会导致内存泄漏。
前端 · 6月24日 16:43
var、let、const 之间的区别是什么?
### var
`var` 是 JavaScript 早期版本中使用的变量声明关键字,它有几个特点:
- **函数作用域**:`var` 声明的变量是按照函数作用域进行绑定的,如果在函数外部声明,它就具有全局作用域。
- **变量提升(Hoisting)**:使用 `var` 声明的变量会被提升至其作用域的顶部,但是只提升声明不提升初始化。
- **重复声明**:用 `var` 声明的变量可以在同一作用域中被重新声明。
```javascript
console.log(foo); // 输出 undefined 而不是抛出错误,因为变量提升
var foo = 5;
function testVar() {
var bar = "hello";
}
console.log(bar); // 抛出错误,因为 bar 是在函数内部声明的,外部无法访问
var foo = "world"; // 这是允许的,foo 被重新声明
```
### let
`let` 是 ES6 (ECMAScript 2015) 引入的关键字,用于声明变量,并且它带来了几个改进:
- **块作用域**:`let` 声明的变量是按照块作用域(如 `{}` 内部)进行绑定的。
- **没有变量提升**:`let` 声明的变量不会提升,它们必须在声明之后才能被使用。
- **不能重复声明**:在同一作用域下不能重新声明同一个变量。
```javascript
console.log(foo); // 抛出错误,foo 没有被提升
let foo = 5;
if (true) {
let bar = "hello";
}
console.log(bar); // 抛出错误,bar 在 if 语句的块作用域外无法访问
let foo = "world"; // 抛出错误,foo 不能被重新声明
```
### const
`const` 同样是在 ES6 引入的,用于声明常量,具有以下特性:
- **块作用域**:与 `let` 相同,`const` 声明的变量也是块作用域。
- **没有变量提升**:同样,`const` 声明的变量在声明之前不能被访问。
- **不能重复声明**:不能在相同作用域下重新声明。
- **必须初始化**:使用 `const` 声明变量时必须立即初始化,并且之后不能修改。
```javascript
const foo = 5;
foo = 10; // 抛出错误,因为 const 声明的变量不能被重新赋值
if (true) {
const bar = "hello";
}
console.log(bar); // 抛出错误,因为 bar 在 if 语句的块作用域外无法访问
const foo; // 抛出错误,因为 const 声明的变量必须在声明时初始化
```
总结来说,`let` 和 `const` 是对 `var` 的一个改进,提供了块作用域特性,并解决了变量提升和重复声明所带来的问题。在现代 JavaScript 编程中,推荐使用 `let` 和 `const` 来声明变量,以便代码更加清晰和安全。
前端 · 6月24日 16:43
module.exports和exports的区别是什么?export和export default的区别是什么?
### module.exports vs exports
在 Node.js 中,`module.exports` 和 `exports` 都是用于导出模块中的变量或者函数,以便其他文件可以使用 `require` 方法来引入和使用。但是它们之间存在一些区别:
1. **module.exports**:
- 这是真正用于定义模块导出的对象。在模块中,可以通过对 `module.exports` 赋值来指定导出的内容。
- 如果你需要导出单个值或者一个完整的对象,通常会使用 `module.exports`。
- 例子:假设你有一个工具模块,想要导出一个类。
```javascript
class Tool {
// ...
}
module.exports = Tool;
```
2. **exports**:
- `exports` 是 `module.exports` 的一个引用,Node.js 默认在每个模块的头部创建了 `exports = module.exports`。
- 它通常用于导出多个对象或函数。
- 但是,如果你给 `exports` 直接赋一个新值,它就不再指向 `module.exports`,这就可能导致模块导出一个空对象 `{}`。
- 例子:假设你有一个工具模块,想要导出多个函数。
```javascript
exports.function1 = function() {
// ...
};
exports.function2 = function() {
// ...
};
```
如果设置了 `module.exports`,`exports` 对象会被忽略。因此,不应该同时使用 `module.exports` 和 `exports` 导出不同的东西,以避免混淆或错误。
### export vs export default
在 ES6 模块系统中,`export` 和 `export default` 用于导出模块内容,但它们的用途和语法有所不同:
1. **export**:
- 用于导出一个或多个命名的变量、函数、类等。
- 导入时需要使用花括号 `{}` 并指定相应的名称。
- 可以在一个模块中使用多个 `export`。
- 例子:导出多个功能。
```javascript
export const CONSTANT = 'constant value';
export function myFunction() {
// ...
}
```
2. **export default**:
- 用于导出一个模块的默认输出。
- 导入时不需要使用花括号,可以给导入的内容任意命名。
- 一个模块只能有一个 `export default`。
- 例子:导出一个模块的主要功能或类。
```javascript
export default class MyClass {
// ...
}
```
- 另一个例子是,在一个模块中即使用 `export` 导出多个值,也可以指定一个默认导出。
```javascript
export const util1 = () => { /* ... */ };
export const util2 = () => { /* ... */ };
export default () => { /* ... */ }; // 这是默认导出
```
在使用时,`import myDefaultImport from 'my-module'` 会导入 `export default` 的值,而 `import { namedImport } from 'my-module'` 会导入通过 `export` 命名导出的值。
总结来说,`module.exports` 和 `exports` 用于 Node.js 的 CommonJS 模块系统,而 `export` 和 `export default` 用于 ES6 模块系统。选择哪一个取决于你的使用环境和特定的需求。
前端 · 6月24日 16:43