2022年4月前端面经

1,593 阅读12分钟

笔者上一段工作在某大厂工作约3年,遇到了一系列困惑和成长阻塞。所以不得不换了份工作(在之前的工作一直拿到较好的绩效,并非被排挤、pua等)

工作期间比较疑惑的几个问题

  • p8\p7 应该是什么样的能力?(我看到的周围高T大多数技术方面很水,抓住了风口跟随公司成长了上来,不否认格局、认知很牛,但是从技术的角度来看对团队几乎没有任何正向作用)。这会给我们带来恶性循环:「我们看不到优秀的大佬应该是什么样的,自身不能良性成长」

  • 学习技术是不是只能依赖github or 刷题。 笔者花了半年多时间去研究vue的源代码,并且成功提了一些pr、后面也陆续给其他知名开源项目写了一些pr、跟风刷type-challenge, 目前可以无压力通关。but发现自己并不能创造价值(我写了这么多代码,那我能不能创造某个工具) 最开始自认为基本上可以。后面发现了肯定的答案:不能。 当你看过了evan、antfu等一系列大牛后才知道自己的战斗力多弱。

  • 试着找领导沟通 团队在做什么技术产品?低代码(PS: 场景匹配低且实现效果一般)、组件库(PS:有自嗨倾向且难度系数低)、rn探索(emm?...)

综上所述,如果你遇到了同样的问题,那么你一定不要犹豫,不要浪费时间,抓紧run。

某乎 emm...

埋点做过吗,如何实现的

跨域是怎么处理的

跨域方法有哪些

如果浏览不支持cors该怎么做呢?(这个我还真不知道,说了通过jsonp\nginx解决?)

ssr有了解过吗

绑定事件最后一个参数是什么?源事件了解过吗*(高难度问题)*

代码题:
// 1.使用 React 类组件或函数组件实现一个登录表单,要求:

// 含有一个名为 username 的输入框,它的值会随着用户输入实时更新

// 含有一个名为 password 的输入框,它的值会随着用户输入实时更新

// 含有一个提交按钮,点击此按钮后在控制台打印出已填写的用户名和密码值
代码题:
2.使用原生 JS 实现一个屏幕居中的弹窗。

Click

要求:

点击此按钮后屏幕中央出现一个弹窗

此弹窗水平和垂直方向都居中,弹窗内的文本内容是按钮的文字,即“Click”。

弹窗出现3秒后自动消失

不建议、感觉技术方面emmm

so app

vue3有哪些优化

性能优化你了解哪些

聊了大约20分钟,面试官人很好,但不推荐

小红书

一面(1.1h) 超级nice的面试官,交流了一些关于vue的思考

你做过的最重要的项目

移动端性能优化措施

vue为什么需要注册为component,而react不需要,就能直接使用(组件大小写)

什么是csrf。csrf的解决方式有哪些

低代码实现方案

vuex的缺点

react的理解有多少

ts支持

为什么vue的组件需要注册component才能使用,react怎么做到不需要注册也可使用

写代码

快排

二面(1.1h)

部门负责人

    [[1,2],[2,3],[4]]
    判断两个点之间的路径有多少种,以及最短距离是多少
    function(entry: [number, number]) {
        return [
            number, // 路径数量
            number, // 最短距离
        ]
    }
    
    我写的:dfs + 记忆化搜索

项目相关的问题..

离职的原因,然后还有就是你期望的环境是什么样的(这个问题回答的不好)

三面(0.7d)

大团队负责人

之前做过的项目有哪些。

一些细节的实现方法等

感受:可考虑,三面留下了差的印象。感觉不太nice的大老板。但是一面 + 二面都不错

某虾

这个小可爱的问题深度无语

一面(1.5h)

缓存你用过哪些,怎么用的?

localstorage你是怎么使用的,具体点,带实际经历场景等说**(不管是啥都要问你具体具体是怎么用的)**

http1、http2、http3的区别

url全链路过程

一道promise流程题,没记录下来

动画方案哪些,你是怎么用的

vue在2和3里,执行<div v-if="text"><div> text =false; 全流程

postmessage你是怎么用的

service-worker你是怎么用的

cookie你是咋怎么用的

nginx缓存你是怎么用的,用哪个字断、怎么配置

跨域你是怎么解决的,分别用哪个字段,怎么配置

二面

给你一个promise队列,分别捕获在前12345...n个promise都结束的结果

比如
excute([promise1, promise2, promise3]).then((data) => {
    console.log(data)
})
// 前N个都结束啦
// 前3个都结束啦
// 前2个都结束啦
// 前1个都结束啦
main().catch((e) => {
    console.log('top', e) // top  networkerror
})

