迭代器(iterator)
迭代器是数据结构遍历的一种机制,为数据结构定义了统一的遍历规则。
for...of遍历数据方法是通过迭代器去实现的,当一种数据没有实现迭代器接口,使用for...of会抛出异常。
- 迭代器在被调用时,返回一个指针对象,指针对象包含一个next()方法,每次调用next()方法时都会返回一个代表当前成员的对象,这个对象包含value和done两个属性。
- 迭代器对象有next()、return()、throw()三个属性方法,next()是必要属性。
- 自己实现迭代器只要给数据结构或者对象添加[Symbol.iterator]属性即可,其值必须是函数,返回一个指针对象。
手写迭代器
// 实现对象迭代器
Object.prototype[Symbol.iterator] = function () {
const that = this
let keys = Object.keys(that)
let keyIndex = 0
return {
next() {
if (keys.length === 0 || keyIndex >= keys.length) {
return {
value: undefined,
done: true,
}
}
const key = keys[keyIndex]
value = [key, that[key]]
keyIndex += 1
return {
value,
done: false,
}
},
}
}
const obj = { name: 'jack', age: 16 }
for (let [key, value] of obj) {
console.log(key, value)
}
// 实现数字迭代器
Number.prototype[Symbol.iterator] = function () {
let that = +this,
i = that < 0 ? that : 0
that = that < 0 ? 0 : that
return {
next() {
if (i <= that) {
const value = i
i++
return {
value,
done: false,
}
}
return {
done: true,
}
},
}
}
for (const n of 5) {
console.log(n)
}
console.log([...5]) // 数组拓展运算符也用到迭代器
手写Promise
- Promise使用的是观察者模式实现,then收集依赖 => 异步触发resolve => resolve执行依赖
- Promise A+规范,Promise是状态机,三种状态:padding、fulfilled、rejected,且只能有两种状态转变:padding => fulfilled 或 padding => rejected;then方法接收两个参数,分别对应的是成功失败的回调,then返回的是promise,并且then方法可以被调用多次。
- 值穿透:当then()接收的参数不是函数时应该忽略,如果没有忽略,当then()回调不为function时将会抛出异常,导致链式调用中断
- 处理状态为fulfilled和rejected时的问题,对于状态已变更的直接执行then回调
- 兼容同步任务
const PADDING = 'padding'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
this._status = PADDING
this._value = undefined
this._resolveQueue = []
this._rejectQueue = []
// _resolve 和 _reject 是在 executor 内部调用,使用箭头函数确定this指向
const _resolve = (val) => {
// 把resolve执行回调的操作封装成一个函数,放进setTimeout里,以兼容executor是同步代码的情况
const run = () => {
if (this._status !== PADDING) return
this._status = FULFILLED
this._value = val
// 这里用一个队列来储存回调,是为了实现规范要求then方法可以被一个promise调用多次
while (this._resolveQueue.length) {
const callback = this._resolveQueue.shift()
callback(val)
}
}
setTimeout(run)
}
const _reject = (val) => {
const run = () => {
if (this._status !== PADDING) return
this._status = REJECTED
this._value = val
while (this._rejectQueue.length) {
const callback = this._rejectQueue.shift()
callback(val)
}
}
setTimeout(run)
}
// new Promise时立即执行executor,并传入resolve和reject
executor(_resolve, _reject)
}
then(resolveFn, rejectFn) {
typeof resolveFn !== 'function' ? (resolveFn = (value) => value) : null
typeof rejectFn !== 'function'
? (rejectFn = (reason) => {
throw new Error(reason instanceof Error ? reason.message : reason)
})
: null
return new MyPromise((resolve, reject) => {
// 把resolveFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论
let fulfilledFn = (value) => {
try {
let res = resolveFn(value)
res instanceof MyPromise ? res.then(resolve, reject) : resolve(res)
} catch (error) {
reject(error)
}
}
let rejectedFn = (err) => {
try {
let res = rejectFn(err)
res instanceof MyPromise ? res.then(resolve, reject) : resolve(res)
} catch (error) {
reject(error)
}
}
switch (this._status) {
case PADDING:
this._resolveQueue.push(fulfilledFn)
this._rejectQueue.push(rejectedFn)
break
case FULFILLED:
fulfilledFn(this._value)
break
case REJECTED:
rejectedFn(this._value)
break
}
})
}
catch(rejectFn) {
return this.then(undefined, rejectFn)
}
finally(callback) {
return this.then(
(value) => MyPromise.resolve(callback()).then(() => value),
(reason) =>
MyPromise.reject(callback()).then(() => {
throw reason
})
)
}
static resolve(val) {
if (val instanceof Promise) val
return new Promise((resolve, reject) => {
resolve(val)
})
}
static reject(reason) {
return new Promise((resolve, reject) => reject(reason))
}
static all(promises) {
let count = 0
const result = []
return new MyPromise((resolve, reject) => {
promises.forEach((p, i) => {
MyPromise.resolve(p).then(
(val) => {
count++
result[i] = val
if (count === promises.length) resolve(result)
},
(err) => reject(err)
)
})
})
}
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach((p) => {
MyPromise.resolve(p).then(resolve).catch(reject)
})
})
}
}
这篇文章用来记录ES6高级相关的面试题,之后会陆续更新~