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 操作符的行为 |