async function main() {
    try {
        loadImage() // uncaught promise
        loadConfig()
    } catch (e) {
        console.log('main', e) /// main  logicbug
    }
}
function loadImage() {
    return new Promise((resolve, reject) => {
        setTimeout(reject, 1000, 'networkerror')
    })
}
async function loadConfig() {
    throw 'logicbug'
    await wait()
    console.log('configok')
}

function wait() {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, 1000)
    })
}

某里(1.1h)

一面

好像都是项目相关的问题,自我反馈出来的情况:

1、做项目的话需要深度

2、你对你期望的团队

二面

项目

业务,对业务的理解

三面

项目

monorepo的好处

新建一个活动、可提炼的能力有哪些

关于线上流程的稳定性

如何判断用户问题

整体不建议去: 没有实际的代码问题、后面逐渐感觉到了pua的氛围

JERRY.AI(1h)

一面

--
Problem Set below:
// Task: Implement a class named 'RangeList'
// A pair of integers define a range, for example: [1, 5). This range
includes integers: 1, 2, 3, and 4.
// A range list is an aggregate of these ranges: [1, 5), [10, 11), [100,
201)
/**
*
* NOTE: Feel free to add any extra member variables/functions you like.
*/
class RangeList {
/**
* Adds a range to the list
* @param {Array<number>} range - Array of two integers that specify
beginning and end of range.
*/
add(range) {
// TODO: implement this
}
/**
* Removes a range from the list
* @param {Array<number>} range - Array of two integers that specify
beginning and end of range.
*/
remove(range) {
// TODO: implement this
}
/**
* Prints out the list of ranges in the range list
*/
print() {
// TODO: implement this
}
}
// Example run
const rl = new RangeList();
rl.add([1, 5]);
rl.print();
// Should display: [1, 5)
rl.add([10, 20]);
rl.print();
// Should display: [1, 5) [10, 20)
rl.add([20, 20]);
rl.print();
// Should display: [1, 5) [10, 20)
rl.add([20, 21]);
rl.print();
// Should display: [1, 5) [10, 21)
rl.add([2, 4]);
rl.print();
// Should display: [1, 5) [10, 21)
rl.add([3, 8]);
rl.print();
// Should display: [1, 8) [10, 21)
rl.remove([10, 10]);
rl.print();
// Should display: [1, 8) [10, 21)
rl.remove([10, 11]);
rl.print();
// Should display: [1, 8) [11, 21)
rl.remove([15, 17]);
rl.print();
// Should display: [1, 8) [11, 15) [17, 21)
rl.remove([3, 19]);
rl.print();
// Should display: [1, 3) [19, 21)

我的答复

type IRange = [number, number];
type Mode = 'add' | 'remove';

// 把当前格式tuple转 array
type WriteAbleTuple<T> = (T extends Array<any> ? (T[number] extends readonly [infer A, infer B] ? [A, B] : never) : never)[];

class RangeList {
    list: IRange[] = [];

    /**
     * @description: 与已有空间不存在交叉合并的情况
     * @param {[number, number]} input
     * @param {add | remove} mode
     * @return {null | number} idx
     */
    private filterInvalid = (input: IRange, mode: Mode = 'add') => {
        const [from, end] = input;
        if (input[0] === input[1]) {
            // 去掉相同输入
            return null;
        }
        let idx = this.list.findIndex((item) => item[1] >= from);
        if (!~idx) {
            // 最右边的case
            if (mode === 'add') {
                this.list.push([from, end]);
            }
            return null;
        }
        if (end < this.list[idx][0]) {
            if (mode === 'add') {
                // 直接插入的case
                this.list.splice(idx, 0, [...input]);
            }
            return null;
        }

        return idx;
    };

    /**
     * @description: add调用,让input区间合并到this.list[idx]上
     * @param {[number, number]} input
     * @param {number} idx
     * @return {void}
     */
    private merge = (input: IRange, idx: number) => {
        const idxRange = this.list[idx];
        idxRange[0] = Math.min(idxRange[0], input[0]);
        idxRange[1] = Math.max(idxRange[1], input[1]);
    };

