Q1
实现一个LazyMan,可以按照以下方式调用
(请写出实现代码或详细思路)
functon LazyMan() {.....}
调用 LazyMan('Hank') 输出:
Hi! This is Hank!
调用 LazyMan('Hank').sleep(10).eat('dinner')按顺序依次输出:
Hi! This is Hank!
// 等待10秒后输出以下内容(本条不包含在输出内容)
Wake up after 10
Eat dinner~
调用 LazyMan('Hank').eat('dinner').eat('supper')按顺序依次输出:
Hi! This is Hank!
Eat dinner~
Eat supper~
调用 LazyMan('Hank').sleepFirst(5).eat('supper')按顺序依次输出:
// 等待5秒后开始输出以下内容(本条不包含在输出内容)
Wake up after 5
Hi! This is Hank!
Eat Supper~
以此类推....
解析
首先LazyMan方法可以链式调用sleep sleepFirst eat方法。
说明LazyMan 和 这3个方法的执行结果都返回一个包含这3个方法的对象。
所以我定义一个class类(_LazyMan),在调用LazyMan的时候返回使用这个类创建的实例。
然后,sleep 和 sleepFirst 方法会延时打印内容,所以会用到setTimeout方法。
而其他方法的打印,会根据使用位置,在sleep[First]方法执行完后再执行。
所以要用到await,强制同步执行。
setTimeout方法本身是同步执行的,它的回调是延迟执行的,所以需要用Promise包裹setTimeout,使await能等待setTimeout执行完再执行后面的代码。
LazyMan单独执行会打印内容,说明_LazyMan的构造函数中有打印任务。
最后关键的是,sleepFirst会使构造函数的默认打印延后。
所以需要创建一个队列,在调用方法时,向队列添加任务,并在最终执行这个队列的任务。
首先,每个方法中的内容包括:添加任务到队列,通知队列执行。
目前每个方法执行完,都会执行队列的任务。
所以执行队列这个操作,要放到当前执行栈外面(通过setTimeout),保证在每个方法执行结束后,才执行队列的任务。
并且在添加任务时,要清除上一次的setTimeout,避免重复执行队列。
代码如下
class _LazyMan {
constructor(name) {
// 创建一个任务队列
this.taskQueue = []
// 存储一个计时器,用于重置
// 计时器的任务是执行队列中的任务
this.runTimer = null
this.say(name)
}
// 创建一个控制台,重置计时器,启动计时器
_run() {
// 重置计时器
if (this.runTimer) {
clearTimeout(this.runTimer)
}
// 启动计时器
this.runTimer = setTimeout(async () => {
// 同步执行队列任务
// 注意不要使用 forEach + await方式
for (let fn of this.taskQueue) {
await fn()
}
})
// 返回实例,保证链式调用
return this
}
say(name) {
this.taskQueue.push(async () => {
console.log(`Hi! This is ${name}!`)
})
// 通知控制台
return this._run()
}
eat(meal) {
this.taskQueue.push(async () => {
console.log(`Eat ${meal} ~`)
})
return this._run()
}
sleep(delay) {
this.taskQueue.push(this._timeout(delay))
return this._run()
}
sleepFirst(delay) {
// 向前添加任务
this.taskQueue.unshift(this._timeout(delay))
return this._run()
}
// 创建一个延时的 sleep
_timeout(delay) {
return async () => {
await new Promise(resolve => {
setTimeout(() => {
console.log(`Wake up after ${delay}`)
resolve()
}, delay * 1e3)
})
}
}
}
const LazyMan = function (name) {
return new _LazyMan(name)
}
LazyMan('Hank')
// LazyMan('Hank').sleep(10).eat('dinner')
// LazyMan('Hank').eat('dinner').eat('supper')
// LazyMan('Hank').sleepFirst(5).eat('supper')
Q2 已知如下对象,请基于es6的proxy方法设计一个属性拦截读取操作的例子
要求实现去访问目标对象example中不存在的属性时,抛出错误:Property “$(property)” does not exist
// 案例代码
const man = {
name: 'jscoder',
age: 22
}
//补全代码
const proxy = new Proxy(...)
proxy.name // "jscoder"
proxy.age // 22
proxy.location // Property "$(property)" does not exist
回答:
// 案例代码
const man = {
name: 'jscoder',
age: 22
}
//补全代码
const proxy = new Proxy(man, {
get(obj, key) {
if (man[key] === undefined) {
throw Error(`Property “${key}” does not exist`)
} else {
return Reflect.get(obj, key)
}
// 其他判断方法
// if (Reflect.has(obj, key)){}
// if (key in obj){/*会一直查找到原型*/}
}
})
const { log } = console
log(proxy.name) // "jscoder"
log(proxy.age) // 22
log(proxy.location) // Property "$(property)" does not exist
Q3 红灯三秒亮一次, 绿灯一秒亮一次, 黄灯2秒亮一次
实现一个函数,如何让三个灯不断交替重复亮灯? (用Promise实现) 三个亮灯函数已经存在:
function red() {
console.log('red')
} // 3秒
function green() {
console.log('green')
} // 1秒
function yellow() {
console.log('yellow')
} // 2秒
回答:
function red() {
console.log('red')
} // 3秒
function green() {
console.log('green')
} // 1秒
function yellow() {
console.log('yellow')
} // 2秒
function light(fn, delay) {
return new Promise(resolve => {
setTimeout(() => {
fn()
resolve()
}, delay * 1000)
})
}
function begin() {
Promise.resolve()
.then(() => {
return light(red, 3)
})
.then(() => {
return light(green, 1)
})
.then(() => {
return light(yellow, 2)
})
.then(() => {
begin()
})
}
begin()