前端面试:分享一道曾让我栽在二面的面试题|项目复盘

复制代码

还可以监听 mousemove 或者键盘等事件,但那个项目是触摸大屏,没有鼠标或者键盘,所以监听点击事件就够了

短短几行代码就解决了我的燃眉之急,当然那时候也菜,这么简单的需求都没想出来,不过谁还不是从小白一步步走上来的呢?正是靠着这些文章一步步扩展了思路才会很快的进步。

Vue 性能优化

看了黄老师出品的 《揭秘 Vue.js 九个性能优化技巧》

才知道原来 computed 里面的函数是可以接收一个 this 参数的:

computed: {
a () { return 1 },
b ({ a }) {
return a + 10
}
}
复制代码

这样就不会在组件刷新时重复获取 getter 了,以前从来没注意过这些。

纯 CSS 实现拖拽效果

以前我们做拖拽的时候基本都会用 JS 去实现,很麻烦,但看了阅文前端团队的《纯 CSS 也能实现拖拽效果》令我佩服的五体投地:

在传统 web 中,页面滚动是一个很常见交互,操作上就是利用鼠标滚轮或者直接拖动滚动条。但是,移动端可不一样,直接用手指拖动页面就可以滚动了。通常页面是要么垂直方向滚动,要么水平方向滚动,如果两个方向都可以滚动呢?例如:

.dragbox {
width: 300px;
height: 300px;
overflow: auto
}
.dragcon {
width: 500px;
height: 500px;
}
复制代码

只需要内部元素宽高都大于容器就实现两个方向的滚动了(记得设置overflow:auto),示意如下:

一般情况下,鼠标滚轮只能同时滚动一个方向(按住Shift可以滚动另一方向),但是移动端可以直接拖着内容任意滚动,如下所示:

现在,在内容中间添加一个元素,跟随内容区域一起滚动:

接下来,把后面的文本隐藏起来:

是不是有点拖拽的味道了?原理就是这么简单!

Vue3 的新语法

现在一搜 Vue3 出来的要不就是 Composition API 要么就是 新的响应式原理,这些东西讲起来都比较复杂,而且大家都忽略了好多其他的点,比如很多时候我们想要 CSS 也能是响应式的,比如曾经幻想过的语法:

{{ color }}

复制代码

不过由于 CSSJS 隶属不同上下文,这一点很难做到,但自从看了这篇《Vue超好玩的新特性:在CSS中引入JS变量》才发现原来还可以这么写:

{{ color }}

复制代码

this.color 发生变化时,css 也会一同做出响应。

还有就是《Vue超好玩的新特性:DOM传送门》,这些小技巧能够非常方便的提升我们的开发效率,但如今的 Vue3 相关文章却很少有人提及到这些。

九宫格面试题

这种面试题代码量不多,但却甚少人能够做对,这篇《千万别小瞧九宫格 一道题就能让候选人原形毕露!》给我们提供了很好的一个思路,因为在做这种九宫格时:

很多人以为只需要给每个格子加上一个边框即可,而实际上如果这么做的话会变成下面这样:

因为在给每个盒子加入了边框之后,相邻的两个边框就会贴合在一起,肉眼看起来就是一个两倍粗的边框。而《千万别小瞧九宫格 一道题就能让候选人原形毕露!》利用负边距轻轻松松的就解决了这个难题:

你不知道的 CSS 负值

提到负边距就让人想起这篇《你所不知道的 CSS 负值技巧与细节》:

[图片上传中…(image-d30ceb-1615723259233-13)]

复制代码

真的是没想到这样就能实现加号。

题目

说了这么多有点跑题了,本意其实是想说明:本篇文章的算法题就像上面列举出来的文章那样,代码量不多、甚至还很简单,但重点就是考察你对技术的灵活运用程度,你的思维能不能转得过弯来。

