[前端面试题总结] ⭐️ 从莉莉丝到滴滴--我的成长之路
前言
距离上篇面试题的分享已经过去半个月了,经历了面试官的灵魂拷问,笔者也在这半个月收获、成长了许多,所以笔者在本篇文章将会分享几家公司实习岗位的面试题(包括莉莉丝、哈啰、滴滴),希望对大家有帮助~ 一起进步哦~
面经分享
莉莉丝 凉经
莉莉丝是一家游戏公司,所以考察相对来说更注重思维能力,面试官会比较积极的引导,答不出来面试官也会讲解一遍。
-
瀑布流列表对于请求过的数据是否做优化?了解虚拟滚动(虚拟列表)吗?
这题是基于我的项目介绍问的。虚拟列表是在处理用户滚动时,只改变列表在可视区域的渲染部分,它可以提高页面渲染性能,减少数据过多时的卡顿。
下面是一个简单的虚拟列表demo:
function FixedSizeList(props){
const {{containerHeight,itemCount,itemHeight}} = props; // 容器高度,列表数量,列表项高度
const [scrollTop,setScrollTop] = useState(0)
const items = [];
// 渲染列表第一个index
let startIdx = Math.floor(scrollTop / itemHeight);
// 渲染列表最后一个index
let endIdx = Math.floor((scrollTop+containerHeight)/itemHeight);
// 在可视区外缓存列表项数量
const paddingCount = 2;
startIdx = Math.max(startIdx - paddingCount,0);
endIdx = Math.min(endIdx + paddingCount,itemCount-1)
for(let i = startIdx;i <= endIdx;i++) {
items.push(<Component key={i} index={i}
style={{height:itemHeight}}/>)
}
const top = startIdx * itemHeight;
const contentHeight = itemHeight * itemCount;
return (
<div style={{height:containerHeight,overflow:'auto'}}
onScroll={
(e) => {
// 处理渲染有导致的白屏问题
// 改为同步更新
flushSync(() => {
setScrollTop(e.target.scrollTop)
})
}
}
>
<div style={{height:contentHeight}}>
<div style={{height:top}}></div>
{items}
</div>
</div>
)
}
哈啰 已oc
面试官非常年轻,好像就已经是技术专家了,好羡慕,人也很好,整体面下来的感觉还是不错的。
滴滴 已oc
感觉面试官问的问题可回答面比较广,所以可以挑自己擅长的领域说~ 而且面试官比较和蔼,让我不要紧张。面试官直接就跟我约了二面的时间。
-
css布局方式
说了以下四种布局方式,然后以水平垂直居中为例,具体的说了下每种布局方式是如何使用的。
- flex
- grid
- float
- position
-
float离开文档流怎么做处理
我大概说了下清浮动、高度塌陷的不同处理方式,顺便说了下BFC
-
position属性,分别相对谁定位,具体使用场景
-
js 数据类型 Symbol使用场景
-
类型判断方法
我说了三种 typeOf、instanceOf、Object.prototype.toString.call() 以及他们各自的优缺点 面试官接着问了instanceOf 原理,说完继续问了下面两个问题 Array instanceOf Array == ?(false) Object instanceOf Object == ?(true)
-
数组api,以及在项目使用场景
-
forEach map区别 使用场景
forEach
和map
都可以对数组的进行遍历。forEach
返回值为undefined
,所以不能进行链式调用,适用于不更改数组的情况;map
会返回一个新数组,这个新数组由原数组中的每个元素都调用一次提供的callbackFn
函数后的返回值组成。一般用于需要修改数组的情况。- 对于数组项是基本数据类型的数组,
forEach
和map
都不修改调用它的原数组本身,但是那个数组可能会被callbackFn
函数改变; 对于数组项是引用数据类型的数组,用forEach
和map
都可以改变它的属性值。
又问:给对象数组中对象的每一项加一个属性,用 forEach 还是 map? 这里用
forEach
会更好,直接在原数组上修改即可,如果用map,还要将返回的结果重新赋值给原来的数组变量,语义不好。 -
手写代码 将对象数组转为对象
const array = [ { code: 101, name: '北京', }, { code: 102, name: '石家庄', }, { code: 102, name: '江苏', children: [{ code: 102, name: '南京', },{ code: 102, name: '连云港', }] } ]
转换成:
{ '北京':{ code: 101, name: '北京' }, '石家庄':{ code: 102 name: '石家庄' }, '南京':{ code: 102 name: '南京' }, '连云港':{ code: 102 name: '连云港' } }
实现代码:
function toObj(arr) { let obj = {}; for(let item of arr) { if(item['children']!==undefined) { obj = {...toObj(item['children']),...obj}; }else { obj[item.name]=item; } } return obj; }
-
根据代码看输出 this问题
const obj1 = { hello: function () { console.log(this); // obj1 setTimeout(function () { console.log(this); // window }); } } obj1.hello(); const obj2 = { hello: function () { console.log(this); // obj2 setTimeout(() => { console.log(this); // obj2 }); } } obj2.hello() const obj3 = { hello: ()=> { console.log(this); // wondow setTimeout(() => { console.log(this); // window }); } } obj3.hello()
-
闭包 以及项目中的使用
-
防抖节流
-
vite webpack 了解多少
-
js模块化 commonjs esmodule区别
-
UMD AMD CMD
- AMD (Asynchronous Module Definition 异步模块定义) commonjs是同步加载的,不适用于浏览器环境,所有有了AMD CMD; AMD是异步加载的,模块的加载不影响后面语句的运行。所有依赖这个模块的语句都定义在一个回调函数中,等 加载完之后,这个回调才会运行。AMD 的模块引入由 define 方法来定义
- CMD (Common Module Definition) 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。CMD 推崇依赖就近,AMD 推崇依赖前置。
- UMD (Universal Module Definition 通用规范模块)
统一浏览器端(AMD)和非浏览器端(commonjs)的模块化方案。
- 先判断是否支持AMD(define是否存在),存在则使用AMD方式加载模块;
- 再判断是否支持commonjs(exports是否存在),存在则使用commonjs模块格式
- 前两个都不存在,则将模块公开到全局
-
浏览器缓存 怎么做demo的
主要针对的是前端静态资源(js css image),大大的减少了请求的次数,提高了网站的性能(两端)
- 强缓存 设置http响应头
- http1.0版本:
Expires
具体时间点,客户端时间不准可能会导致误差 - http1.1版本:
Cache-Control
:max-age=xxx
时间偏移量 倒计时
- http1.0版本:
- 协商缓存
- Last-Modified
会经常更改的数据,不变则发送304。请求数量不变,请求体积减小
- 设置响应头:
Last-Modified
文件最近更改时间 - 判断请求头:
if-modified-since
==Last-Modified
发送304
- 设置响应头:
- etag
根据文件生成哈希串
- 设置响应体:
Etag
- 判断请求头:
if-none-match
==Etag
发送304
- 设置响应体:
- Last-Modified
会经常更改的数据,不变则发送304。请求数量不变,请求体积减小
- 强缓存 设置http响应头
-
nginx
-
跨域 jsonp缺点
-
项目后端做了啥
-
异步并行/串行 问 异步事件1、异步事件2是并行还是串行?怎么变为并行?
async() { await 异步事件1 await 异步事件2 }
这里我说的是让异步事件不写在
await
后面,这是一种实现方式。当然还有更好的实现方式,所以面试官问了下一题。async ()=>{ let result1 = 异步事件1 let result2 = 异步事件2 let res1 = await result1 let res2 = await result2 console.log(res1,res2) }
-
promose.all 和 promise.allSettled
先说怎么用
promise.all
解决上一题吧async ()=>{ var result = await Promise.all([异步事件1,异步事件2]) console.log(result[0],result[1]) }
这么写确实可以解决上题的问题,但是在
promise.all
中如果有一个异步事件出错,那么将会返回一个失败的promise
。所以如果异步事件互不依赖的话使用promise.allSettled
可能会更好,无论每个异步事件执行成功或失败都会返回一个带有执行结果的promise
。 > 想了解更多更多promise.allSettled
可以看这里~async ()=>{ var result = await promise.allSettled([异步事件1,异步事件2]) console.log(result[0],result[1]) }
最后
转载自:https://juejin.cn/post/7146151385707315213