likes
comments
collection
share

经典回溯,从一个老面试题(LazyMan)中学到的知识

作者站长头像
站长
· 阅读数 30

人们因能感知太多信息而感到心神不宁,或因产生过多欲望而痛苦不堪,又或因担忧能力不足而滋生焦虑,无论顺境或是逆境都不得安生。就像今天的我们,虽衣食无忧,却总是苦于无法摆脱手机的干扰,无法获取让人羡慕的技能,无法拥有想要的生活,等等。低层次的动物是没有这种烦恼的,它们的心灵只容纳环境中确实存在的、与它们切身相关的、靠直觉判断的信息——饥饿的狮子只注意能帮助它猎到羚羊的信息,吃饱的狮子的注意力则集中在温暖的阳光上...... ——《认知觉醒》

前言

“旧游如梦,重逢却是初见”,这句话很好的描述了再看见这个面试题的感觉,最开始知道这道题的时候,它只是道题,再看见时,它好像变成了很多知识点,这也是一种进步的吧。

题目

可能有些人不知道这道题目,所以有必要描述一下题目,这是一道经典题目

> LazyMan('Hank')
你好,我是 Hank

> LazyMan('Hank').sleep(10).eat('lunch')
你好,我是 Hank
(等10秒...)
我醒了,我刚睡了 10
吃午餐

> LazyMan('Hank').eat('lunch').eat('supper')
你好,我是 Hank
吃午餐
吃晚餐

> LazyMan('Hank').sleepFirst(5).eat('supper')
(沉默5秒)
我醒了,我刚刚睡了 5
你好,我是 Hank
吃晚餐
  • 需要实现 LazayMan 函数,函数接收一个参数
  • 实现链式调用
  • 实现sleep, eat, sleepFirst方法,且执行时机要与上述匹配

知道上述情况后,大致可以奠定LazyMan的基调

const LazyMan = (name) => {
	
	return {
	
		sleep(){},
		
		eat(){},
		
		sleepFirst(){}
		
		
	}

}

  
LazyMan("Hank")

清楚题目后,我们开始来实现吧,如果可以,我建议你可以自己先想一想!

实现 LazyMan('Hank')

从这里第一个打印来看,非常简单,只需要将这个参数值在控制台打印出即可

代码实现

const LazyMan = (name) => {

	console.log(`你好,我是${name}`)
	
	return {
	
		sleep(){},
		
		eat(){},
		
		sleepFirst(){}
		
	}

}
  
LazyMan("Hank")

这样就可以得到第一个打印结果:

经典回溯,从一个老面试题(LazyMan)中学到的知识 第一步实现非常简单,对吧,接下来继续实现下一个

实现sleep(10)和eat('lunch')

首先看到LazyMan('Hank').sleep(10).eat('lunch')打印结果

你好,我是 Hank
(等10秒...)
我醒了,我刚睡了 10
吃午餐
  • sleep函数的执行时机是按照链式调用顺序执行,且sleep的作用是等待n秒后,再执行后续调用
  • eat函数就是根据参数打印是吃午餐,还是吃晚餐还是... 清楚这些后,我们开始在原来的代码上添加修改代码,以达到目的,既然 sleep 是等待n秒后执行其他函数,那我直接死循环等待即可

代码实现:

const LazyMan = (name) => {

	console.log(`你好,我是${name}`)
	
	const lazyMan = {
	
		sleep(second){
		
			let startTime = Date.now()
			
			while (Date.now() - startTime < 10000) { } // 空等 second 秒
			
			console.log(`我醒了,刚刚睡了${second}秒`)
			
			return lazyMan // 链式调用
		
		},
		
		eat(type){
		
			console.log("吃午餐")
		
		},
		
		sleepFirst(){}
	
	}
	
	return lazyMan

}

LazyMan("Hank").sleep(10).eat("lunch")

完工,这样我们也达到了这个调用的打印顺序要求了,查看结果:

经典回溯,从一个老面试题(LazyMan)中学到的知识 接下来继续实现下一个

实现eat函数

我们先看到调用方式LazyMan('Hank').eat('lunch').eat('supper'),在看打印结果