当然也不是说那些代码量很多很复杂的文章不好,其实那些文章技术含量都很高,但毕竟大部分人没有心思那么仔细的钻研各种复杂的算法,不过你要去的如果是字节跳动百度阿里腾讯这类大厂去面试的话,钻研一下那些复杂的文章还是非常有必要的。

情景再现

面试那天我来到了一个看起来像是会议室的屋子里,面试官给了我几张卷子和一只笔,让我先写,然后他就出去了。我还在想:没人看着我难道就不怕我用手机搜索答案么?是不是有摄像头然后通过屏幕来观察我有没有用手机搜索答案,进而考察候选人的诚实与否…

当然我也没有想用手机

卷子有点像是中学考试那样:选择题 + 填空题 + 大题

大题就是手写代码,其实挺烦这种的… 一方面写大括号只能先写一半,因为不知道在大括号里会写多少行代码,不像是编辑器里那样,尾括号随着行数的增加会自动移动;另一方面是没有控制台,自己也不知道自己写的到底对不对,只能通过直觉来判断。

其中一道题目是:写一个函数,这个函数会返回一个数组,数组里面是 2 ~ 32 之间的随机整数(不能重复),这个函数还可以传入一个参数,参数是几,返回的数组长度就是几

就像这样:

刚看到题目的时候还在想这有啥难的,写呗!首先先来生成从 2 ~ 32 之间的随机数…怎么生成 2 ~ 32 之间的随机数呢?Math.random() * 32 ,可是这是生成 0 ~ 32 的,有了!先生成 0 ~ 30 之间的随机数然后再加上 2 不就得了:

const fn = num => {
let arr = []

for (let i = num; i-- > 0;) {
arr.push(Math.round(Math.random() * 30 + 2))
}

return arr
}
复制代码

这样写带来的问题就是,随机生成的数字会有重复:

这时我想到了 ES6 的新增数据结构 Set,它里面是可以保证没有重复值的,而且它还可以用 ... 操作符很方便的转为数组,于是继续写:

const fn = num => {
let arr = []

for (let i = num; i-- > 0;) {
arr.push(Math.round(Math.random() * 30 + 2))
}

arr = […new Set(arr)]

return arr
}
复制代码

这样写虽然解决了重复值的问题,但却带来了新的问题:如果有几个重复值数组的长度就会少几,就像这样:

当时我的思路是这样的:假如 fn(10) 传的参数是 10,如果最终出来的数组不为 10,那就用 10 减去数组的长度,就是相差的位数了。比如 fn(10) 导致 arr.length = 8,那么 10 - 8 就代表只需要再生成 2 个随机数就可以了,但随机的两个数也可能会和现有的 8 位数组重合,所以我们要把随机生成的两位数连接到原来的 8 位数组中去,然后再用 Set 数据结构去重,用 while 循环判断,如果传进来的参数 10 减去数组长度 arr.length 不等于 0 的话就证明依然还是有重复项,那就继续再生成随机数重复刚才的步骤,直到生成 10 位所有数字都不重复的数组就会自动跳出 while 循环,然后返回这个数组:

const fn = num => {
let arr = []

for (let i = num; i-- > 0;) {
arr.push(Math.round(Math.random() * 30 + 2))
}

arr = […new Set(arr)]

let len = arr.length

while (num - len > 0) {
arr = […new Set(arr.concat(fn(num - len)))]
len = arr.length
}

return arr
}
复制代码

运行结果:

当然我在笔试的过程中是看不到运行结果的,这是我回到家之后凭借着印象写出来的代码,想试验一下写的对不对。

二面加难度后的题

到了二面的时候(省略问的其他问题),面试官说那道题虽然你做对了,但其实有点像是暴力破解的感觉,效率很差。比如我在函数里传入 30,从 2 ~ 32 总共也就 30 个数,你想想生成随机数的这个方法会运行多少次,假如足够幸运,第一次运行函数就生成了 29 位不同数字的数组,那么还差一位就齐了,你想想最后这一位重复的几率有多大?是不是30分之29?重复一次就要再运行一遍、重复一次再运行… 每次都要新建数组然后再新建 Set 再转回数组,开销很大的,你有没有什么想优化的点?

