Moka前端面试

1,200 阅读7分钟

【持续更新,更多内容查看面试专栏

moka是一家saas业务的公司,主要为其他公司提供服务(招聘+人事管理系统)等。

一面和二面的流程大致一样,分为四部分(面试官上来就会介绍面试流程,我觉得还挺有礼貌的)。面试官会说,如果某一个环境觉得不ok,他们可能就会终止面试。

  • 自我介绍
  • 算法题
  • 面试官提问
  • 是否有问题问面试官

这家公司

一面

时间:20220517
状态:通过

自我介绍

简单问了一些项目

算法题一:判断区间是否重叠

function isOverlapped(a, b) {
    const {start: start1, end: end1} = a;
    const {start: start2, end: end2} = b;
    if(end1 < start2 || start1 > end2) {
        return false
    }
    return true;
}

const a = {start: 2, end: 5};
const b = {start: 4, end: 8};
const c = {start: 6, end: 9};

isOverplay(a, b); // true
isOverplay(a, c); // false

算法二:合并区间

function merge(times) {
    const arr = times.sort((a, b) => a.start - b.start);
    const res = [];
    for(let i = 0, l = arr.length; ) {
        let {start: curStart, end: curEnd} = arr[i];
        let j = i + 1;
        for(; j < l; j++) {
            const {start, end} = arr[j];
            if(start > curEnd) {
                break;
            }
            else {
                curEnd = end;
            }
        }
        i = j;
    
        res.push({
            start: curStart,
            end: curEnd
        })
    }
    return res;
}

merge([{start: 3, end: 5}, {start: 9, end: 11}, {start: 4, end: 8}]);
// [{start: 3, end: 8}, {start: 9, end: 11}]

flex:0 1 auto 的含义是啥

flex是一个复合样式属性,
flex-grow:内容超过flex-base的时候,是否往外延伸,0表示不延伸,1表示延伸
flex-shrink:内容没有填充满flex-basis,其余部分空间不够时,内容是否压缩,0表示不压缩,1标识压缩
flex-basis:前两者变化的基准宽度,如果设置了width,也设置basis,basis的优先级更高

详细分析:FLEX布局

如何覆盖加了important的样式

面试给出的答案:在本身的class后面,再写个class,后面的class会覆盖上面的样式,如下

<style>
    .wrap {
        background-color: bisque !important;
    }
    .wrap {
        background-color: red !important;
    }
</style>

面试官:这种后面就更不好维护了,可以用js设置样式的方式。
我: js设置样式不是设置的行内样式吗?行内样式也无法覆盖important
面试官:这和是否设置行内样式没关系,是因为文档流加载的先后问题,所以能覆盖,你可以下去试试。

我下来尝试后,没找到怎么用js覆盖,知道可以评论下

防抖和节流,以及应用场景

防抖:把多个触发合并为一个事件进行处理。典型实践:输入框的实时搜索。 节流:一段时间内只触发一次时间处理。典型实践:用户点击按钮,防止用户误触发

浏览器内核

浏览器内核
IEtrident
GoogleBlink
SafraiWebkit
FirefoxGecko

简单介绍下Event Loop

因为js是单线程,请求其他资源是需要消耗时间的,为了提高对本地资源的充分的利用,此时线程会将这个任务挂起,先去执行其他任务,这个就是我们所说的异步任务,而异步任务中也分为 宏任务和微任务。
js代码本身也是一个宏任务,从上而下执行,遇到宏任务(如setTimeout)会将他们放在宏任务待执行队列中,遇到微任务队列(如promie.then)会放在微任务执行队列中,本来执行完成后,会先去查看微任务队列中,有就执行,没有就看看是否需要执行渲染,如果有就先执行渲染。渲染完成之后,再看看宏任务是否有需要执行的。

详细解答:Event Loop

从什么时候准备面试,是不是拿到合适的offer就离职

二面

时间:20220520
状态:通过
时长:一个半小时
他们公司效率挺高的,面试完,可能接近半个小时,就告诉我通过,和我约hrbp面试

面试官介绍

面试官自我介绍了下,说了面试流程,简单说了下面试的流程,让我简单自我介绍下,项目后面会提问

自我介绍

算法题1,

实现一个分也器

/**
 * 支持固定展示边界
 * curr左右页数足够的情况下,prev展示4页,next展示4页
 * pre展示的页数和next展示的页数尽可能的等于8页(在总页数足够的情况),
 * 例如prev展示1页,total足够的话,next最多展示7页。
 * 
 * ,
 * 对已有的页数进行分割,如下面总页数7
 * 
 * total = 7 curr = 6
 *         curr
 *           |
 * 1 2 3 4 5 6 7 
 * ---------   --
 *     |        |   
 *    prev     next 
 * 
 * total = 10, curr = 2
 *  curr
 *    |
 *  1 2 3 4 5 6 7 8 9 10
 *  -   -------------
 *  |       |   
 *  prev    next 
 * 
 * total=12, curr = 5
 *            curr
 *            |
 *  1 2 3 4 5 6 7 8 9 10 11 12
 *    -------   --------
 *       |        |   
 *      prev    next 
 * 
 */

 function splitV1(total, curr){
     if(curr > total) {
         throw new Error('params is error')
     }
    const arrTotal = 8;
    const arrLen = arrTotal / 2;
    let res = Array.from({length: total}, (x, index) => index + 1);
    let currIndex = curr - 1;
    let nextTotalNums = total - curr;
    let prev, next;
    
    // 计算在数组中的位置 比如 97页 在数组中索引的 96位
    let preStart = curr - arrLen - 1, nextEnd = curr + arrLen;
    
    console.log(arrTotal, nextTotalNums)
    if(currIndex < arrLen) {
        preStart = 0;
        
        // currIndex + 1 + (arrTotal - currIndex)
        nextEnd = arrTotal + 1;
    }
    else if(nextTotalNums < 4) {
        preStart = Math.max(0, currIndex - (arrTotal - nextTotalNums));
        console.log(preStart, arrTotal, nextTotalNums)
        nextEnd = total;
    }
    prev = res.slice(preStart, currIndex);
    next = res.slice(currIndex + 1, nextEnd);
    return {
        prev,
        curr,
        next,
    }
}
// test case
console.log(splitV1(12, 6)); // { prev: [2, 3, 4, 5], curr: 6, next: [7, 8, 9,10]}
console.log(splitV1(10, 5)); // { prev: [1, 2, 3, 4], curr: 5, next: [6, 7, 8,9]}
console.log(splitV1(5, 2)); // { prev: [1], curr: 2, next: [3, 4, 5]}
console.log(splitV1(10, 2)); // { prev: [1], curr: 2, next: [3, 4, 5,6,7,8,9]}
console.log(splitV1(100, 97)); // { prev: [92,93,94,95,96], curr: 97, next: [98, 99, 100]}

