#字节跳动前端工程师面经# #前端工程师面经# #校招面经# #实习面经# 👈点击话题查看更多同类面经干货!每日面经精选,为你发掘牛客干货!
 先进行说明一下,本人刚刚大三结束,去年十二月的时候是投递了字节的视频架构的实习,共三轮技术面+一轮hr面,成功拿到offer实习了五个月。 今年秋招提前批是投了抖音架构,共三轮技术面+一轮hr面,已经成功拿到offer。 由于去年实习面经没有在牛客上发,所以跟着本次提前批面经一共八轮面试一起发了。 面经更多的记录我当时面试的思考和一些相关问题回答的切入点,可能会跟其他面经直接给答案有所不同,其实掌握了切入点再进行扩展就是一份好的回答了~  实习一面(50min)   面试我的的是一个二十出头的小哥哥,第一次面试我还是比较紧张的,还好小哥哥待人和蔼可亲,针对不会的问题并没有刁难我,反而给我不少提示(给点个赞)   首先是自我介绍,问了下我学习前端的途径和方法啥的,算是暖了个开场吧,然后是具体的问题。   1. ES6新特性说一下?   我首先说的是let、const,学长就直接让我详细说一下这两个。let、const有三个方面可说:   1.1 let、const声明的变量只在其所在的块代码内有效,隐式截取了作用域,有了块级作用域的概念。   1.2 暂时性死区,变量声明提升但在不会被赋值为`undefined`,不能在声明之前使用。   1.3 不允许重复声明。   然后给了两个例子说一下: const a = 1const a = 2console.log(a)const b = []b.push(1)console.log(b)       2. ES6之前的模块引入方式和区别         ES6之前模块引入主要是CommonJS和AMD两种。          然后学长让说明一下CommonJs和ES6模块引入的区别:          2.1. 首先,CommonJS导出值是**浅拷贝**,一旦输出某个值,模块内部的变化就影响不到这个值。而ES6导出是采用实时绑定的方式,是将其内存地址导出,导入是动态加载     模块取值,并且变量总是绑定其所在的模块,不能重新赋值。          2.2. ES6模块化导入是异步导入,CommonJS导入是同步导入。这跟ES6模块通常用于web端,而CommonJS用于服务器端有关。          2.3. CommonJS导入支持动态导入require(`${path}/xx.js`),ES6模块化导入不支持,目前已有草案。          2.4. ES6模块化会编译成require/exports来执行的。            3. 一道数组遍历          实现`[1,2,3] => [2,4,6]`,这个太简单了:      function fn(arr) {return arr.map(item => item * 2)}             4. 字符串算法题               对输入的字符串,去除其中的字符'b'以及连续出现的'a'和'c',例如:            'aacbd' -> 'ad'  'aabcd' -> 'ad'  'aaabbccc' -> ''可以使用正则:          function fn(str) {  let res = str.replace(/b+/g, '');  while(res.match(/(ac)+/)) {    res = res.replace(/ac/, '')  }  return res;}                   5. CSS9宫格                       问题:创建出CSS9宫格,3*3宫格,每个宫格的长宽未知,要求达到自适应并且精确分配。      学长特意说要精确分配,所以用1/3肯定是不行的,于是使用flex布局:          <!--HTML--><div class="container">  <div class="wrapper">    <div class="item"></div>    <div class="item"></div>    <div class="item"></div>  </div>  <div class="wrapper">    <div class="item"></div>    <div class="item"></div>    <div class="item"></div>  </div>  <div class="wrapper">    <div class="item"></div>    <div class="item"></div>    <div class="item"></div>  </div></div><!--CSS-->.container {  display: flex;  flex-direction: column;  flex-wrap: nowrap;}.wrapper {  display: flex;  flex: 1;  flex-direction: row;  flex-wrap: nowrap;}.item {  flex: 1;}                   6. 说下React虚拟DOM                虚拟DOM是用JS模拟的DOM结构,将DOM变化的对比放在JS来做。可以从虚拟DOM的两个优势点来说:                6.1 函数式的UI编程,即UI取决于数据。                6.2 打开了跨平台的大门。提供了统一的JS层对象,根据不同平台制定不同的渲染方式,带来了跨平台渲染的能力。                                7. 说一下diff算法                创建一个React元素树之后,在更新的时候将创建一个新的React元素树,React使用Diff算法对元素树进行比对,只更新发生了改变的部分,避免多余的性能消耗。                     主要是三个思想,可以从这三个谈:                      7.1. 永远只比较同层节点。                      7.2. 不同的两个节点产生两个不同的树。                      7.3. 通过key值指定哪些更新是相同的。                                            8. NodeJS中的事件循环:              process.nextTick(function() {  console.log('a')})process.nextTick(function() {  console.log('b')})setImmediate(function() {  console.log('c')  process.nextTick(function() {    console.log('d')  })})setImmediate(function() {  console.log('e')})console.log('f')输出顺序应该是:f -> a -> b -> c -> d -> e                               这个值得注意的是setImmediate只能占用一次,这块setImmediate当时就回答错了。                                                    9. Promise实现网络超时判断                                         使用Promise实现网络请求超时判断,超过三秒视为超时。                            借助的是Promise.race这个方法,网络请求和三秒定时器进行竞争:                  const uploadFile = (url, params) => {  return Promise.race([    uploadFilePromise(url, params),    uploadFileTimeout(3000)  ])}function uploadFilePromise(url, params) {  return new Promise((resolve, reject) => {    axios.post(url, params, {      headers: {'Content-Type': 'multipart/form-data'}, // 以formData形式上传文件      withCredentials: true    }).then(res => {      if(res.status===200 && res.data.code===0) {        resolve(res.data.result)      }else {        reject(res.data)      }    })  })}function uploadFileTimeout(time) {  return new Promise((resolve, reject) => {    setTimeout(() => {      reject({timeoutMsg: '上传超时'})    }, time)  })}                     实习二面(50min)          一面完等待10min,二面就开始了。                            1. 进程和线程                            这是操作系统的问题,可以从四方面思考:                                                   1.1. 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位。                                  1.2. 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线。                                  1.3. 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间。                                  1.4. 调度和切换:线程上下文切换比进程上下文切换要快得多。                                                                             2. 说下操作系统中的死锁                                                   死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,两羊过独木桥。                                  死锁的解除与预防,资源的合理分配。。                                  这一块答的不是很好。。。                                                 3. 说一下链表类型                                       单向链表、双向链表、循环链表                                         4. 栈和队列的区别?                                                   栈是后进先出的数据结构,常用的比如调用栈。                                  队列是先进先出的数据结构,常用的就是NodeJS和浏览器中的任务队列了。                                                                             5. 遍历一个二叉树所有节点,返回它们的和          function numSum(root) {    if(!root) return 0;    return root.val + numSum(root.left) + numSum(root.right);}6. 连续自串最大和                                                        给出一个数组,求出连续元素组成子串和的最大值:                                                   输入: [-2,1,-3,4,-1,2,1,-5,4],                                        输出: 6                                                  双层循环的话时间复杂度是: O(n * n), 使用分治的话时间复杂度可以压缩到O(n * logn),问了下学长说直接用笨方法写一下,给出代码:                        function numSum(arr) {  if(!arr.length) return null;  if(arr.length < 2) return arr[0];  let sumMax = arr[0];  let sumTemp;  for(let i = 0; i < arr.length; i++) {    sumTemp = arr[i];    if(sumTemp > sumMax) sumMax = sumTemp;    for(let j = i + 1; j < arr.length; j++) {      sumTemp += arr[j];      if(sumTemp > sumMax) sumMax = sumTemp;    }  }  return sumMax;}7. 介绍一下你的项目?                                     略。。。                                                                          8. 微信小程序的原理说一下?                                     从微信小程序的四层说起:JS、WXML、WXSS、JSON。然后可以分析一下小程序的webview和jscore部分。                                                                          实习三面(1h)                                     然后紧接着就是三面压力面,也是我入职的部门leader。                                     1. 列举下ES6新特性并简要说明一波                                     可以列举一下以下几大块:                                     1.1 let、const                                                                  1.2 解构赋值                                           1.3 字符串、正则、数值、函数、数组、对象的扩展                                           1.4 symbol                                           1.5 Set、Map                                           1.6 Promise                                           1.7 Generator                                           1.8 async                                           1.9 class                                           1.10 修饰器                                           1.11 module                                                                         2. Generator实现一个自执行                                  Generator自执行就是著名的自执行模块co,可以使用递归思想,不停地的调用next方法。                                                                    3. 说一下微信小程序的生命周期                                                             这个我没温习到,没有回答完整哈~                                        页面生命周期:                                        - onLoad // 页面创建时执行                                        - onShow    // 页面出现在前台执行                                        - onReady   // 页面首次渲染完毕时执行                                        - onHide    // 页面从前台变为后台时执行                                        - onUnload  // 页面销毁时执行                                        - onPullDownRefresh   // 触发下拉刷新时执行                                        - onReachBottom       // 页面触底时执行                                        - onPageScroll        // 页面滚动时执行                                        - onResize            // 页面尺寸变化时执行                                        - onTabItemTap        // tab点击时执行                                        组件声明周期(在lifetimes字段内进行声明):                                        - created // 在组件实例刚刚被创建时执行                                        - attached // 在组件实例进入页面节点树时执行                                        - ready         // 在组件在视图层布局完成后执行                                        - moved         // 在组件实例被移动到节点树的另一个位置时执行                                        - detached // 在组件实例被从页面节点移除时执行                                        - error         // 每当组件方法抛出错误时执行                                                           4. 说一下你会的React技术栈                                  略。。。                                                                    5. React-Router实现简单路由                      import { BroserRouter, Route, Switch, Redirect } from 'react-router-dom'<BroserRouter>  <Switch>    <Route path='/login' exact component={Login} />    <Route path='/' render={() => (      <Switch>        <Route paht='/home' component={Home} />        <Redirect to='/home' />      </Switch>    )} />  </Switch></BroserRouter>6. 说一下Redux原理                                                             Redux就是一个数据状态管理,React单向数据流带来的数据跨组件传递问题。                                        简要说下Redux数据流,store、action、reducer,搭配React的使用。                                        可以有空看下源码,内容不是很多。                         7. 说一下你的项目                                  略。。。(遵循STAR原则)                                                                    8. 说一下Koa洋葱模型                                               Koa洋葱模型指的就是:一个请求从外到里一层一层地经过中间件,响应时从里到外一层一层地经过中间件。                                     观看Koa源码,你会发现:Koa中间件被存储在middleware数组中,实现洋葱模型关键依赖于koa-compose,分析一波koa-compose的源码:            'use strict'module.exports = composefunction compose (middleware) {  // 类型判断 middleware必需是一个函数数组  if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!');  for (const fn of middleware) {    if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!');  }  // 返回一个函数,接受context和next参数,koa在调用koa-compose时只传入context,所以此处next为undefined  return function (context, next) {    // last called middleware #    // 初始化index    let index = -1;    // 从第一个中间件开始执行    return dispatch(0);    function dispatch (i) {      // 在一个中间件出现两次next函数时,抛出异常      if (i <= index) return Promise.reject(new Error('next() called multiple times'));      // 设置index,作用是判断在同一个中间件中是否调用多次next函数      index = i;      // 中间件函数      let fn = middleware[i]      // 跑完所有中间件时,fn=next,即fn=undefined,可以理解为终止条件      if (i === middleware.length) fn = next      // fn为空时,返回一个空值的promise对象      if (!fn) return Promise.resolve();      try {        // 返回一个定值的promise对象,值为下一个中间件的返回值        // 这里时最核心的逻辑,递归调用下一个中间件,并将返回值返回给上一个中间件        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));      } catch (err) {        return Promise.reject(err)      }    }  }}9. 实现一个Koa中间件                                  看懂洋葱模型的源码,中间就很简单了,中间件都遵循这个结构:                      module.exports = function(options) {  // 配置处理  return async (ctx, next) => {    // 中间件逻辑...  }}中间件就是一个函数,接受两个函数:ctx(全局上下文)、next(调用下一个中间件的方法)                                                                    10. 参与比赛项目中,你自我感觉发挥比较重要的一个项目?                                  略。。。                                                                                 实习hr面(40min)                                     四面是hr面,聊了一下选择前端的原因和对于前端发展的看法,平时的学习途径和学习方法,大学校园生活、参加的一些比赛和活动,对于考研和就业的一些看法,别人对你的看法,随后也是顺利拿到了字节的实习offer。                                                           字节的面试官还是比较善解人意的,对于没有思路的问题会引导着你去思考而不是以难倒人为乐趣,算法题嘛整体也是比较简单的,多关注于实际实现和知识基础。                                     面试的节奏也是比较快的,三轮技术面基本都是连着面中间隔个15min。也可以看到字节对于基础知识是比较看重的,知识基础还是需要多看,还有一些框架的源码和原理看了的话也是加分很多的。                                     三面的效果并不是很理想,但最后还是成功拿到了实习offer,还是比较幸运的。                                                                                               提前批一面(1h)                                                   1. 跨域说一下                                  可以从跨域的原因和解决跨域的方法说。                                  1.1 跨域主要是由于浏览器的同源策略,是对跨域响应进行了拦截。                                  1.2 跨域的解决方法有JSONP、CORS、反向代理等等,具体说一说。                                                                    2. 输入URL发生了什么                                  寻找强缓存 -> 构建请求 -> DNS解析 -> 建立TCP连接 -> 发送HTTP请求 -> 响应(200/304协商缓存) -> 构建DOM树 -> 构建CSSOM树 -> 声称布局树 -> 建图层树 -> 生成绘制列表 -> 生成图块 -> 显示器显示                                                                    3. 重绘和回流说一下                                  重绘、回流的触发条件、过程,可以针对此做的优化策略。                                                                    4. new操作符进行的操作                                  以构造函数.prototype为原型创建空对象,将构造函数的this绑定到建立的空对象并执行,结果是引用数据类型则返回结果否则返回创建的对象。                                                                    5. 说一下CommonJS和ES模块化的区别                                  这个实习问过,答案在上面就不说了。                                                                    接着算法题:                                  6. 版本号排序                                                             输入 ['1.1.1.1.1.1', '6', '5.4.3', '2.3.1', '2.3.1.1'] 返回从大到小的版本号数组                         我的写法是将每一个版本号以.分隔为数组,然后从第一位往后比较。                                                                    7. 判断一个链表是否有环。                                  快慢指针即可。                                                    8. 实现一个类的add方法,使得同时的并行请求只有两个,并行请求完成后自动执行下一个任务直到全部执行完。class Scheduler {    async add(promiseFunc) {    }}const scheduler = new Scheduler()const timeout = (time) => {    return new Promise(r => setTimeout(r, time))}const addTask = (time, order) => {    scheduler.add(() => timeout(time)).then((res) => console.log(res))}addTask(1000, 1)addTask(500, 2)addTask(300, 3)addTask(400, 4)这个解法我详细总结过,可以查看:https://juejin.im/post/5f194ab1e51d45346d310b30                                  9. 使用Koa实现请求-响应的时间监听                         提前批二面(1h)        上面先是两道题:                      1. ES5实现数组去重,要求时间复杂度是O(n).  [1, 2,  2, '1', 'test'] => [1, '1', 'test'] 要求可以区分到1和'1'我问了是否可以使用额外空间,面试官回答是可以的。那就定义一个hash对象,遍历存入,key值的话需要添加typeof,用来区分1和''1'                      2. Promise并行              [promiseGenerator]promiseGenerator => Promisedispatch(arr, n) {}// 实现dispatch方法// 要求:并行限制2个                 跟上一面的思路差不多        3. 简历上的实习项目                      这个过程很长,问的东西也很多,所以多准备准备自己的项目吧。                                            4. HTTPS相对于HTTP做了些什么?                      从SSL+数字签名这两个方面说。                                            5. HTTP2.0的优化?                      头部压缩、多路复用、服务器推送这三个方面详细回答。                                                               6. 说一下为什么HTTP1有队头阻塞的问题                                                        7. HTTP2.0实现了服务器主动PUSH,那么如果服务端文件(html)没有发生改变,那么此时还会主动PUSH,如何解决这个问题?                            这个我有点懵,因为面试官对于服务端主动推送问的很深,我不会,他回答是服务端可以主动释放。                                                        8. React看你比较熟悉,说一下?                            可以说一下解决的问题、优势。                                                        9. 看过相关的源码没,说一下印象深刻的地方?                                                        10. Virtual DOM的优势                            函数式UI编程、跨平台。                                                        11. webpack的原理说一下?                            其实就是依赖关系图的构建过程                                                        12. webpack自己实现过loader或plugin吗?                                                        13. webpack loader和plugin的区别?                                  提前批三面(35min)        三面进行的时间比较短,首先是围绕项目进行的一些提问,这个持续的时间比较长。                      然后其他没问,直接给了两道题:                      1. 一个非常长的非递减数组,找出一个数出现的次数。比如:[1, 2, 3, 3, 4, ......, 45, 45, 69, ......, 1000]找出80出现的次数                      使用二分查找,找出这个数字出现的索引,然后向左向右扩展。                                            2. 同花顺扑克牌问题                      将扑克牌去除大小王,剩余1~13的黑桃、红桃、梅花、方块,1~13黑桃编号1~13,1~13红桃编号14~26,一次类推,求解取出5张牌是同花顺(同一颜色,顺子)                       解决方法很简单,先排序,最大与最小和需要是4,然后判断最小或最大是否在指定区间。              const fn = arr => {  if (!arr || arr.length !== 5) return false;  arr.sort((a, b) => a - b);  if (arr[4] - arr[0] !== 4) return false;  if (arr[0] % 13 >= 9) return false;  return true;}                                            三面进行的很简单时间也很短,主要是项目,算法也很简单,应该是前两面反馈比较好就没过多问什么问题。                      提前批hr面(40min)                      三面完就约了当天进行hr面,不得不说字节的效率,hr面是个可爱的小姐姐~                          主要沟通了一些工作、实习、学校、学习方面的事情吧。                                      为什么实习离职了,而不是留下来转正?                   你觉的你的优势在哪里?                   你觉得你的缺点在哪里?                   你对薪资待遇有什么要求?                   薪资在你心中的地位和分量?                   实习期间对于公司的工作时间怎么看的?能接受吗?                   对于加班怎么看待的呢?                   你未来两三年的职业规划?                   你是会学习一些后端、人工智能,还是专注于前端?                   平时学习的方式?                                      大概是有这些问题,当时还有其他问题想不起来了,沟通了四十分钟过程还是挺OK的。                   反问环节我问她怎么看待印度最近封杀了抖音海外版的情况,她笑了笑说说明了公司这方面的工作,然后通知我三个工作日offer会到。                                      如今意向书到了也快有一星期了,也祝愿大家都能拿到心仪的offer~                   整理不易,求个点赞关注~                    
点赞 48
评论 22
全部评论

相关推荐

点赞 评论 收藏
转发
点赞 收藏 评论
分享
牛客网
牛客企业服务