此时我想的是不用 Set 来去重,想的是怎么复用原来的数组不让它重新生成,每次只返回一个数然后递归?还是加入第二个参数,传入原来生成的数组?

我把我的想法说给他听后,他并不满意,觉得我没有说到他想要的点上,于是他给了我点提示:假设生成随机数这个操作是特别费时的一项操作,你有没有办法只让他运行传进来的参数那么多次?就好比 fn(10),能不能只让 Math.random 这个函数只运行 10 次?

当时我一听头都大了,这怎么可能呢?既然是在一定范围内生成随机数,那么肯定无可避免的会有重复项,哪怕不用 Set 用别的方式去重,那也必须得运行超过 10 次啊!不过他既然这么问了就证明肯定有什么办法能够做到,于是我绞尽脑汁想啊想,最终还是钻了牛角尖:认为无论什么方法都无法避免生成重复项,即使是足够幸运运行一次就得到了想要的结果,那也不算是技术实现出来的,只能算是运气好,最后只好摊牌说自己没思路。

我本以为面试到这里就要结束了,没想到他居然主动跟我说了一下这道题的解法,但当时心里有些沮丧,他说的那一大堆都没听进去,只记住了他说要定义两个数组,在坐地铁回家的路上我一直在想:两个数组… 两个数组?

回到家打开电脑开始写代码,先把我原来的那个解法做个测试,看看性能到底有没有他说的那么差:

0.1 毫秒,也还行啊!可能是数字小了吧,如果是生成从 0 ~ 10000 之间的随机数应该就崩溃了吧?修改一下函数:

const fn = num => {
let arr = []

for (let i = num; i-- > 0;) {
arr.push(Math.round(Math.random() * 10000))
}

arr = […new Set(arr)]

let len = arr.length

while (num - len > 0) {
arr = […new Set(arr.concat(fn(num - len)))]
len = arr.length
}

return arr
}
复制代码

运行结果:

这回确实明显的感觉到卡顿了,两秒多钟才出来结果,在计算机运算里两千毫秒已经算得上是天文数字了。那再来试试他说的两个数组:我的理解是先事先定义一个里面装着从 2 ~ 32 范围内的所有整数,然后再定义一个空数组用来存放结果,在数组的长度(length)范围内随机生成整数,用这个生成出来的整数当作下标从那个数组中取出数字来放入空数组中,这样即使生成出来的随机数有重复项也没有关系,因为这两个数组不会有重复项:

const fn = num => {
const allNums = Array.from({ length: 31 }, (_, i) => i + 2)
const result = []

for (let i = num; i-- > 0;) {
result.push(allNums.splice(Math.floor(Math.random() * allNums.length), 1)[0])
}

return result
}
复制代码

这回再来试一下:

没有任何毛病,那性能呢?来测一下:

确实是比以前快得多,再来试一下 0 ~ 10000 的随机数:

const fn = num => {
const allNums = Array.from({ length: 10001 }, (_, i) => i)
const result = []

for (let i = num; i-- > 0;) {
result.push(allNums.splice(Math.floor(Math.random() * allNums.length), 1)[0])
}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

前端CSS面试题文档,JavaScript面试题文档,Vue面试题文档,大厂面试题文档,需要的读者可以戳这里免费领取!

]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

[外链图片转存中…(img-enZesg2B-1713730670663)]

最后

前端CSS面试题文档,JavaScript面试题文档,Vue面试题文档,大厂面试题文档,需要的读者可以戳这里免费领取!

[外链图片转存中…(img-NbfzWSzf-1713730670663)]

[外链图片转存中…(img-RrkIetbS-1713730670664)]

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值