算法题2

实现first、prev、curr、next、last的分野器,上一个题的加深难度

/**
 * 支持固定展示边界
 * 
 * 除了prev, curr, next,额外增加了2个边界first, last,分别代表第一页和最后一页
 * 由于多了first和last,因此prev和next各需要减少1页,默认展示2页
 * 例如(不包括省略号):
 *            curr
 *             |
 * 1 ... 3 4 5 6 7 8 9 ... 12
 * -     -----   -----     --
 * |       |       |       |
 * first  prev    next    last
 * 
 * 如果first和prev紧挨着,则first需要合并入prev中,即first为空(用-1表示)
 * 例如(不包括省略号):
 *        curr
 *         |
 * 1 2 3 4 5 6 7 8 ... 10
 * -------   -----     -
 *    |        |       |
 *  prev      next    last
 * 
 * 如果last和next紧挨着,同理操作
 */

function splitV2(total, curr){
    let res = Array.from({length: total}, (x, index) => index + 1);
    let currIndex = res.indexOf(curr);
    let arrTotal = 6;
    const arrLen = arrTotal / 2;
    const nextLen = total - currIndex;
    let prevStart = currIndex - arrLen, nextEnd = currIndex + arrLen + 1, first = -1, last = -1;
    if(currIndex < arrLen) {
        prevStart = 0;
        // currIndex + 1+ (arrTotal - currIndex)
        nextEnd = arrTotal + 1
    }
    else if(nextLen < arrLen) {
        prevStart = Math.max(0, currIndex - (arrTotal - nextLen));
        nextEnd = arrTotal + 1
    }
    let prev = res.slice(prevStart, currIndex);
    let next = res.slice(currIndex + 1, nextEnd);

    if(prevStart > 1) {
        first = res.at(0);
    }
    else if(prevStart === 1) {
        prev.unshift(res.at(0))
    }

    if(nextEnd < total - 1) {
        last = res.at(-1);
    }
    else if(nextEnd === total - 1) {
        next.push(res.at(-1))
    }
    // TODO
    return {
        first,
        prev,
        curr,
        next,
        last,
    }
}

// test case
console.log(splitV2(12, 6)); // { first: 1, prev: [3, 4, 5], curr: 6, next: [7, 8, 9], last: 12 }
console.log(splitV2(10, 5)); // { first: -1, prev: [1, 2, 3, 4], curr: 5, next: [6, 7, 8], last: 10 }
console.log(splitV2(5, 2)); // { first: -1, prev: [1], curr: 2, next: [3, 4, 5], last: -1 }

说说你觉得比较满意的项目

【此处省略,避免遇到熟人,哈哈哈哈】
我说了一个去年我负责的,多人合作的项目

从产品角度介绍下你负责的业务

如何维护一个sdk

【问这个问题,主要是我前面提到了一个插件的开发】

  • 如果是一个通用的sdk中的模块,由sdk的owner负责
  • 如果是和业务强关联的模块,由业务模块的负责人负责,但是修改方案,已经代码规范性有owner同意把关

如何管理项目

如果一个比较大型的项目,开发一段时间后,发现进度不理想,应该怎么处理

  • 首先,需要一起总结,为什么进度不理想,找出原因,并找出解决方案
  • 对剩下的需要完成的功能进行拆分,并且将完成进度与时间点对齐,严格遵守在某个时间我们需要完成什么功能
  • 作为项目主要负责人,还需要跟进协同伙伴的进度,关注大家的进度,以及大家的是否有遇到问题

说说你解决过的bug

【尽量说有难度,后续如何避免此类问题,体现出你的设计与项目的统筹】

还和我聊了一些性能优化的事儿

有什么问题要问

此时,我电脑已经没有电了,只能说没有什么问题。
面试官:是否没有了解过B端和C端
我:简单了解过,但是可能不是很深入,你可以简单讲下吗?
面试官:……,B端相对C端会更加的复杂,就那上班打卡这个,就可能存在多种情况,比如晚来晚走,早来早走中等情况,需要不同的验证。……。做B端不仅仅要求我们把代码写正确,还需要我们对产品有一定的了解,对一个东西的开发,可能需要我们不断的与产品、测试商量,最后确定方案。……。【10分钟过去了】
我:哦,我懂了,谢谢【内心一直在担心我的电脑会不会突然关机】

备注

二面主要聊了很多项目,不只上面的问题,我记得的大概就一些比较主要的点,祝大家面试顺利

写在最后

祝大家面试顺利,拿到想要的offer