前端笔试面试合集2
js类型比较
数组对象是object,用==比较会进行toString转换,所以不能和其他基本类型进行比较,而其他基本类型进行==比较的时候会先转换类型
阻塞和非阻塞网络IO有什么区别
在阻塞I/O模型中,当应用程序发起一个网络I/O请求时,程序会一直等待,直到I/O操作完成并返回结果才会继续执行下一条指令。在这个过程中,程序会被阻塞,无法执行其他任务,直到I/O操作完成。
相比之下,在非阻塞I/O模型中,当应用程序发起一个网络I/O请求时,程序不会一直等待,而是立即返回一个结果(通常是0或者错误码),然后继续执行下一条指令。在这个过程中,程序不会被阻塞,可以执行其他任务,同时通过轮询的方式不断地检查I/O操作是否完成,直到完成后再处理I/O操作的结果。
下面分别举两个例子说明阻塞I/O和非阻塞I/O的应用场景:
- 阻塞I/O的应用场景
阻塞I/O适用于需要等待I/O操作完成后才能进行下一步处理的场景,例如:
- 文件读写:当应用程序需要从磁盘读取大量数据时,阻塞I/O可以确保数据被完整读入内存后再进行处理,避免数据不完整或丢失。
- 网络连接:当应用程序需要建立一个稳定的网络连接时,阻塞I/O可以确保连接成功后再进行后续的网络通信。
- 非阻塞I/O的应用场景
非阻塞I/O适用于需要同时处理多个I/O请求的场景,例如:
- 网络通信:当应用程序需要同时处理多个客户端的网络请求时,非阻塞I/O可以让程序在等待I/O操作完成的同时处理其他任务,提高程序的并发性能。
- GUI界面:当应用程序需要响应用户的操作同时又需要进行I/O操作时,非阻塞I/O可以让程序在等待I/O操作完成的同时响应用户的操作,避免界面出现卡顿的情况。
多线程和多进程的程序各有什么优缺点
多线程和多进程是并发编程中两种常用的方式,它们都有优缺点,并且适用于不同的场合。
- 多线程的优缺点和适用场合
优点:
- 线程之间共享内存,数据交换和通信更加方便快捷,不需要使用IPC(进程间通信)。
- 线程创建和销毁的开销小,执行效率高。
- 由于共享内存,可以方便地实现数据共享和协同工作。
缺点:
- 多个线程共享进程的资源,线程之间需要进行同步和互斥操作,否则容易出现数据竞争和死锁等问题。
- 程序复杂度高,需要进行线程调度和管理。
- 线程之间的调试和排错较为困难。
适用场合:
- CPU密集型任务,例如图像处理、数值计算等。
- 数据共享和协同工作的场合,例如多人协作的软件项目。
- 需要高效利用CPU资源的程序。
- 多进程的优缺点和适用场合
优点:
- 进程之间互不干扰,不存在线程之间的数据竞争和死锁问题。
- 程序结构清晰,容易调试和排错。
- 进程之间可以使用IPC进行通信。
缺点:
- 进程间通信的开销比线程间通信要大,效率较低。
- 进程创建和销毁的开销大,执行效率较低。
适用场合:
- I/O密集型任务,例如网络通信、文件读写等。
- 需要独立运行的任务,例如系统服务、后台进程等。
- 需要在不同的机器上进行分布式处理的任务。
二分查找法检索元素速度比顺序法快还是慢
用二分查找法检索元素的速度通常比顺序查找法快,但这并不是绝对的,也要具体问题具体分析。
在一个已排序的数组中,如果需要查找一个元素,使用二分查找法的时间复杂度是O(log n),而使用顺序查找法的时间复杂度是O(n)。当n较大时,二分查找法的效率明显高于顺序查找法。
但是,如果数组本身是无序的,需要先进行排序操作,排序的时间复杂度是O(nlog n),此时二分查找法的总时间复杂度为O(nlog n),而顺序查找法的总时间复杂度也为O(nlog n),因此它们的效率相当。
此外,在某些特定场景下,顺序查找法可能比二分查找法更加高效。例如在数据集很小的情况下,二分查找法的优势可能并不明显;又如,数据的分布不均匀,最小值和最大值相差较小,也可能会导致二分查找法的效率降低。
五种linux版本
- Ubuntu:由 Canonical 公司开发,基于 Debian 操作系统,是最受欢迎的 Linux 发行版之一。它拥有大量的软件包和广泛的社区支持,适合用于桌面和服务器应用。
- CentOS:由社区维护,基于 Red Hat Enterprise Linux(RHEL),也是一种流行的服务器操作系统,提供长期支持和安全更新,广泛应用于企业服务器和云环境。
- Debian:Debian 是一个社区开发的操作系统,拥有大量的软件包和强大的软件管理工具。它稳定、可靠,并提供了很好的软件包更新机制。
- Fedora:由 Red Hat 公司赞助,是一种流行的桌面和服务器操作系统,它拥有最新的软件包和技术,并且经常更新。
- Arch Linux:是一个自由、轻量级和灵活的 Linux 发行版,具有简单的设计和文档化的文件结构,适合高级用户和程序员使用。它提供了最新的软件包和自定义配置选项,但需要用户有一定的 Linux 经验。
js写一个字符串大小写转换的功能
function toggleCase(str) { let result = ""; for (let i = 0; i < str.length; i++) { let char = str.charAt(i); if (char === char.toUpperCase()) { result += char.toLowerCase(); } else { result += char.toUpperCase(); } } return result; }
js写一个函数,输入某人生日之后,可以计算出今年还有多少天过生日
function daysUntilBirthday(birthday) { const today = new Date(); const thisYear = today.getFullYear(); const nextBirthday = new Date(birthday); nextBirthday.setFullYear(thisYear); if (nextBirthday < today) { nextBirthday.setFullYear(thisYear + 1); } const oneDay = 24 * 60 * 60 * 1000; return Math.round((nextBirthday - today) / oneDay); }
js写一个函数,给定s1,s2,s3,三个字符串,验证s3是否由s1和s2交错组成,这个当时没实现,太麻烦了
该函数接受三个字符串参数 s1、s2 和 s3,判断 s3 是否由 s1 和 s2 交错组成。首先判断 s3 的长度是否等于 s1 和 s2 长度之和,如果不等于,则直接返回 false。然后使用动态规划的思想递归判断 s1 和 s2 的字符是否能够交错组成 s3 的前 k 个字符,其中 i、j 和 k 分别表示 s1、s2 和 s3 中当前判断的字符下标。使用 memo 对已经判断过的结果进行缓存,避免重复计算。最终返回递归的结果。
function isInterleave(s1, s2, s3) { if (s1.length + s2.length !== s3.length) { return false; } const memo = {}; function helper(i, j, k) { if (i === s1.length && j === s2.length && k === s3.length) { return true; } if (k === s3.length) { return false; } const key = `${i}-${j}-${k}`; if (memo.hasOwnProperty(key)) { return memo[key]; } let result = false; if (i < s1.length && s1[i] === s3[k]) { result = result || helper(i + 1, j, k + 1); } if (j < s2.length && s2[j] === s3[k]) { result = result || helper(i, j + 1, k + 1); } memo[key] = result; return result; } return helper(0, 0, 0); }
IM有消息已读标志,请简述一下一个消息到界面展示已读,数据在客户端和网络上的流转过程
- 客户端发送消息:当客户端发送一条消息时,消息会首先被发送到服务器端,服务器将消息存储到数据库中,并向接收方推送消息。
- 客户端接收消息:接收方的客户端会接收到推送过来的消息,并在本地缓存中保存这条消息的相关信息,包括发送者、消息内容、时间戳等。
- 客户端读取消息:接收方在查看消息时,会向服务器发送一个已读回执,告知服务器这条消息已经被阅读了。
- 服务器更新消息状态:当服务器接收到已读回执后,会将这条消息的状态更新为已读,并将状态信息存储到数据库中。
- 客户端获取消息状态:当接收方再次打开聊天界面时,客户端会从服务器端获取消息的状态信息,并根据状态信息更新本地消息的状态,例如将消息标记为已读。
- 客户端展示已读状态:当消息被标记为已读后,客户端会根据已读状态来展示消息。例如,可以在消息列表中将已读的消息显示为灰色,或者在消息内容中显示已读标志。
两套后端服务程序A和B在线上长期运行,A和B之间通过TCP协议进行数据传输,A和B之前的数据传输是否可能丢失,并举例说明原因
A和B之间通过TCP协议进行数据传输时,一般情况下不会丢失数据。TCP协议是一种面向连接的可靠传输协议,它提供了数据完整性检查、数据重传、拥塞控制等机制,保证了数据的可靠传输。
然而,在极端情况下,TCP协议也可能会出现数据丢失的情况,例如:
- 网络故障:当网络出现故障时,可能会导致数据包无法正常传输,从而导致数据丢失。
- TCP连接超时:当TCP连接长时间没有活动时,可能会被网络设备或操作系统中的TCP协议栈关闭,从而导致数据丢失。
- 突发网络拥塞:当网络突然出现大量数据传输时,可能会导致网络拥塞,从而导致部分数据包丢失。
请用二进制计算解释为啥0.1+0.2不等于0.3
在 JavaScript 中,使用 IEEE 754 标准对浮点数进行表示,使用的是 64 位双精度浮点数,也就是说一个浮点数占用 8 个字节。
0.1 在二进制中无法精确表示,会变成一个无限循环小数,因此,当对这两个数进行加法计算时,会产生一个舍入误差,结果并不等于 0.3。
请论述含参数的宏和函数的优缺点和应用场合
- 宏是在预处理阶段处理的。
- 宏展开时直接替换代码文本,不需要函数调用的额外开销。
- 宏支持参数化,可以根据不同的参数展开不同的代码文本。
- 宏没有返回值,可以实现一些不需要返回值的简单代码块。
函数:
- 函数是在运行时调用的。
- 函数调用有一定的开销,包括参数传递、栈操作等。
- 函数可以接受参数并返回值。
- 函数的执行过程可以通过调用栈进行追踪和调试。
宏和函数的应用场合有所不同:
宏:
- 宏适合处理一些简单的代码块,例如常量定义、条件编译等。
- 宏可以根据不同的参数生成不同的代码,适合用于一些代码生成场景,例如字符串拼接、动态代码生成等。
函数:
- 函数适合处理一些复杂的逻辑,例如数学计算、字符串处理等。
- 函数可以封装一些通用的操作,例如数据结构的操作、IO操作等。
- 函数可以进行参数传递和返回值,方便进行复杂的数据处理和逻辑控制。
对称加密和非加密的区别
对称加密是指使用相同的密钥来加密和解密数据的方式。
非对称加密是指使用一对密钥,分别为公钥和私钥,在加密和解密时使用不同的密钥。
A函数和B函数都实现相同的功能,那么如何检测两个函数的执行效率
function A() { // A 函数实现代码 } function B() { // B 函数实现代码 } console.time('A函数执行时间'); A(); console.timeEnd('A函数执行时间'); console.time('B函数执行时间'); B(); console.timeEnd('B函数执行时间');
数组中有一个数字出现次数超过数组长度的一半,请找出这个数字,你可以假设数组是非空的,并切给定的数组总是存在多数元素,有一个限制,数组长度大于等于1小于等于50000
可以使用摩尔投票法来解决这个问题,该算法的基本思想是:
如果一个数字出现的次数超过了数组长度的一半,那么在遍历这个数组的时候,出现次数最多的数字一定是这个数字。
我们遍历数组,使用计数器来记录多数元素的出现次数,如果当前元素等于多数元素,则计数器加1,否则减1。如果计数器为0,则将当前元素设为多数元素。遍历完数组后,多数元素即为所求。
function majorityElement(nums) { let count = 0; // 计数器 let majority = null; // 多数元素 // 摩尔投票法 for (let i = 0; i < nums.length; i++) { // 如果计数器为0,则将当前元素设为多数元素 if (count === 0) { majority = nums[i]; } // 如果当前元素等于多数元素,则计数器加1,否则减1 if (nums[i] === majority) { count++; } else { count--; } } // 遍历完数组后,多数元素即为所求 return majority; }
给定一个非负整数num,反复将各个位上的数字相加,直到结果为一位数
我们先判断num是否大于等于10,如果大于等于10,则执行循环。在循环中,我们用一个内层循环来取出num的每一位数字,并进行累加。当内层循环结束后,将累加和赋值给num。这样反复执行,直到num小于10,即可得到最终结果。
function addDigits(num) { while (num >= 10) { let sum = 0; while (num > 0) { sum += num % 10; // 取出个位数并累加 num = Math.floor(num / 10); // 去掉个位数 } num = sum; // 将累加和赋值给num } return num; }
现在有一个div的类为cont,下面有两个子元素div,一个类名为sidebar,一个类名为content,其中content下面有两个子元素div,一个类名为header,一个类名为body,其中header下面有三个div,类名分别为avatar,name,quit,现在要求sidebar固定款段200px,content宽度自适应,header固定高度60px
我们使用了flex布局来实现两个子元素的水平排列。sidebar元素的宽度设置为200px,content元素的宽度设置为自适应。header元素的高度设置为60px,内部的三个div元素使用display:inline-block来实现水平排列。其中avatar和quit元素的宽度和高度设置为60px,用来模拟实际内容。在name元素中使用了calc()函数来计算宽度,使其剩余的空间正好够容纳后面的两个元素。
.cont { display: flex; } .sidebar { width: 200px; } .content { flex: 1; } .header { height: 60px; } .header > div { display: inline-block; } .header .avatar { width: 60px; height: 60px; background-color: #ccc; } .header .name { width: calc(100% - 180px); margin-left: 10px; font-size: 18px; line-height: 60px; } .header .quit { width: 60px; height: 60px; background-color: #ccc; }
页面中有两个节点A和B请写代码分析两个节点的关系
const nodeA = document.querySelector('#nodeA'); const nodeB = document.querySelector('#nodeB'); if (nodeA.contains(nodeB)) { console.log('nodeB is a descendant of nodeA'); } else if (nodeB.contains(nodeA)) { console.log('nodeA is a descendant of nodeB'); } else if (nodeA.parentNode === nodeB.parentNode) { console.log('nodeA and nodeB are siblings'); } else { console.log('nodeA and nodeB have no relationship'); }
请用js实现,把下面标签中字符串为div的文字加粗<div id='content'>outherdivss<div>3adiv4d</div>d</div>
const divs = document.getElementsByTagName('div'); for (let i = 0; i < divs.length; i++) { if (divs[i].textContent === 'div') { divs[i].style.fontWeight = 'bold'; } }
使用promise实现一个image同步加载的函数,onload的情况下执行resolve,onerror的时候执行reject,输出错误
function loadImage(src) { return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => { resolve(img); }; img.onerror = (error) => { reject(error); }; img.src = src; }); }
使用proxy,reflect劫持target使proxy.getDate()和new Date().getDate()返回一致结果
const handler = { construct(target, args) { return new Proxy(new target(...args), { get(target, prop) { if (prop === 'getDate') { return target.getDate.bind(target); } else { return Reflect.get(target, prop); } } }); } }; const ProxyDate = new Proxy(Date, handler); const proxy = new ProxyDate(); console.log(proxy.getDate()); // 输出当前日期的天数 console.log(new Date().getDate()); // 输出当前日期的天数
堆栈指针存储在ESP(Extended Stack Pointer)寄存器中。
DNS的默认缓存时间是根据DNS服务器的配置而定,可以是几分钟到几天不等。
dns的作用是域名和ip的相互映射 dns协议运行在UDP上 DNS的协议端口号为53
- RESTful接口中,HTTP的method用来描述要对资源操作的方式
- HTTP状态码304是缓存相关的状态码,表示客户端缓存资源仍然有效,无需重新传输资源
- HTTP协议是无状态协议,即每个请求都是独立的,服务器不会保留任何客户端的信息,需要客户端再次发送所有信息
解决元素重叠问题
clear属性可以清除元素浮动,让后面的元素不再和前面的浮动元素重叠。 .outer { clear: both; } 给.outer添加position:relative属性,可以使其成为相对定位元素,从而改变其在文档流中的位置, 同时,z-index属性可以控制元素的层叠顺序,使元素出现在浮动元素之上。 .outer { position: relative; z-index: 1; } 可以为其添加上内边距,避免与浮动元素重叠。代码如下: .outer { padding-top: 200px; }