你好,我是 Hank
吃午餐
吃晚餐
  • eat链式调用
  • eat根据参数不同,打印出不同文字 很巧,链式调用就是直接返回一个对象即可,我们已经完成了,剩下就是根据不同参数,打印不同结果了

代码实现:

const LazyMan = (name) => {

	console.log(`你好,我是${name}`)
	
	const eatInfoMap = {
	
		'lunch': "吃午餐",
		
		'supper': "吃晚餐"
	
	}
	
	const lazyMan = {
	
		sleep(second){
		
			let startTime = Date.now()
			
			while (Date.now() - startTime < 10000) { } // 空等 second 秒
			
			console.log(`我醒了,刚刚睡了${second}秒`)
			
			return lazyMan // 链式调用
		
		},
		
		eat(type){
		
			console.log(eatInfoMap[type])
			
			return lazyMan
		
		},
		
		sleepFirst(){}
	
	}
	
	return lazyMan

}


LazyMan('Hank').eat('lunch').eat('supper')

这样似乎就满足了当前需求,查看打印结果:

经典回溯,从一个老面试题(LazyMan)中学到的知识 实现了这个以后,我们继续实现

实现sleepFirst函数

看到调用方式LazyMan('Hank').sleepFirst(5).eat('supper'),再看打印结果

(沉默5秒)
我醒了,我刚刚睡了 5
你好,我是 Hank
吃晚餐

这时候发现,如果继续采用之前的方式,好像永远也无法实现这个了,有点难受,这说明前面的想法都是错误的了 再分析一下,sleepFirst可以改变链式调用的执行顺序,所以代码执行的顺序,需要我们进行控制,关于控制顺序的方式,刚好有执行队列呀,我们只需要把需要先执行的推送到执行队列最前方不就好了,然后考虑到有等待,可以让每一个执行队列的第一个任务执行完成后,再提醒后续执行,这样不就解决了嘛,那就试试吧!

删除之前代码,改变思路,使用一个队列来控制执行顺序

代码实现:

const LazyMan = (name) => {

	const eatInfoMap = {
	
		'lunch': "吃午餐",
		
		'supper': "吃晚餐"
	
	}
	
	
	const queue = [] // 任务队列
	
	const helloTask = () => {
	
		console.log(`你好,我是${name}`);
		
		next()
	
	}
	
	queue.push(helloTask) // 入任务队列,默认是第一个打印,出了特殊情况
	
	  
	// 提醒需要执行后续函数,所以每个方法里执行了自己后,都需要向后传递,让后面执行
	const next = () => {
	
		const firstFn = queue.shift();
		
		firstFn?.()
	
	}
	
	  
	
	const lazyMan = {
	
		sleep(s){
		
			const task = () => {
			
				setTimeout(() => {
				
					console.log(`我醒了,我刚睡了 ${s} 秒`)
					
					next()
			
				}, s * 1000)
			
			}
			
			queue.push(task)
			
			return lazyMan
		
		},
	
		eat(type){
		
			const task = () => {
			
				console.log(eatInfoMap[type])
				
				next()
			
			}
			
			queue.push(task)
			
			return lazyMan
		
		},
	
		sleepFirst(s){
		
			// 这个任务比较特殊,需要在最前方等待,所以直接推送到队列第一个
			const task = () => {
			
				setTimeout(() => {
				
					console.log(`我醒了,我刚刚睡了 ${s} 秒`)
					
					next()
				
				}, s * 1000)
			
			}
			
			queue.unshift(task)
			
			return lazyMan
		
		}
	
	}
	  
	
	// 启动执行,但不能直接执行,如果直接执行的话,任务没有收集完,但是收集任务是同步的,所以要异步调用,所以就是手机完成后执行
	setTimeout(() => next())
	
	return lazyMan

}

LazyMan('Hank').sleepFirst(5).eat('supper')
  • 使用了任务队列,让执行顺序变成可控的
  • 所有的链式调用,先收集任务,按照函数效果插队,到对应位置
  • 使用 next 作为媒介,当自己执行完成,让后续继续执行

查看结果:

经典回溯,从一个老面试题(LazyMan)中学到的知识

至此,就完成了这道题目,我真的觉得相出这道题目的人是天才,哈哈哈,考点挺多的,如果之前没接触,还是很难写出来。

转载自:https://juejin.cn/post/7380221367823712265
评论
请登录