一面
关于项目:
常规问题:
webpack 打包细节
- commonjs 与 esmodule 是如何相互转换(这里不会)
 
 
Task 与 Microtask 的区别, 以及时序
详细说明可以见: https://html.spec.whatwg.org/multipage/webappapis.html#queuing-tasks
简单的说就是每次执行task之前会把microtask都处理掉。在处理过程中加入的microtask也会按照顺序处理掉
 
闭包问题:
1 2 3 4 5
   | for (var i = 0; i < 5; i++) {   setTimeout(() => {     console.log(i);   }, 1000); }
  | 
 
结果是什么? (我回答了5个4, 其实是5个5..扶额)
然后问如何让其输出 0 1 2 3 4
解法一:
1 2 3 4 5 6 7
   | for (var i = 0; i < 5; i++) {   (function (_i) {     setTimeout(() => {       console.log(_i);     }, 1000);   })(i); }
  | 
 
解法二:
1 2 3 4 5
   | for (let i = 0; i < 5; i++) {   setTimeout(() => {     console.log(i);   }, 1000); }
  | 
 
我没有说出解法二, 想了半天想出了一个解法三。。
1 2 3 4 5 6 7
   | for (var i = 0; i < 5; i++) {   Promise.resolve(i).then((i) => {     setTimeout(() => {       console.log(i);     }, 1000);   }); }
  | 
 
 
中文数字转阿拉伯数字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
   | 
  const chineseNumMap = {   一: 1,   二: 2,   三: 3,   四: 4,   五: 5,   六: 6,   七: 7,   八: 8,   九: 9, };
  const unitMap = {   千: 1000,   百: 100,   十: 10, };
  function parseChineseNumWhenLowerThan10K(chineseNum: string) {   let res = 0;   let hasNum = false;   let currentNum = 0;   for (const s of chineseNum) {     if (hasNum === false && chineseNumMap[s]) {       res += chineseNumMap[s];       hasNum = true;       currentNum = chineseNumMap[s];     } else if (hasNum === true && unitMap[s]) {       res -= currentNum;       res += currentNum * unitMap[s];       hasNum = false;       currentNum = 0;     }   }
    return res; }
  function chineseNumToInt(input: string): number {   const chineseNumArr = input.split("万");   let res = 0;
    res += parseChineseNumWhenLowerThan10K(chineseNumArr[0]);
    if (typeof chineseNumArr[1] === "string") {     res *= 10000;     res += parseChineseNumWhenLowerThan10K(chineseNumArr[1]);   }
    return res; }
  console.log(chineseNumToInt("五千三百万零五百零一"));
 
  | 
 
 
二面
一面过后第二天 HR 就打电话过来约二面,就效率方面来说还是非常迅速的
二面没有考察具体代码,主要是问了问项目方面的问题。
比如公司项目的架构,公司团队协作方面是如何协作的,除了平时工作之外有没有做什么其他的事情,有自己的项目么,自己的项目由什么亮点难点这种比较抽象的问题。
其中可能也有混入价值观方面的问题。总之需要注意一下。
然后我这面被刷了,第三天收到了感谢面试的信。至少有个反馈,从这点来看字节做的还是非常不错的。
再战
又被别的部门捞起来了, 之前是飞书, 现在是抖音部门, 就再战一次呗。
一面
- 聊聊项目
 
- esmodule 和 commonjs 的区别
 
- webpack 打包原理
 
- nodejs 的 event loop
 
- nodejs 的使用 microtask 和 task 的时机
 
算法题
路径总和:给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
   | class TreeNode {   left: TreeNode | null = null;   right: TreeNode | null = null;   value: number; }
  function getTreePathSum(node: TreeNode, targetSum: number): boolean {   if (!node) {     return false;   }
    if (targetSum - node.value === 0) {     if (node.left === null && node.right === null) {              return true;     }
         }
    return (     getTreePathSum(node.left, targetSum - node.value) ||     getTreePathSum(node.right, targetSum - node.value)   ); }
  const root = new TreeNode(); root.value = 1; const left = new TreeNode(); left.value = 2; const right = new TreeNode(); right.value = 3;
  root.left = left; root.right = right;
  console.log(getTreePathSum(root, 5));
  | 
 
