十道大厂面试题(含答案)总结
又到了跳槽季啦,该刷题走起了。这里总结了一些被问到可能会懵逼的面试真题,有需要的可以看下~
1.不借助变量,交换两个数
1. 算术交换
针对的是Number,或者类型可以转换成数字的变量类型
function swap(a, b) {
a = a + b;
b = a - b;
a = a - b;
}
通过算术运算过程中的技巧,可以巧妙地将两个值进行互换。但是,有个缺点就是变量数据溢出。因为JavaScript能存储数字的精度范围是 -2^53 到 2^53。所以,加法运算,会存在溢出的问题。
2. 异或运算
^ 按位异或 若参加运算的两个二进制位值相同则为0,否则为1 此算法能够实现是由异或运算的特点决定的,通过异或运算能够使数据中的某些位翻转,其他位不变。这就意味着任意一个数与任意一个给定的值连续异或两次,值不变.
a = a ^ b;
b = a ^ b;
a = a ^ b;
3. ES6的解构
[a, b] = [b, a];
更多参考:
https://juejin.im/post/58b42fcc5c497d006793cd04 https://www.jianshu.com/p/61370227d096
2.实现sum(1,2,3)==sum(1)(2)(3)
function sum(...args){
function currySum(...rest){
args.push(...rest)
return currySum
}
currySum.toString= function(){
return args.reduce((result,cur)=>{
return result + cur
})
}
currySum.toNumber= function(){
return args.reduce((result,cur)=>{
return result + cur
})
}
return currySum
}
更多参考:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/134
3.观察者模式 vs 发布-订阅模式,说说区别
观察者模式的概念
观察者模式模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主体对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
发布订阅者模式的概念
发布-订阅模式,消息的发送方,叫做发布者(publishers),消息不会直接发送给特定的接收者,叫做订阅者。意思就是发布者和订阅者不知道对方的存在。需要一个第三方组件,叫做信息中介,它将订阅者和发布者串联起来,它过滤和分配所有输入的消息。换句话说,发布-订阅模式用来处理不同系统组件的信息交流,即使这些组件不知道对方的存在。
区别
我们把这些差异快速总结一下:
在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。
在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)。
观察者 模式需要在单个应用程序地址空间中实现,而发布-订阅更像交叉应用模式。
更多:https://juejin.im/post/5a14e9edf265da4312808d86
PS:最好把对应的模式的模型代码也写一下
4.实现LRU算法
实现一个LRU过期算法的KV cache, 所有KV过期间隔相同, 满足如下性质:
最多存储n对KV; 如果大于n个, 则随意剔除一个已经过期的KV; 如果没有过期的KV, 则按照LRU的规则剔除一个KV; 查询时如果已经过期, 则返回空;
class LRUCache {
constructor(capacity,intervalTime){
this.cache = new Map();
this.capacity = capacity;
this.intervalTime = intervalTime;
}
get(key){
if(!this.cache.has(key)){
return null
}
const tempValue = this.cache.get(key)
this.cache.delete(key);
if(Date.now() - tempValue.time > this.intervalTime){
return null
}
this.cache.set(key, {value: tempValue.value, time: Date.now()})
return tempValue.value
}
put(key, value){
if(this.cache.has(key)){
this.cache.delete(key)
}
if(this.cache.size >= capacity){ //满了
const keys = this.cache.keys()
this.cache.delete(keys.next().value)
}
this.cache.set(key, {value,time:Date.now()})
}
}
巧妙地利用了Map结构的key是有序的这个特点。普通Object的key是无序的。
5.如何监控网页崩溃?
基于 Service Worker 的崩溃统计方案
随着 PWA 概念的流行,大家对 Service Worker 也逐渐熟悉起来。基于以下原因,我们可以使用 Service Worker 来实现网页崩溃的监控:
Service Worker 有自己独立的工作线程,与网页区分开,网页崩溃了,Service Worker 一般情况下不会崩溃; Service Worker 生命周期一般要比网页还要长,可以用来监控网页的状态; 网页可以通过navigator.serviceWorker.controller.postMessage API 向掌管自己的 SW 发送消息。
基于以上几点,我们可以实现一种基于心跳检测的监控方案:
p1:网页加载后,通过postMessageAPI 每5s给 sw 发送一个心跳,表示自己的在线,sw 将在线的网页登记下来,更新登记时间; p2:网页在beforeunload时,通过postMessageAPI 告知自己已经正常关闭,sw 将登记的网页清除; p3:如果网页在运行的过程中 crash 了,sw 中的running状态将不会被清除,更新时间停留在奔溃前的最后一次心跳; sw:Service Worker 每10s查看一遍登记中的网页,发现登记时间已经超出了一定时间(比如 15s)即可判定该网页 crash 了。
更多:https://zhuanlan.zhihu.com/p/40273861
6.求代码输出,并说出为什么
var obj = {
'2':3,
'3':4,
'length':2,
'splice':Array.prototype.splice,
'push':Array.prototype.push
}
obj.push(1)
obj.push(2)
obj.push(3)
console.log(obj)
答案:
{
'2':1
'3':2,
'4':3,
'length':5,
'splice':Array.prototype.splice,
'push':Array.prototype.push
}
obj有长度,相当于类数组,调用数组的push,会在数组的最后加一项,第一次调用,相当于长度变为3,那么下标为2的那一项被赋值为1,下标是2,当其作为对象的key值的时候,会隐式调用toString方法转为字符串2,则和obj本来有的key '2'相同,原来的key为2的value就被覆盖了。以此类推后面的2个push。
详情:https://github.com/LuckyWinty/fe-weekly-questions/issues/48
7.node中的 setTimeout 和 setImmediate 有什么区别
setImmediate() 和 setTimeout() 很类似,但是基于被调用的时机,他们也有不同表现。
setImmediate 设计在poll阶段完成时执行,即check阶段; setTimeout 设计在poll阶段为空闲时,且设定时间到达后执行,但它在timer阶段执行
执行计时器的顺序将根据调用它们的上下文而异。如果二者都从主模块内调用,则计时器将受进程性能的约束。举个例子,有如下代码:
setTimeout(() => console.log(1));
setImmediate(() => console.log(2));
上面代码应该先输出1,再输出2,但是实际执行的时候,结果却是不确定,有时还会先输出2,再输出1。
这是因为setTimeout的第二个参数默认为0。但是实际上,Node 做不到0毫秒,最少也需要1毫秒,根据官方文档,第二个参数的取值范围在1毫秒到2147483647毫秒之间。也就是说,setTimeout(f, 0)等同于setTimeout(f, 1)。
实际执行的时候,进入事件循环以后,有可能到了1毫秒,也可能还没到1毫秒,取决于系统当时的状况。如果没到1毫秒,那么 timers 阶段就会跳过,进入 check 阶段,先执行setImmediate的回调函数。
但是,如果是这样的情况,输出顺序就固定了,例:
const fs = require('fs');
fs.readFile('test.js', () => {
setTimeout(() => console.log(1));
setImmediate(() => console.log(2));
});
在上述代码中,一定是先输出2,再输出1。因为两个代码写在 IO 回调中,IO 回调是在 poll 阶段执行,当回调执行完毕后队列为空,发现存在 setImmediate 回调,所以就直接跳转到 check 阶段去执行回调了,执行完成后再去到 timers 阶段,然后执行setTimeout。
8.写一个正则,根据name取cookie中的值。
function get(name){
var reg = new RegExp(name+'=([^;]*)?(;|$)');
var res = reg.exec(document.cookie);
if(!res || !res[1])return '';
try{
if(/(%[0-9A-F]{2}){2,}/.test(res)){//utf8编码
return decodeURIComponent(res);
}else{//unicode编码
return unescape(res);
}
}catch(e){
return unescape(res);
}
}
正则表达式中重点看这几句代码: '([^;])', 意思是匹配str=后面的不为; ([^;]表示非集, 也就是所有不为;的字符都能被匹配)的字符串, 该字符串出现0或更多次(), 之后将匹配到的字符串放入第一个捕获组.
详情:https://github.com/LuckyWinty/fe-weekly-questions/issues/32
9.在不改变html结构的情况下,写出至少7种方法实现以下布局。
要求:左边2列,右边1列,中间自适应
<div class="parent" style="width: 200px">
<div class="child child1" style="width: 20px"></div>
<div class="child child2" style="width: 20px">2</div>
<div class="child child3" style="width: 20px">3</div>
</div>
答案:
/* 1 */
.parent{
background-color: burlywood;
display: flex;
}
.child{
background-color: black;
font-size: 20px;
color: white;
}
.child3{
margin-left: auto;
}
/* 2 */
.parent{
background-color: burlywood;
position: relative;
}
.child{
font-size: 20px;
color: white;
}
.child1{
background-color: black;
position: absolute;
left: 0;
}
.child2{
background-color: black;
position: absolute;
left: 20px;
}
.child3{
background-color: black;
position: absolute;
right: 0;
}
/* 3 */
.parent{
background-color: burlywood;
}
.child1{
background-color: black;
float: left;
}
.child2{
background-color: red;
float: left;
}
.child3{
float: right;
background-color: blue
}
/* 4 */
.parent{
background-color: burlywood;
display: table;
}
.child{
background-color: black;
display: table-cell;
height: 20px;
}
.child3{
display: block;
margin-left: auto;
}
/* 5 */
.parent{
background-color: burlywood;
position: relative;
}
.child{
background-color: black;
position: absolute;
top:0;
left:0;
}
.child2{
transform: translate(20px, 0);
}
.child3{
transform: translate(180px, 0);
}
/* 6 */
.parent{
background-color: burlywood;
display: grid;
grid-template-columns: repeat(10, 1fr);
}
.child{
background-color: black;
font-size: 20px;
color:white;
}
.child3{
grid-column: 10 / 11;
}
/* 7 */
.parent{
background-color: burlywood;
font-size: 0;
}
.child{
background-color: black;
display: inline-block;
font-size: 20px;
color: white;
}
.child3{
margin-left: 140px;
}
10.用css画一个扇形?
width: 0;
height: 0;
border: solid 100px red;
border-color: red transparent transparent transparent;
border-radius: 100px;
说明
本文部分题目及答案来源于网络,如有错误,欢迎纠正,如有侵权问题,麻烦微信联系 winty230 交涉处理。
欢迎加我微信(winty230),拉你进技术群,长期交流学习...
欢迎关注「前端Q」,认真学前端,做个专业的技术人...