本文主要记录在面试过程中,所遇到的题目。
问题:
object (Object): 要检索的对象。
path (string): 要获取属性的路径。
[defaultValue] (*): 如果解析值不存在,会返回 default。
用例:
const object = { 'a': [{ 'b': { 'c': 3 } }] };
console.log(_get(object, 'a[0].b.c'));
// => 3
console.log(_get(object, ['a', '0', 'b', 'c']));
// => 3
console.log(_get(object, 'a.b.c', 'default'));
// => 'default'
分析:
function _get(object, path, defaultVal='default') {
// 在这里实现
let newPath = [] //存放预处理的path
if (Array.isArray(path)){// 如果传入路径为数组形式直接赋值不用处理
newPath = path
}else {// 处理path为数组,利用replace替换'[]'为'.',利用split将字符串分割成字符数组
newPath = path.replace(/\[/g,'.').replace(/\]/g,'').split('.')
}
return newPath.reduce((o,k)=>{//通过reduce迭代newPath找路径没找到则返回defaultVal
// { a: [ { b: [Object] } ] } a
// [ { b: { c: 3 } } ] 0
// { b: { c: 3 } } b
// { c: 3 } c
return (o ||{})[k]
},object) || defaultVal
}
问题:
实现一个 EventEmitter。
用例:
const eventEmitter = new EventEmitter()
function callback() {
console.log('hit!')
}
// 监听事件, 其中有一个 once 单次监听
eventEmitter.on('custom-event', callback)
eventEmitter.once('custom-event', callback)
// 连续触发两次
eventEmitter.emit('custom-event')
eventEmitter.emit('custom-event')
// 预期输出 3 次 "hit!"
// 删除并再次=触发
eventEmitter.removeListener('custom-event');
eventEmitter.emit('custom-event')
// 预期没有输出
这里查了一下EventEmitter属于node服务端events模块对外提供的一个EventEmitter对象,用于对Node.js中对事件进行统一管理,表示没学Node,根本不知道啊,只知道浏览器事件EventTarget,不过二者都差不多,都是用来对事件进行处理的,不过浏览器事件会存在冒泡,因为在Node中不存在层级关系,浏览器DOM是存在层级关系的,且浏览器事件是基于观察者模式的,而EventEmitter的事件是基于发布订阅模式的。
分析:
需要编写一个类,实现内部方法on、once、emit、removeListener
class EventEmitter {
constructor() {
this.events = {}// 存储事件
}
// 在这里实现
on(event,callback) {// 监听
if (!this.events[event]){// 是否存在该事件
this.events[event] = []// 不存在创建一个空数组
}
this.events[event].push(callback)// 将事件处理函数添加到数组集合中
}
once(event,callback){// 单次监听
const wrapper = () => {// 在外包裹一层,使调用时同时清除该次事件处理函数
callback();
this.off(event);
};
this.on(event, wrapper);
}
off(event){
if (!this.events[event]) {
return;
}
this.events[event] = this.events[event].filter((cb) => cb!== callback);
}
emit(event){// 触发事件
if (!this.events[event]) {
return;
}
this.events[event].forEach((callback) => callback());// 循环执行事件
}
removeListener(event){
if (!this.events[event]) {
return;
}
delete this.events[event]// 删除事件
}
}
问题:
写个函数用来渲染这个结构
就是将一个虚拟DOM渲染成真实DOM的过程
用例:
const renderJSON = {
type: 'div',
props: {
className: '',
},
childrens:[
{
type: 'p',
props: {
text:'xxxxx'
},
childrens:['xxxx']
}
]
}
分析:
const render = (renderJSON) =>{
const {type, props, childrens} = renderJSON //将三个参数结构出来
let el = document.createElement(type)// 创建标签元素
for (let key in props){// 挂载属性
el.setAttribute(key,props[key])//设置属性上的值,这里没有考虑行内样式以及绑定值的处理
}
//创建子节点
childrens.forEach(child =>{
if (child instanceof Object){//如果为标签元素
el.appendChild(render(child))//将子元素添加到父元素内部末尾处,递归创建子元素
}else {//如果为文本
let textNode = document.createTextNode(child)//创建一个文本节点,将文本塞入
el.appendChild(textNode)//添加文本结点到父元素内部
}
})
return el
}
document.body.appendChild(render(renderJSON))// 挂载到body下
问题:
设计一个 Cache
支持下列两个基本操作:
- set(id, object), 根据id设置对象;
- get(id): 根据id得到一个对象;
同时它有下面几个性质:
- x秒自动过期, 如果cache内的对象, x秒内没有被get或者set过, 则会自动过期;
- 对象数限制, 该cache可以设置一个n, 表示cache最多能存储的对象数;
- LRU置换, 当进行set操作时, 如果此时cache内对象数已经到达了n个, 则cache自动将最久未被使用过的那个对象剔除, 腾出空间放置新对象;
用例:
const cache = new Cache(2,3)
cache.set(1,{name:'smz1'})
cache.set(2,{name:'smz2'})
cache.set(1,{name:'smz3'})
setTimeout(()=>{
console.log(cache.get(1))// 已过期
},4000)
分析:
class Cache {
constructor(maxSize = 10,maxAge = 60) {
this.maxSize = maxSize // 最大缓存数
this.maxAge = maxAge// 最长过期时间
this.cache = {}// 缓存列表
this.lruList = [] // 缓存唯一标识
}
set(id,obj){
const item = this.cache[id];// 在缓存中查找是否存在
const timestamp = Date.now() // 存储建立的时间
this.cache[id] = {obj, timestamp} // 封装成对象存储在缓存中
if (!item){// 不存在
this.lruList.push(id)// 将唯一标识添加到链表末尾
this._checkSize()// 检查缓存大小
}else {// 存在
this._moveToFront(id);//将该id标识移动到最后面
}
}
get(id){
const item = this.cache[id];// 在缓存中查找是否存在
if (!item) {// 不存在返回提示
return '不存在';
}
if (Date.now() - item.timestamp > this.maxAge * 1000) {// 判断是否过期,过期删除并返回提示
this.delete(id);
return '已过期';
}
this._moveToFront(id);//将该id标识移动到最后面
return item.obj;
}
delete(id){// 过期删除缓存及标识
delete this.cache[id];// 删除缓存
this.lruList = this.lruList.filter((item) => item!== id);// 移除标识
}
_checkSize() {// 缓存满删除缓存
if (this.lruList.length > this.maxSize) {// 大于了最大储存数时
const id = this.lruList.shift();// 返回第一个元素
delete this.cache[id];// 在缓存中删除
}
}
_moveToFront(id) {// 更新id标识位置
const index = this.lruList.indexOf(id);// 指定元素下标
if (index!== -1) {// 存在
this.lruList.splice(index, 1);//移除id标识旧位置
this.lruList.push(id);// 将id标识添加到链表最后面
}
}
getList(){// 返回存储集合
return this.cache
}
}