ES6真题合集(二)
- 6. ES6中 Module
- 6.1 导出(Export)
- 6.2 导入(Import)
 
- 7. ES6中 Generator
- 7.1 基础用法
- 7.2 特点
- 7.3 应用场景
 
- 8. ES6中 Decorator
- 8.1 基础用法
 
- 9. ES6新增Set、Map两种数据结构
- 9.1 Set
- 9.2 Map
- 9.3 对比
- 9.4 案例
 
- 10. ES6中 Proxy
- 10.1 基础用法
- 10.2 处理器对象
 
 
6. ES6中 Module
模块(Module)是一个重要的特性,它允许开发者将代码分割成独立的、可复用的单元,每个单元都有自己的作用域。ES6模块通过import和export关键字来实现模块之间的导入和导出。
6.1 导出(Export)
可以使用export关键字来导出函数、对象、原始值或类。一个模块可以导出多个值。
// math.js  
export function add(x, y) {  return x + y;  
}  export function subtract(x, y) {  return x - y;  
}  // 或者使用花括号({})来导出多个值  
// 这种方式也允许你重命名导出的值  
function multiply(x, y) {  return x * y;  
}  export { multiply as mult };  // 还可以导出整个模块的内容  
export * from 'anotherModule';  // 导出默认值  
const pi = 3.14159;  
export default pi;
6.2 导入(Import)
在另一个模块中,你可以使用import关键字来导入已导出的函数、对象或值。
// main.js  
// 导入命名导出  
import { add, subtract } from './math.js';  console.log(add(1, 2)); // 输出 3  
console.log(subtract(2, 1)); // 输出 1  // 导入重命名的导出  
import { mult as multiply } from './math.js';  console.log(multiply(2, 3)); // 输出 6  // 导入整个模块的内容  
import * as math from './math.js';  console.log(math.add(1, 2)); // 输出 3  // 导入默认值  
import pi from './math.js';  console.log(pi); // 输出 3.14159(注意:这里默认导出的是pi,而不是整个math模块)  // 导入默认值并给它一个别名  
import myPi from './math.js';  console.log(myPi); // 输出 3.14159注意点:
1.ES6模块是静态的,这意味着在编译时(而不是运行时)就确定了导入和导出的内容。
2.ES6模块默认是严格模式(strict mode),即代码在严格模式下运行。
3.ES6模块支持循环依赖,但应该尽量避免,因为它们可能导致难以预料的结果。
4.在浏览器中,可以通过<script type="module">标签来加载ES6模块。在Node.js中,从v12开始,可以使用.mjs文件扩展名或package.json中的"type": "module"字段来启用ES6模块支持。
5.ES6模块提供了一个内置的模块作用域,这意味着模块顶层的变量和函数声明在模块内部是私有的,只有通过export才能被外部访问。
7. ES6中 Generator
Generator(生成器)是一种特殊的函数,它允许你暂停和恢复函数的执行。这主要通过function*语法和yield关键字来实现。Generator函数在执行过程中,遇到yield表达式就暂停执行,并返回一个遍历器对象。这个遍历器对象可以记录当前Generator函数的执行上下文,以便后续再次调用时从该位置继续执行。
7.1 基础用法
Generator函数的声明方式是在function关键字后添加一个星号*。函数体内部使用yield表达式来定义每次返回的表达式。
function* generatorFunction() {  yield 'Hello';  yield 'World';  return 'ending';  
}  const generator = generatorFunction();  console.log(generator.next().value); // 输出 "Hello"  
console.log(generator.next().value); // 输出 "World"  
console.log(generator.next().value); // 输出 "ending"  
console.log(generator.next().value); // 输出 undefined,因为已经执行完毕
7.2 特点
- 暂停执行:Generator函数在执行过程中,遇到yield表达式就会暂停执行,返回遍历器对象的指针。
- 恢复执行:通过调用遍历器对象的next()方法,可以恢复Generator函数的执行,并从上次暂停的位置继续执行。
- 状态可控:由于Generator函数可以暂停和恢复执行,因此可以通过编程来控制其执行状态。
- 返回值:每次调用next()方法时,会返回一个对象,该对象包含两个属性:value和done。value属性表示当前yield表达式的值,done属性表示Generator函数是否已经执行完毕。当done为true时,value表示返回的最终结果。
7.3 应用场景
- 异步编程:Generator函数常与Promise结合使用,实现异步编程的流程控制。通过yield表达式来等待异步操作的结果,然后恢复执行后续的代码。这种方式可以使异步代码看起来更加同步,易于理解和维护。
- 控制流程:Generator函数可以用于控制函数的执行流程,例如实现迭代器、协程等。
- 数据懒加载:由于Generator函数可以暂停和恢复执行,因此可以实现数据的懒加载。即只有在需要时才加载数据,从而节省内存和网络带宽。
- 简化复杂操作:对于一些复杂的操作,可以将其拆分成多个步骤,并使用Generator函数来组织这些步骤。这样可以使代码更加清晰、易于理解和维护。
案例:
异步编程的同步化表达
function* fetchData(url) {  try {  const response = yield fetch(url); // 发起异步请求  if (!response.ok) {  throw new Error(`HTTP error! status: ${response.status}`);  }  const data = yield response.json(); // 等待异步请求结果并解析为JSON  console.log(data); // 输出获取到的数据  } catch (error) {  console.error('There has been a problem with your fetch operation:', error);  }  
}  // 实例化Generator函数  
const getData = fetchData('https://api.example.com/data');  // 启动Generator函数  
getData.next().value // 返回一个Promise对象  .then(response => getData.next(response).value) // 传入response到下一个yield  .then(data => getData.next(data)) // 如果没有yield,则不需要传入值  .catch(error => getData.throw(error)); // 如果有错误,则抛出异常
控制抽奖次数
function draw() {  // 具体抽奖逻辑  console.log('抽奖一次!');  
}  function* residue(count) {  while (count > 0) {  count--;  yield draw(); // 每次抽奖都yield一个表达式(在这里是draw函数的调用)  }  console.log('抽奖结束!');  
}  // 实例化Generator函数并传入初始抽奖次数  
let star = residue(5);  // 模拟用户点击抽奖按钮  
for (let i = 0; i < 5; i++) {  star.next(); // 每次点击都调用next()方法  
}
8. ES6中 Decorator
在ES6中,Decorator(装饰器)是一个实验性的特性,用于修改类的行为或属性。它本质上是一个用于类声明、方法、属性或参数上的设计模式,它允许你添加额外的功能到类声明、方法、属性或参数上,而无需修改其本身的代码。 要在TypeScript或某些Babel转译的JavaScript环境中使用Decorator,你需要相应的插件或配置。
8.1 基础用法
@decoratorName(parameters)  
class MyClass {  // ...  
}  // 或者用于方法、属性等  
class MyClass {  @decoratorName(parameters)  myMethod() {  // ...  }  
}
示例:用于记录类实例化的次数
function Loggable(target) {  let counter = 0;  return class extends target {  constructor(...args) {  super(...args);  counter++;  console.log(`${this.constructor.name} instantiated ${counter} times`);  }  };  
}  @Loggable  
class MyClass {  // ...  
}  // 实例化MyClass  
new MyClass(); // 输出 "MyClass instantiated 1 times"  
new MyClass(); // 输出 "MyClass instantiated 2 times"
9. ES6新增Set、Map两种数据结构
9.1 Set
Set 对象是一种值的集合,它类似于数组,但成员的值都是唯一的,没有重复的值。Set 本身是一个构造函数,用来生成 Set 数据结构。
- 创建Set:使用new Set()创建一个空的Set,或者使用new Set(iterable)从一个可迭代对象(如数组)中创建Set。
- 添加成员:使用add()方法向Set中添加新的成员。
- 删除成员:使用delete()方法从Set中删除指定的成员。
- 检查成员:使用has()方法检查Set中是否包含指定的成员。
- 清除成员:使用clear()方法清除Set中的所有成员。
// 创建一个 Set 实例  
const mySet = new Set();  // 添加元素  
mySet.add(1);  
mySet.add(2);  
mySet.add(2); // 重复添加,Set 中不会有重复值  // 遍历 Set  
for (let item of mySet) {  console.log(item); // 1, 2  
}  // 检查元素是否存在  
console.log(mySet.has(1)); // true  
console.log(mySet.has(3)); // false  // 删除元素  
mySet.delete(2);  
console.log(mySet.has(2)); // false  // 获取集合大小  
console.log(mySet.size); // 1
9.2 Map
Map 对象保存键值对,并且可以记住键的原始插入顺序。任何值(对象或者原始值)都可以作为一个键或一个值。
- 创建Map:使用new Map()创建一个空的Map,或者使用new Map(iterable)从一个可迭代对象(如数组)中创建Map,其中可迭代对象的元素是键值对数组。
- 添加键值对:使用set()方法向Map中添加新的键值对。
- 获取值:使用get()方法通过键获取对应的值。
- 检查键:使用has()方法检查Map中是否包含指定的键。
- 删除键值对:使用delete()方法从Map中删除指定的键值对。
- 清除所有键值对:使用clear()方法清除Map中的所有键值对。
// 创建一个 Map 实例  
const myMap = new Map();  // 添加键值对  
myMap.set('key1', 'value1');  
myMap.set('key2', 'value2');  // 遍历 Map  
for (let [key, value] of myMap) {  console.log(key + ' => ' + value); // "key1 => value1", "key2 => value2"  
}  // 检查键是否存在  
console.log(myMap.has('key1')); // true  // 获取键对应的值  
console.log(myMap.get('key1')); // "value1"  // 删除键值对  
myMap.delete('key1');  
console.log(myMap.has('key1')); // false  // 获取集合大小  
console.log(myMap.size); // 1
9.3 对比
| 特性/方法 | Set | Map | 
|---|---|---|
| 数据结构 | 值的集合,成员唯一 | 键值对的集合,键唯一 | 
| 成员类型 | 成员是任意类型的值 | 键和值都是任意类型的值 | 
| 成员重复性 | 成员的值是唯一的,没有重复 | 键是唯一的,值可以重复 | 
| 有序性 | 无序(但迭代时按插入顺序) | 有序(迭代时按插入顺序) | 
| 获取大小 | size 属性返回成员数量 | size 属性返回键值对数量 | 
| 添加成员 | add(value) | set(key, value) | 
| 删除成员 | delete(value) | delete(key) | 
| 检查成员 | has(value) | has(key) | 
| 获取成员 | 通过迭代获取值 | get(key) 获取与键对应的值 | 
| 清空集合 | clear() | clear() | 
| 遍历值 | 迭代Set即可遍历所有值 | 使用 values() 方法或 […map.values()] | 
| 遍历键 | 无直接方法,但迭代Set即遍历所有值 | 使用 keys() 方法或 […map.keys()] | 
| 遍历键值对 | 无直接方法 | 使用 entries() 方法或 […map.entries()] | 
| 默认迭代器 | 迭代Set时默认遍历值 | 迭代Map时默认遍历键值对 | 
| 应用场景 | 数组去重、交集、并集等集合操作 | 对象存储、缓存、统计数据等需要键值对结构的情况 | 
9.4 案例
数组去重
let arr = [1, 2, 2, 3, 4, 4, 5];  
let uniqueArr = [...new Set(arr)]; // 使用扩展运算符将Set转换回数组  
console.log(uniqueArr); // 输出: [1, 2, 3, 4, 5]
交集、并集、差集
let set1 = new Set([1, 2, 3]);  
let set2 = new Set([2, 3, 4]);  // 交集  
let intersection = new Set([...set1].filter(x => set2.has(x)));  
console.log(intersection); // 输出: Set { 2, 3 }  // 并集  
let union = new Set([...set1, ...set2]);  
console.log(union); // 输出: Set { 1, 2, 3, 4 }  // 差集 (set1中存在于set2中不存在的元素)  
let difference = new Set([...set1].filter(x => !set2.has(x)));  
console.log(difference); // 输出: Set { 1 }
对象存储
let obj1 = { id: 1, name: 'Alice' };  
let obj2 = { id: 2, name: 'Bob' };  
let map = new Map();  
map.set(obj1, 'Alice\'s data');  
map.set(obj2, 'Bob\'s data');  console.log(map.get(obj1)); // 输出: 'Alice\'s data'  
console.log(map.get(obj2)); // 输出: 'Bob\'s data'
缓存
let cache = new Map();  // 存储数据到缓存  
cache.set('key1', 'value1');  
cache.set('key2', 'value2');  // 从缓存中获取数据  
console.log(cache.get('key1')); // 输出: 'value1'  // 检查缓存中是否存在某个键  
console.log(cache.has('key2')); // 输出: true  // 删除缓存中的某个键值对  
cache.delete('key1');  // 遍历缓存中的所有键值对  
for (let [key, value] of cache) {  console.log(key, value); // 输出: 'key2' 'value2'  
}
统计数据
let text = 'hello world hello es6';  
let wordMap = new Map();  // 分割文本为单词数组,并统计每个单词的出现次数  
text.split(' ').forEach(word => {  if (wordMap.has(word)) {  wordMap.set(word, wordMap.get(word) + 1);  } else {  wordMap.set(word, 1);  }  
});  // 遍历Map并打印结果  
for (let [word, count] of wordMap) {  console.log(`${word}: ${count}`); // 输出: hello: 2, world: 1, es6: 1  
}
10. ES6中 Proxy
Proxy 对象用于定义一个自定义的行为,包括基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。通过使用 Proxy,你可以拦截并修改一个对象上的底层操作。
10.1 基础用法
Proxy 接收两个参数:一个目标对象(即需要被代理的对象)和一个处理器对象(一个定义了各种拦截行为的对象)。
const target = {}; // 目标对象  const handler = {  get(target, propKey, receiver) {  // 拦截 get 操作  console.log(`Getting ${propKey}`);  return Reflect.get(...arguments);  },  set(target, propKey, value, receiver) {  // 拦截 set 操作  console.log(`Setting ${propKey} = ${value}`);  return Reflect.set(...arguments);  },  // ... 可以定义其他陷阱函数  
};  const proxy = new Proxy(target, handler); // 创建一个代理对象  // 现在对 proxy 的操作会触发 handler 中的陷阱函数  
proxy.foo = 'bar'; // "Setting foo = bar"  
console.log(proxy.foo); // "Getting foo", 然后输出 "bar"10.2 处理器对象
处理器对象定义了各种陷阱函数(trap),用于拦截不同的操作。
| 陷阱函数 | 参数 | 描述 | 
|---|---|---|
| get(target, propKey, receiver) | target: 目标对象 propKey: 属性名(字符串或Symbol) receiver: 最初被调用的对象(通常是代理对象本身) | 拦截对象属性的读取操作 | 
| set(target, propKey, value, receiver) | target: 目标对象 propKey: 属性名(字符串或Symbol) value: 要设置的值 receiver: 最初被调用的对象(通常是代理对象本身) | 拦截对象属性的设置操作 | 
| has(target, propKey) | target: 目标对象 propKey: 属性名(字符串或Symbol) | 拦截 in 操作符的行为 | 
| deleteProperty(target, propKey) | target: 目标对象 propKey: 属性名(字符串或Symbol) | 拦截 delete 操作符删除属性的行为 | 
| defineProperty(target, propKey, desc) | target: 目标对象 propKey: 属性名(字符串或Symbol) desc: 属性描述符 | 拦截 Object.defineProperty 的行为 | 
| getOwnPropertyDescriptor(target, propKey) | target: 目标对象 propKey: 属性名(字符串或Symbol) | 拦截 Object.getOwnPropertyDescriptor 的行为 | 
| getPrototypeOf(target) | target: 目标对象 | 拦截 Object.getPrototypeOf 的行为 | 
| setPrototypeOf(target, proto) | target: 目标对象 proto: 新的原型对象 | 拦截 Object.setPrototypeOf 的行为 | 
| isExtensible(target) | target: 目标对象 | 拦截 Object.isExtensible 的行为 | 
| preventExtensions(target) | target: 目标对象 | 拦截 Object.preventExtensions 的行为 | 
| ownKeys(target) | target: 目标对象 | 拦截 Object.keys、Object.getOwnPropertyNames、Object.getOwnPropertySymbols 以及 for…in 循环的行为 | 
| apply(target, thisArg, argumentsList) | target: 目标函数 thisArg: 函数调用时使用的 this 值 argumentsList: 函数的参数数组 | 拦截函数调用或 Function.prototype.apply 调用 | 
| construct(target, argumentsList, newTarget) | target: 目标构造函数 argumentsList: 构造函数的参数数组 newTarget: 最初被 new 操作符调用的构造函数 | 拦截 new 操作符的行为 |