    add = (range: IRange) => {
        // 找到第一个, 右小于等于from的; 后mixin
        let idx = this.filterInvalid(range);

        if (typeof idx === 'object') { // null
            return this;
        }
        const finalPoi = this.collect(range, idx);

        this.merge(range, idx);
        this.merge(this.list[finalPoi], idx);
        this.list.splice(idx + 1, finalPoi - idx); // 删除掉中间位置
        return this;
    };
    print = () => {
        console.log(JSON.stringify(this.list).replace(/\](?!$)/g, ')'));
        return this;
    };

    /**
     * @description: 从idx位开始收集,找到所有的区间内数组
     * @return {number} 最后一位相关数组
     */
    private collect = (input: IRange, idx: number) => {
        const end = input[1];
        const len = this.list.length;
        for (let i = idx; i < len; i++) {
            const item = this.list[i];
            if (item[0] > end) {
                return i - 1;
            }
        }
        return len - 1;
    };


    /**
     * @description: 同理于merge
     */
    private detach = (input: IRange, idx: number): IRange[] => {
        const [from, end] = input;

        const appendRanges = ([
            [
                this.list[idx][0],
                from
            ], [
                end,
                this.list[idx][1]
            ]
        ] as const).filter(item => item[1] - item[0] > 0)

        return appendRanges as WriteAbleTuple<typeof appendRanges>
    };

    remove = (range: IRange) => {
        let idx = this.filterInvalid(range, 'remove');
        if (typeof idx === 'object') {
            return this;
        }
        const finalPoi = this.collect(range, idx);

        const appendRanges = (finalPoi === idx) ? this.detach(range, idx) : this.detach(range, idx).concat(this.detach(range, finalPoi));
        this.list.splice(idx, finalPoi - idx + 1);
        this.list.splice(idx, 0, ...appendRanges);
        return this;
    };
}


const l = new RangeList();

l.add([1, 5]);

l.add([10, 20]);

l.add([20, 20]); // [1, 5) [10, 20)

l.add([20, 21]); // [1, 5) [10, 21)

l.add([2, 4]); //  [1, 5) [10, 21)

l.add([3, 7]); //  [1, 7) [10, 21)

// l.add([8, 10]); // [[1,7),[7,21)]

l.remove([10, 10]); // [1, 7) [10, 21)

l.remove([10, 11]); // [1, 7) [11, 21)

l.remove([15, 17]); // [1, 7) [11, 15) [17, 21)

l.remove([3, 19]); // [1, 3) [19, 21)

l.print();

二面

代码题 40分钟2道M+的题 。比较challenge 大概28分钟做完,剩下10分钟做一些优化

这个题没给出最优解,「最优解基于位运算」

// 给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。
// 返回被除数 dividend 除以除数 divisor 得到的商。
// 整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2
// 输入: dividend = 10, divisor = 3
// 输出: 3
// 解释: 10/3 = truncate(3.33333..) = truncate(3) = 3

二分查找,给出了最优解

// 整数数组 nums 按升序排列,数组中的值 互不相同 。
// 在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
// 给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
// 示例:
// 输入:nums = [4,5,6,7,0,1,2], target = 0
// 输出:4

第一题没给出最优解,面试官并没有难为我

三面

讲了一些项目的东西

然后场景题:设计一个微博列表滚动

1、前后端交互应该有哪些内容

2、分页时候,用户刷新了数据,后端应该怎么做保证不重复。

3、页面数据量大,滚动的一些方式。

4、不同的分页方案的差别是什么

其他的忘了?

四面

为什么Set的复杂度是O(1), 如何做到的

treemap和hashmap的实现以及区别

http1、2、3区别。 3中如何实现的快速握手。

dns解析方式

还有一堆很难得好问题,忘了,gan!!!!

大概就是:很算法、很难

五面

cto面试,他基本都是英文,聊一些经历之类的。

总结: 强烈推荐,我没去是因为自身想再国内卷一卷。 每个面试官都超级无敌nice。最后一面是王国辉大佬,真大佬,那边纯js做前后端,应该有很多机会和挑战

微软

pre

1、改错题,忘了

2、给一个数组,每一位是字符串,找出可以构成的无重复最长字符串['abc','cdf','pe']最长就是abcpe

3、忘了,大概是找出操作次数最少的,可以让字符串成为唯一字符的次数

4、给一个二进制的数字, '111000'. 要么处以2要么减1, 找到操作最少的次数。 (这个题有性能测试,目前没找到有效答案能过性能测试,40万个1),所以扣分了

找到了解决方案,基于bigInit来做, BigInt(2 ** 53).toString(2)

5、找到给定字符中最短的匹配字符串(要求如果有它的大写,就应该有它的小写)

一面 nice面试官

1、聊项目

2、网络的dns了解有多少

3、cdn了解有多少

4、dns解析过程

6、请求太多怎么优化、一个页面的img太多了有什么解决方案

7、埋点怎么控制show事件只执行一次

8、map和weakmap的区别

9、1px问题解决方式

10、gpu底层怎么工作的

代码:
已知fx递增,求它的反函数(调用fx,然后二分法。核心在于分析递增区间)

二面

项目相关,然后介绍了angular和react的一些数据管理手段

代码题:

判断是不是搜索二叉树(写出来了,但是严重被绕晕了,延伸到中序遍历相关的知识)

三面

ts的作用

范型

实现ipv4的判断
实现ipv6的判断
const replaceStart = (str) => {
    if (str === '0') {
        return true;
    }
    if (str.replace(/^0*(^0)/g, '') !== str) { // 这块直接reg.test就行,这么写麻烦了
        return false;
    }
    return true;
};

