跳到主要内容

迭代器、生成器、元编程✅

iterator 是什么?

答案

Iterator(迭代器)是 JavaScript 中用于遍历数据结构的一种协议。它提供了一种统一的方式来访问集合中的元素,而不需要关心集合的具体实现细节。Iterator 协议定义了一个对象,该对象包含一个 next() 方法,每次调用该方法都会返回集合中的下一个元素。

Iterator 协议的核心是实现了 next() 方法的对象,这个方法返回一个包含两个属性的对象:

  • value:当前元素的值。
  • done:一个布尔值,表示是否已经遍历完所有元素。如果 donetrue,则表示没有更多元素可供迭代。
  • Symbol.iterator:一个方法,返回一个新的迭代器对象。
  • for...of 循环可以直接使用 Iterator 协议来遍历集合。
  • 示例:
const arr = [1, 2, 3]
const iterator = arr[Symbol.iterator]()
console.log(iterator.next()) // { value: 1, done: false }
console.log(iterator.next()) // { value: 2, done: false }
console.log(iterator.next()) // { value: 3, done: false }
console.log(iterator.next()) // { value: undefined, done: true }

Iterator 协议的主要作用是提供一种统一的方式来遍历各种数据结构,如数组、字符串、Set、Map 等。通过实现 Iterator 协议,开发者可以自定义自己的数据结构,使其能够被 for...of 循环等语法直接遍历。Iterator 还可以与其他 ES6 特性(如生成器、解构赋值等)结合使用,增强代码的可读性和可维护性。

用过 generator ,讲一下?

答案

Generator(生成器)是 ES6 引入的一种特殊函数,具备“可暂停、可恢复”的执行特性。调用 Generator 函数不会立即执行,而是返回一个迭代器对象(Generator 对象),通过调用其 .next() 方法,函数体才会逐步执行,每次遇到 yield 语句暂停,并返回对应的值。

Generator 函数的声明方式为 function*,内部通过 yield 关键字实现“分段执行”。

示例:

function * gen () {
yield 1
yield 2
yield 3
}
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next()) // { value: 2, done: false }
console.log(g.next()) // { value: 3, done: false }
console.log(g.next()) // { value: undefined, done: true }
  1. 实现自定义迭代器
    Generator 天然实现了迭代器协议,可以用来生成序列、遍历数据结构等。

  2. 异步流程控制
    通过 yield 暂停异步操作,配合自动执行器(如 co 库)或手动调用 .next(),实现“同步写法的异步流程”,提升代码可读性。

    function * asyncGen () {
    const data1 = yield fetchData1()
    const data2 = yield fetchData2(data1)
    return data2
    }
  3. 协程/任务分片
    可将复杂任务拆分为多个步骤,逐步执行,避免阻塞主线程。

  4. 与 for...of、解构等配合
    Generator 返回的对象可直接用于 for...of、解构赋值等场景。

    for (const x of gen()) {
    console.log(x)
    }

注意事项

  • 只能由 function* 声明,不能用箭头函数。
  • 每次调用 .next(),从上次暂停处继续执行,直到下一个 yield 或函数结束。
  • yield 只能在 Generator 函数内部使用。
  • 可以通过 .next(value) 向上一个 yield 表达式传值,实现双向通信。
  • 异常处理:可用 try/catch 捕获 Generator 内部异常,也可用 .throw() 向生成器抛出异常。
  • 与 async/await 区别:Generator 更灵活,适合复杂控制流;async/await 更适合简单 Promise 链式异步。
  • Generator 返回的对象本身就是可迭代的(实现了 [Symbol.iterator]),可与解构、扩展运算符等配合。

Generator 提供了“可暂停、可恢复”的函数执行机制,适合实现自定义迭代器、异步流程控制、协程等高级场景。使用时需注意语法规范、异常处理和与异步工具的配合,能极大提升代码的灵活性和可读性。

用过 Proxy 么,简单讲解下?

答案

Proxy 是 ES6 引入的用于拦截和自定义对象基本操作(如属性读取、赋值、函数调用等)的机制。通过 Proxy,可以在对象操作前后插入自定义逻辑,实现如数据校验、虚拟属性、私有属性保护、日志记录等功能。

定义与用法

const proxy = new Proxy(target, handler)
  • target:被代理的对象。

  • handler:包含拦截方法(trap)的对象,如 getsethasdeletePropertyapplyconstruct 等。

常用拦截方法

方法说明
get读取属性时触发
set设置属性时触发
hasin 操作符时触发
deletePropertydelete 操作符时触发
ownKeys枚举属性时触发
apply代理函数调用时触发
construct代理构造函数调用时触发

