前端面试题

文章目录

  • 前言
  • 一、lodash.get方法?
  • 二、实现一个EventEmitter.js
  • 三、渲染VNode.js
  • 四、设计一个Cache.js


前言

本文主要记录在面试过程中,所遇到的题目。


一、lodash.get方法?

问题:
object (Object): 要检索的对象。
path (string): 要获取属性的路径。
[defaultValue] (*): 如果解析值不存在,会返回 default。

用例:

const object = { 'a': [{ 'b': { 'c': 3 } }] };console.log(_get(object, 'a[0].b.c'));
// => 3console.log(_get(object, ['a', '0', 'b', 'c']));
// => 3console.log(_get(object, 'a.b.c', 'default'));
// => 'default'

分析:

  • lodash.get是用来解决一些链路调用问题,使其能够在各种情况下都返回正确的值,而不是报错。
  • 官方API中path不止是string还可以是Array,也就是使用连续字符串也可以使用。
  • 在这里先将path进行处理,处理成数组[‘a’, ‘0’, ‘b’, ‘c’]形式,然后通过迭代去取值
function _get(object, path, defaultVal='default') {// 在这里实现let newPath = [] //存放预处理的pathif (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 } creturn (o ||{})[k]},object) || defaultVal
}

二、实现一个EventEmitter.js

问题:
实现一个 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

  • on:注册事件监听器,接受两个参数,第一个参数是事件名称,第二个参数是事件监听器。
    先判断是否存在该事件,不存在旧创建空数组并将事件处理函数添加到数组中
  • once:注册事件监听器,只会触发一次,触发后会自动移除。
    本质还是调用on方法,只不过事件处理函数会被额外包裹一层,其中事件处理函数最后会调用off方法,
    off方法,会根据传入事件处理函数名称来去除不是 callback 的函数,这样旧形成了只调用一次
  • 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]// 删除事件}
}

三、渲染VNode.js

问题:
写个函数用来渲染这个结构
就是将一个虚拟DOM渲染成真实DOM的过程

用例:

const renderJSON = {type: 'div',props: {className: '',},childrens:[{type: 'p',props: {text:'xxxxx'},childrens:['xxxx']}]
}

分析:

  • type:标签名
  • props:属性名,是个集合可能包含许多属性,需要遍历挂载,需要处理行内样式和值的绑定
  • childrens:子元素,子元素分为两种:一种为标签元素,另外一种为文本元素
    查看结构每个对象中都会包含childrens用来储存该DOM下的层级关系,通过递归的形式进行渲染
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.js

问题:
设计一个 Cache
支持下列两个基本操作:

  • set(id, object), 根据id设置对象;
  • get(id): 根据id得到一个对象;
    同时它有下面几个性质:
  1. x秒自动过期, 如果cache内的对象, x秒内没有被get或者set过, 则会自动过期;
  2. 对象数限制, 该cache可以设置一个n, 表示cache最多能存储的对象数;
  3. 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)

分析:

  • set方法,在设置缓存对象时,我们首先将其封装成一个对象 { obj, timestamp },其中 timestamp 表示缓存对象的时间戳,用于判断对象是否过期。
    然后,我们将该对象存储在缓存中,当缓存中不存在该缓存时将其唯一标识添加到 LRU 链表的末尾。检查缓存的大小,如果超过了最大大小,则自动删除最早添加的缓存对象;如果存在,则更新 LRU 链表位置,以及缓存时间戳信息。
  • get 方法用于获取缓存对象,它接受一个参数 id,表示要获取的缓存对象的唯一标识。
    在获取缓存对象时,我们首先检查该对象是否存在,如果不存在,则返回 不存在。
    如果存在,则检查该对象是否过期,如果过期,则从缓存中删除该对象,并返回 ‘已过期’。
    否则,我们将该对象移动到 LRU 链表的首部,并返回缓存对象。
  • delete 方法用于删除缓存对象,它接受一个参数 id,表示要删除的缓存对象的唯一标识。
    在删除缓存对象时,我们首先从缓存中删除该对象,并从 LRU 链表中删除该对象的唯一标识。
  • delete方法,用于删除缓存及标识
  • _checkSize方法,用于删除最久未使用的
  • _moveToFront方法,用于更新LRU链位置
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}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/70411.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

传输层协议--UDP

引入 传输层负责数据能够从发送端传输到接收端。 端口号(Port) 端口号标识了一个主机上进行通信的一个进程。 两个问题: 1. 一个进程可以绑定多个端口号吗?--可以 2.一个端口号可以绑定多个进程吗?--不可以 我们…

Matlab/Python教程系列 | 根据目录下的已有图片制作视频(动画)

MATLAB和Python的编程教程: 根据目录下的已有图片制作视频(动画) 注1:本文系“MATLAB/Python编程教程”系列之一,致力于使用Python和Matlab实现特定的功能。本次要实现的功能是:根据目录下的已有图片制作视频(动画)。 在这个教程中,我们将一起学习如何使用MATLAB和Python编…

YOLO目标检测——VOC2007数据集+已标注VOC格式标签下载分享