function checkIp6(str) {
    const arr = str.split(':');
    if (arr.length !== 8) {
        return false;
    }
    return arr.every(item => {
        if (item.length !== 4) {
            return false;
        }
        return item.split('').every(v => {
            const val = parseInt(v, 16);
            return val >= 0 && val <= 16;
        });
    });
}
'2001:0db8:85a3:0000:0000:8a2e:0370:7334';
console.log(
    checkIp6(
        '2001:0db8:85a3:0000:0000:8a2e:0370:7334'
    )
);

这个用ts体操来实现也算是个ts入门题

四面

一些场景题;
有点记不住了
分享一些好东西:团队鼓励开源参与、团队做e2e和test、下班6点?

五面

忘了要面试,面试官等了我50分钟....................

1、项目为主

分享一些关于团队评估。

当场给了通过sde2,感恩。

总结:所有面试官+hr都是无敌nice,hr叫chufan,给的薪酬整体也很满意,总包较大,强烈推荐,和网上传言的差别很大

字节

一面 体验不佳

感觉面试官是个中级工程师,水平不高,更适合考察校招生

说一说从原生js到现在,你对前端发展趋势的理解

对其他语言的了解

学习方法

class 写声明函数和普通函数的区别(问的是class中的箭头函数的区别... )

js的执行机制 (问的是预编译相关,没理解到 )

还存在一些我感觉并不专业的「专业术语」,记不清了

1、寻找最长不重复字符串

没写最优解,大约1分钟完成。直接口述了最优解。因为很简单没必要深究

2class Sceduler{}

这个题网上有很多,感觉并不难写,就不记录了

这个题描述有问题,没说返回,说的是输出,它的函数就是console.log。导致gap,然后写了3个版本的,最终我也不确定到底啥意思。

后来网上看了一下,其实这个题目本身还不错

3、实现promise\promise.all

promise简单写了一下,问题很多,面试官没要求去优化,比较好说话

promise.all 由于写过,3分钟输出

二面

怎么判断线上的手机白屏

离线包如何判断优先级

从前端视角去推动业务优化

如果觉得lottie-js的资源体积太大,如何去优化这个问题呢

首页怎么优化

性能优化的方案有哪些

动画的手段优劣点

js怎么实现的调用bridge流程知道吗

自动生成埋点代码怎么实现的

如何知道一个页面的访问成功率(进来一半就退了)

// ... 还有一些记不住了, 但是都是特别好的问题,通过面试带来思考,起码时间没有浪费掉, 大家感兴趣可以私信我,让这个面试官给你二面

// 需求看作包,转化为0-1背包问题

方案1、dp。结构大概如下:第0位表示做当前需求、第一位表示不做当前需求

[

{

acc: 0,

user: [1,0,0]

},

{

acc: 1,

user: [1,2,0]

}

]

方案2、贪心

按照需求不需要人数排序,比如一个需求需要人数为0,排到最前面,然后「优先支持需要人数少的需求」

三面

项目相关

假设某个项目想做公司平台要考虑哪些,以及怎么去做

react、vue、zepto等选型问题

react和vue性能对比 。

埋点平台功能哪些

jsb、axios、fetch进行封装、支持实例、拦截等。 写的并不好,因为太大了,但是题目很好

四面

全是业务

总结:二面体验最佳。整体体验不是最佳的(不如微软),但是这边会做很多深度复杂的业务。可以有机会学习到很多知识

重点来了

经过笔者入职2个月的观察,对当前的团队给予高度满意的评价。(确认了解团队,不想为了内推而坑人!!!极其厌恶为了内推无底线坑的人)

  • 大家和部门领导日常交流完全对等(日常开领导玩笑等等)
  • 深度跨端,和架构团队合作,比如:
    • lynx react第一个版本是我们这边写的
    • 直播容器能力开发
    • 前端做安卓、ios开发(不会有前端和客户端互相推责任的问题)
    • 比如webview预热、bridge重构等均是我们自己定制
    • nsr、prefetch、snapshot、ssg等
    • and so on
  • 没部门没有小组群。只有一个部门群(40人+)
  • 几个小组长都极其的好,并且技术上有很多创意的想法

来这边学习到了很多跨端的真正概念。也第一次去想rn为什么性能差(我一直以为rn很牛逼)。我的下一份工作基本不会再考虑国内大厂了。

特别强调

部门的差异性比公司的差异性还大、我只是分享了我遇到的面试经历,不代表该公司其他部门。切勿无脑黑

推荐的公司

  • jerry.ai 是一家美国外企。我觉得很有前景,技术水平应该也不错。
  • ms的hr chufan真的很nice,大家可以联系她。职业生涯遇到的最佳体验都在微软这个部门。

其他感受

  • 面试没遇到刚ts体操的问题
  • 外企要多刷算法
  • 面试阶段都觉得不match的千万不要考虑
  • 优秀的人比你想象的更多