基础示例

const obj = { name: 'Alice' }
const proxy = new Proxy(obj, {
get (target, key) {
console.log(`读取属性 ${key}`)
return target[key]
},
set (target, key, value) {
console.log(`设置属性 ${key}${value}`)
target[key] = value
return true
}
})
proxy.name // 控制台输出:读取属性 name
proxy.name = 1 // 控制台输出:设置属性 name 为 1

典型应用场景

  • 数据校验:拦截属性赋值,校验数据合法性。
  • 虚拟/计算属性:动态生成属性值。
  • 私有属性保护:限制对特定属性的访问。
  • 日志/调试:记录对象操作。
  • 深度监听:递归代理嵌套对象,实现深层变更监听。

注意事项

  • Proxy 只能代理直接对象,嵌套对象需递归代理才能深度监听。
  • 性能相较于 Object.defineProperty 略低,频繁操作大对象需谨慎。
  • 兼容性有限,IE 不支持。
  • 代理对象与原对象引用不同,注意引用比较和序列化等场景。

Object.defineProperty 区别

方面ProxyObject.defineProperty
作用范围整个对象及其所有操作单个属性
能力拦截多种操作仅能拦截属性的读写
深度监听支持(需递归实现)不支持
兼容性新浏览器,IE 不支持兼容性好
性能相对较低相对较高

示例:深度代理嵌套对象

function deepProxy (obj) {
return new Proxy(obj, {
get (target, key, receiver) {
const value = Reflect.get(target, key, receiver)
if (typeof value === 'object' && value !== null) {
return deepProxy(value)
}
return value
},
set (target, key, value, receiver) {
console.log(`设置属性 ${key}${value}`)
return Reflect.set(target, key, value, receiver)
}
})
}
const data = { info: { age: 18 } }
const p = deepProxy(data)
p.info.age = 20 // 控制台输出:设置属性 age 为 20

总结

Proxy 提供了强大的对象操作拦截能力,适合需要灵活定制对象行为的场景,但需注意性能和兼容性。

用过 Reflect 么,简单讲解下?

答案

Reflect 是 ES6 引入的内置对象,提供了一组与对象操作相关的方法,目的是让对象的底层操作变得更规范和函数化。它的方法与 Object 上的部分方法类似,但行为更一致,返回值更明确,且与 Proxy 的拦截器一一对应。

方法作用
Reflect.get(target, property, receiver)获取对象属性值,支持指定 this 上下文
Reflect.set(target, property, value, receiver)设置对象属性值,支持指定 this 上下文
Reflect.has(target, property)判断属性是否存在,等价于 property in target
Reflect.deleteProperty(target, property)删除对象属性,等价于 delete target[property]
Reflect.ownKeys(target)返回对象自身所有属性(包括 Symbol 和不可枚举属性)
Reflect.getPrototypeOf(target) / Reflect.setPrototypeOf(target, proto)获取/设置对象原型
Reflect.isExtensible(target) / Reflect.preventExtensions(target)判断/设置对象是否可扩展
Reflect.defineProperty(target, property, descriptor) / Reflect.getOwnPropertyDescriptor(target, property)定义/获取属性描述符
Reflect.apply(target, thisArg, args)调用函数,等价于 Function.prototype.apply
Reflect.construct(target, args, newTarget)构造实例,类似于 new target(...args)
  • Proxy 结合:在 Proxy 的 handler 中调用对应的 Reflect 方法,可以保证默认行为和一致性。
  • 更安全的对象操作:Reflect 方法返回布尔值或明确的结果,不会抛出异常,便于错误处理。
  • 统一的 API 风格:所有操作都是函数调用,便于元编程和自动化处理。

注意事项

  • Reflect 只提供静态方法,不能作为构造函数使用。
  • Object 方法相比,Reflect 方法的返回值更规范(如 Reflect.set 返回布尔值)。
  • 频繁操作原型或扩展性可能影响性能,建议按需使用。
  • 主要用于底层对象操作、元编程、代理拦截等高级场景,普通属性访问仍推荐直接点操作。
const obj = { a: 1 }
const proxy = new Proxy(obj, {
get (target, prop, receiver) {
console.log('读取属性', prop)
return Reflect.get(target, prop, receiver)
}
})
console.log(proxy.a) // 控制台输出:读取属性 a,返回 1

Reflect 提供了更一致、可控的对象操作方式,常与 Proxy 配合实现自定义行为,是现代 JS 元编程的重要工具。

48%