VOC2007数据集是一个经典的目标检测数据集,该数据集包含了20个常见的目标类别,涵盖了人、动物、交通工具等多个领域,共同11220图片。使用lableimg标注软件标注,标注框质量高,标签格式为VOC格式(即xml标签&a…

从零开发一款ChatGPT VSCode插件

‍本文作者是360奇舞团开发工程师 引言 OpenAI发布了ChatGPT,就像是给平静许久的互联网湖面上扔了一颗重磅炸弹,刹那间所有人都在追捧学习它。究其原因,它其实是一款真正意义上的人工智能对话机器人。它使用了深度学习技术,通过大…

《TCP/IP网络编程》阅读笔记--基于 TCP 的半关闭

目录 1--基于TCP的半关闭 1-1--TCP单方面完全断开的问题 1-2--shutdown()函数 1-3--半关闭的必要性 2--基于半关闭的文件传输程序 1--基于TCP的半关闭 1-1--TCP单方面完全断开的问题 Linux 系统中的 close 函数会将 TCP Socket 的连接完全断开,这意味着不能收…

苍穹外卖 day12 Echats 营业台数据可视化整合

苍穹外卖-day12 课程内容 工作台Apache POI导出运营数据Excel报表 功能实现:工作台、数据导出 工作台效果图: 数据导出效果图: 在数据统计页面点击数据导出:生成Excel报表 1. 工作台 1.1 需求分析和设计 1.1.1 产品原型 工作台是系…

智能安全帽~生命体征检测与危险气体检测一体化集成设计还是蓝牙无线外挂式方式好?

生命体征(心率、血氧等)检测&上报平台,危险气体采集&上报平台,是智能安全帽产品中常见的两种选配件,它们的实现有两种典型的模式: 1)将传感器集成到主板上,做成一体化的智能…

Java并发编程第7讲——CompletableFuture、Future和ForkJoinPool(万字详解)

在Java中进行异步计算是比较难以理解的。一般来说,我们希望将任何计算都视为一系列步骤,但是在异步的情况下,这些步骤通常以回调函数的形式存在,要么散布在代码中,要么互相嵌套的很深。而我们需要处理可能发生在某个步…

关于redux持久化的配置记录

前提是安装好redux相关关于在ts中使用最新版redux的方法记录_奋斗在前端的实习小白的博客-CSDN博客 1.下载 npm install redux-persist git地址:GitHub - rt2zz/redux-persist: persist and rehydrate a redux store 在项目根目录中配置好PersistGate标签 //re…

Java上传文件大小受限怎么解决

一般控制台上会出现像这样 ***1048576 bytes.这大小限制 org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size exceeded; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBa…

设计模式-01简单工厂模式详解 详细代码对比

目录 ChatGpt问答原生代码简单工厂模式代码 简单工厂模式(Simple Factory Pattern)新增boat 对比两种方法原生代码为什么使用强制转换?简单工厂模式 简单工厂方法总结与原生代码的区别:优点:缺点: 参考 本文将介绍什么…

栈和队列题目

文章目录 栈和队列栈基本概念常见操作实现方式应用场景经典题目1. 括号匹配2. 柱状图中最大的矩形队列基本概念常见操作实现方式应用场景经典题目1. 翻转队列2. 滑动窗口的最大值栈和队列 栈 基本概念 在计算机科学中,栈(stack)又称为堆栈,是一种特殊的数据结构,它只能…

jieba 加whooh 构建自己本地数据库的搜索引擎

例子 from whoosh.index import create_in from whoosh.fields import Schema, TEXT, ID from jieba.analyse import ChineseAnalyzer from whoosh.qparser import QueryParserimport osanalyzer ChineseAnalyzer() schema Schema(titleTEXT(storedTrue, analyzeranalyzer),…

二维数组笔试题及其解析

Lei宝啊 :个人主页 愿所有美好不期而遇 前言: 数组名在寻常情况下表示首元素地址,但有两种情况例外: 1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小 2.&数组名,这里的…

Hystrix和Sentinel熔断降级设计理念

目录 1 基本介绍2 Hystrix信号量和线程池区别2.1 信号量模式2.2 线程池模式2.3 注意 3 Sentinel介绍 1 基本介绍 Sentinel 和 Hystrix 的原则是一致的: 当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源…

算法通关村-----海量数据的处理方法

从40亿中产生一个不存在的数 问题描述 给定一个文件,包含40亿个非负整数,请你设计一个算法,产生一个不在该文件中的数字。假设你只有1GB内存。 问题分析 40亿整数,在java中,用int存储的话,大概需要40亿✖️4B,大约…

目前无法建立VS2013与Qt的连接???

因为下载组件的时候,没有哪个选项,还是没有MSVC2013

微信小程序集成腾讯im,会话列表数据过多(长列表),卡顿问题的解决

说明 我这边用小程序集成im,然后结合公司的需求,做了一个聊天的小程序,在测试上线的时候没有问题,结果到客户那边,因为他们聊天的人多,会话列表达到了300多条,然后点击会话列表,进入…

C#,《小白学程序》第十六课:随机数(Random)第三,正态分布的随机数的计算方法与代码

1 文本格式 // 定义一个全局性&#xff08;公共&#xff09;的随机数发生器&#xff0c;便于大家&#xff08;各函数&#xff09;后面共同使用。 Random global_rnd new Random(); /// <summary> /// 生成服从标准正态分布的随机数 /// https://zhuanlan.zhihu.com/p/6…

Postern配置HTTP和HTTPS的步骤

Postern是一款强大的Android代理工具&#xff0c;它允许您在设备上配置全局代理来实现安全、隐私保护和自由上网。本文将详细介绍如何使用Postern在Android设备上配置HTTP和HTTPS代理&#xff0c;为您提供更便捷的上网体验。 步骤1&#xff1a;下载和安装Postern应用 首先&am…