二面
- js有哪些类型
number, boolean, string, object, undefined, symbol 
- 以上是我的回答, 是错误答案,正确答案应该是 
string, number, bigint, boolean, undefined, symbol, null. (Reference: https://developer.mozilla.org/en-US/docs/Glossary/Primitive) 
 
- js中基本类型和引用类型分别存在哪里
 
- 如何获得地址输入 - 缓存 - 加载模块 - 渲染 - 加载请求 - 渲染可交互 这个过程中各个区域的时延
 
- 如何设计一个全链路的监控体系
我的回答:1 2 3 4 5 6 7 8 9 10
   | 1. 监听所有的用户事件   并区分重点事件,和普通事件。 2. 全局捕获错误信息。并将错误信息上报,以及错误信息所在的堆栈信息。         如果可能的话,同时发送相关的dom接口与网络请求,以及截屏。         如果是无法复现的一些线上问题,内置一个web录屏工具,在开启一个flag后录屏并将录屏信息上传(rrweb) 3. 对用户的操作,比如RUM,等关键信息,以及用户的请求。划分不同的measure标准。并通过这些信息来衡量用户的痛苦程度。       资源是有限的,我们应当优先处理用户最痛苦的部分——即使用频率最高,相对痛苦程度最高的部分。 4. 增加有效的反馈机制,比如聊天机器人,比如报出异常时弹出反馈错误的模态框。或者引导用户到相关的社区。来尽可能让用户知晓我们会尽快解决问题。     同时能够收集来自用户的直接反馈,而这些是看log很难看到的信息。 5. 对线上服务进行监控。使用监控工具来检测各个关键服务,以及相对独立的服务的可访问性。以及相关的报警措施(比如钉钉消息)
   | 
 
- React Native 的热更新 / 部署
 
- 长列表 VirturalList
 
- React Native的事件推送
 
算法题
二面算法题难度急速升高, 反正我都没做过。
1 2 3 4 5
   | 用 Javascript 对象模拟 DOM 树,并实现它的 render 方法,通过调用该方法可以转成真正的 DOM 节点,例如我们已经实现了 element.js,通过 require('element.js'),我们可以定义 ul, 如下:
  function el(tag, props, children) {   return ... }
  | 
 
我的算法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | function el(tag, props, children) {   const el = document.createElement(tag);   for(let key in props) {       el.setAttrible(key, props[key])   }
    const childrenEls = children.map(item => {       if(typeof item === 'string') {           return item;       }
        return el(item.tag, item.props, item.children);   })   el.append(...childrenEls)
    return el; }
  | 
 
1 2 3 4 5 6 7 8 9
   | 给你一副类似于如下的地图:  0000000000  0010001011  000010E001  00S0000100  0000001000  1000000000
  初始时你在S的位置, 你可以向上下左右四个方向发射一枚子弹
   | 
 
我的算法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
   | const land = [   '0000000000',   '0010001011',   '000010E001',   '00S0000100',   '0000001000',   '1000000000' ].map((str) => {   return str.split('') });
  function findS(target: string): [number, number] {   for(let i = 0; i < land.length; i++) {     for(let j = 0; j < land[i].length; j++) {       if(land[i][j] === target) {         return [i, j]       }     }   } }
  function getLandSymbol(y: number, x: number): '0' | '1' | 'S' | 'E' | undefined {   return land[y][x] as any; }
  const size = [land.length, land[0].length]; const sPos = findS('S');
  function tryPath(step: number, currentPos: [number,number], vector: [number, number]): number {   step = step + 1;   const symbol = getLandSymbol(currentPos[0], currentPos[1]);   if(symbol === '1') {                         vector = [vector[1], -vector[0]]    }else if(symbol === 'E') {     return step;   }else if(symbol === undefined) {     return 999999;   }
    return tryPath(step, [currentPos[0] + vector[0], currentPos[1] + vector[1]], vector); } console.log(tryPath(0, sPos, [-1, 0])); 
   | 
 
三面
面试官上来就说,之前面了很多技术问题,我们来聊一聊项目吧。
大概问了问项目经历,工作经历,职业规划,如何协作,如何codereview,最近在学什么新技术等等看上去很随意的问题。
回头跟朋友们复盘了一下,其实是一道情商题,只有情商高的人才能答对,而我情商。。不高,就是一个憨憨。
如何面对这种软刀子题:
记住以下几点:
- 面试不是跟你谈心,作为候选人得揣测对方想听啥。
 
- 可以先说下自己有啥优点,经验。能为公司,部门,要做的事情带来什么转机。然后再夸一把公司,部门。说来虚心学习。
 
- 学会包装自己,技术是为业务服务的
 
三战
又换了个团队继续,直接从二面开始
二面
- 项目经历。项目是如何打包,如何发布的。
 
- 有使用过一些自动化工具来确保项目质量么。
 
- 有了解过
webpack打包原理么?你们的项目是用什么打包的 
- 了解过
lock文件么,升级单个依赖时会遇到什么坑 
- 看你的项目用到过
cypress, 你是如何解决cypress的下载问题的 
题:
1 2 3 4 5 6 7 8
   | 设计一个组件,用 React 编写,写出伪代码,有三个要求
  Input 组件 非受控 Validate:     1. 只接受英文字母
      2. 如果出现不合法的字符,就删掉
   | 
 
我的解法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
   | import React, {useState, useCallback} from 'react';
  interface Props {     initVal: string;     onChange: (newVal: string) => void; } const MyInput: React.FC<Props> = React.memo((props) => {     const [val, setVal] = useState(props.initVal ?? '');     const handleChange = useCallback((e) => {         const text = e.target.value;         const newVal = '';         for(let c of text) {             if(/[a-zA-Z]/.test(c)) {                                  newVal += c             }         }
          if(typeof props.onChange === 'function') {             props.onChange(newVal);         }
          setVal(newVal);     }, [props.onChange])
      return <input value={val} onChange={handleChange} /> }) MyInput.displayName = 'MyInput'
  | 
 
其实还有就是使用jquery时代的代码来进行处理,然后外面包一层react代码
但是我觉得不够react因此没有选择这个方案。
三面
杂七杂八的聊了聊,大部分都忘了,就记录一下记得的
- 讲讲你的开源项目
 
- 你的开源项目是怎么宣传的,取得了什么样的milestone
 
- 你的开源项目未来的发展是怎么样的
 
- 你公司的产品的基础架构是怎么样的
 
- 你对未来三年的职业规划是怎么样的
 
- 你希望做什么样的产品,是to b的还是to develop的
 
- 你如何实现一个需求,在用户不知道自己想要什么样的功能的情况下
 
- 你现在是965, 可以接受996么
 
一道题:
1 2 3 4 5 6 7 8 9
   | 实现 tom().eat('apple').play('football').sleep(5).eat('apple').play('football')
  输出: // tom // eat apple // play football // 停顿 5 s // eat apple // play football
  | 
 
我的写法, 其实不对。主要是一个sleep函数不太会写。求大佬给出正解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
   | function tom() {   return new Tom() }
  function Tom() {   this.p = Promise.resolve()   console.log('tom') }
  Tom.prototype.eat = function(name: string) {   this.p.then(() => {       console.log('eat ' + name);   })
    return this; }
  Tom.prototype.play = function(name: string) {   this.p.then(() => {       console.log('play ' + name);   })
    return this; }
  Tom.prototype.sleep = function(interval: number) {   this.p.then(() => {       return new Promise(resolve => {           setTimeout(() => {               resolve(1);           }, interval * 1000)       })   })
    return this; }
  | 
 
四战
直接三面
- 聊了聊项目
 
- 怎么做工程化
 
- 如何监控前端的性能
 
- 我们会做很多页面,如何提升开发体验
 
- 了解无头浏览器么?它内部的引擎是如何去计算代码的
 
题:
实现一个LRU:
我的回答:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   | class LRU {     list: string[] = [];     map: Record<string, unknown> = {};
      get(key: string): unknown {         const index = this.list.findIndex((item) => key === item);         if (index >= 0) {             this.list.splice(index, 1);         }         this.list.unshift(key);
          return this.map[key];     }
      set(key: string, value: unknown): void {         const index = this.list.findIndex((item) => key === item);         if(index >= 0) {             this.list.splice(index, 1);         }         this.list.unshift(key);
          if(this.list.length > 1000) {             const _key = this.list.pop();             delete this.map[_key];         }
          this.map[key] = value;     } }
  | 
 
我的不是一个优解。
建议正确的答案参考:
五战
又被教育部门捞起来了,反正我来者不拒继续面呗。
一面
- 一个空数组的原型链是怎么样的
 
- 用过flex么? 讲讲flex, 有那些熟悉,干嘛用的
 
- webpack_require esmodule commonjs, 他们的区别是什么。esmodule可以通过拼字符串来动态加载么
 
- function的构造函数与class的构造函数有什么区别
 
- 讲讲HTTP2解决了什么问题
- 二进制传输
 
- Header 压缩
 
- 多路复用
 
- server push
 
- TLS(虽然HTTP2不强制, 但是chrome, firefox 只支持tls的HTTP2)
 
 
- 讲讲箭头函数
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
   | inner = 'window';
  function say() {     console.log(inner);     console.log(this.inner); }
  var obj1 = (function() {     var inner = '1-1';     return {         inner: '1-2',         say: function() {             console.log(inner);             console.log(this.inner);         }     } })();
  var obj2 = (function() {     var inner = '2-1';     return {         inner: '2-2',         say: () => {             console.log(inner);             console.log(this.inner);         }     } })();
 
  say(); obj1.say();  obj2.say();  obj1.say = say; obj1.say();  obj1.say = obj2.say; obj1.say(); 
   | 
 
做算法题:
实现16进制加法,不能将两数直接转成十进制相加再转回十六进制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
   | var map = {     a: 10,     b: 11,     c: 12,     d: 13,     e: 14,     f: 15 }
  function parseToNumber(hex) {     if(hex === undefined) {         return 0;     }
      if(Object.keys(map).includes(hex)) {         return map[hex]     }else {         return Number(hex)     } }
  function hexAdd(hex1, hex2) {     const arr1 = hex1.split('')     const arr2 = hex2.split('')
      let addon = 0;     const result = [];     for(let i = 0; i < Math.max(arr1.length, arr2.length) + 1; i++) {         const num1 = parseToNumber(arr1[arr1.length - 1 - i])         const num2 = parseToNumber(arr2[arr2.length - 1 - i])
          let sum = num1 + num2 + addon;         addon = Math.floor(sum / 16);         let rest = sum % 16;         let r = String(rest)         if(rest >= 10) {             const [hex] = Object.entries(map).find(([key, val]) => {                 return val === rest             })
              r = hex         }
          result.unshift(r)     }
      return result.join('') }
  console.log(hexAdd('ff', '1'))
  | 
 
结果
最后还是没有成功拿到offer, 很遗憾,原因是【不太适合团队协作】(当然也有可能只是单纯的敷衍)。面试者除了提升自己的硬实力,还应当提升一下自己在职场的软实力,比如朋友推荐的这本书 软技能
六战
被飞书部门又拉起来了,继续
一面
- 聊一下项目
 
- 聊一下你对前端的优化的实践,达成了那些目的
 
- 聊一下在前端体验方面做的工作
 
- 聊聊vue和react的区别
 
- 聊聊如何处理中文输入法在输入过程中定时保存状态的问题
 
算法题
- 用css实现一个布局, 来实现一个3x3的布局,每个窗口都要实现16比9的比例
 
没做出来,关键词 padding-bottom百分比
实现: https://blog.csdn.net/weixin_39357177/article/details/81186498
- 实现一个方法,通过栈来存储,实现O(1)的复杂度下获取栈的最小值。
 
没做出来
框架
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | class MiniStack {   store = [];
    push(val: number) {        }
    pop() {        }
    getMini(): number {        } }
  | 
 
原题: https://leetcode-cn.com/problems/min-stack/
二面
- 聊聊公司的项目实现
 
- 了解http3么
 
- 聊聊react hooks
 
- 聊聊公司的业务规范与组件设计规范
 
- 聊聊业务代码
 
- 聊聊你是如何进行项目优化的
 
- 聊聊项目上线的流程
 
- 聊聊网络安全性方面的问题,做过xss用户输入防护么
 
- 聊聊你是如何关注用户的体验的,主要关心那些指标
 
- 让你设计一个倒计时组件,你会怎么设计
 
做题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
   | 
 
 
 
 
  function fixStr(str) {   let result = str;
    function loop(str) {     const len = str.length     const fixedStr1 = str.replace('y', '');     if(len !== fixedStr1.length) {              loop(fixedStr1)       return;     }
      const fixedStr2 = str.replace('xz', '');     if(len !== fixedStr2.length) {       loop(fixedStr2);       return     }
 
      result = str;   }
    loop(str)
    return result } console.log(fixStr('xxyyz')) console.log(fixStr('xxxyyyzzz')) console.log(fixStr('xyzwzyx'))
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
   | 
 
 
  function renderErrorPage() {    }
  function renderSuccessPage(resultMap) {    }
  const arr = ['a', 'b', 'c', 'd', 'e']; const successedResultMap = {};
  let errorCount = 0;
  Promise.all(   arr.map((url, i) =>     fetch(url)       .then((res) => {         successedResultMap[url] = res;       })       .catch(() => {         errorCount++;       })   ) ).finally(() => {   if (errorCount >= 4) {     renderErrorPage();   } else {     renderSuccessPage(successedResultMap);   } });
 
  | 
 
该问题可以使用Promise.allSettled进行简化
三面
问的和二面差不多,只是更加细一点
算法题在面试官的要求下不做公开
HR面
反正基本不挂人,主要是谈薪的问题。有一个比较难受的点就是字节的薪资是基于你上家薪资来的,或者说大部分大厂的薪资都是基于上家来的。所以比较好的做法是可以先去别的厂然后再来字节996。我就是基础薪资比较低的那种,最后也没感觉有太大的竞争力,比较菜。
结论
万万没想到,我最终还是拿到了Offer。虽然经历比较坎坷,但总不算完全浪费时间。总得来说跳槽这事三分靠运气七分靠实力,但绝对不要被眼前的利益限制住了自己的发展,主要还是要看这次跳槽在自己的职业生涯中能收获什么,而不要为跳而跳。