面试复盘|字节秋招前端提前批一二三面面经
一面:
1、自我介绍+项目介绍
2、学习前端的途径
3、聊项目20min
4、vue双向数据绑定
- 数据劫持: 用 Object.defineProperty 为每个数据设置 getter/setter
- 数据渲染: 为页面使用到数据的每个组件都添加一个观察者(依赖) watcher
- 发布订阅: 为每个数据添加订阅者(依赖收集器)dep,并将对应的观察者添加进依赖列表,每当数据更新时,订阅者(依赖收集器)通知所有对应观察者(依赖)自动更新对应页面。
这里再补充一下:Object.defineProperty和Proxy的区别,各自的优缺点,也是常考题。
5、rem布局 em vw vh 百分比 宽高自适应屏幕
(1)rem(font size of the root element)是指相对于根元素的字体大小的单位。简单的说它就是一个相对单位。
(2)em(font size of the element)是指相对于父元素的字体大小的单位。
(3)其他可以实现响应式布局的单位:vw、vh (视口宽度和视口高度的百分比)
6、实现bind函数
Function.prototype._bind = function(){ var self = this //原函数 var context = [].shift.call(arguments) //this上下文 var args = [].slice.call(arguments) //参数 return function(){ self.apply(context, args.concat([].slice.call(arguments))) } } function a(m, n, o) { console.log(this.name + ' ' + m + ' ' + n + ' ' + o); } var b = { name: 'kong' }; a._bind(b, 7, 8)(9); // kong 7 8 9
7、call 、apply、bind的区别
当我们使用一个函数需要改变this指向的时候才会用到call``apply``bind。
- 如果你要传递的参数不多,则可以使用fn.call(thisObj, arg1, arg2 ...)。
- 如果你要传递的参数很多,则可以用数组将参数整理好调用fn.apply(thisObj, [arg1, arg2 ...]),第二个参数必须包含在数组里面。
- 如果你想生成一个新的函数长期给某个对象使用,则可以使用const newFn = fn.bind(thisObj); newFn(arg1, arg2...),bind() 方法不会调用函数。但是能改变函数内部this 指向。
- call,apply,bind 不传参数自动绑定在 window
8、使用vue相对于使用原生js有什么好处(操作dom、js对象,vue组件)
(1)控件跟数据自动绑定,可以直接使用data里面的数据值来提交表单,而不需要再使用$("#myid").val()那一套方法来获取控件的值,对控件赋值也方便很多,只需要改变data的值,控件就会自动改变值。将复杂的界面操作,转化为对数据进行操作。
(2)页面参数传递和页面状态管理。
页面传值对于vue来说,可供选择的方法非常多。比如使用子组件实现,通过对props属性传值;也可以使用页面url参数的方法传值;或使用vuex全局状态管理的方法页面传值等等。而原生开发的时候,在页面有多个参数的时候,页面传值和初始化,要复杂很多。
(3)模块化开发、无刷新保留场景参数更新
比如一个列表页面里面有添加功能,有修改功能,这时候我们可以通过引用子组件的形式,当子组件内容更新的时候,修改主组件的数据,比如修改了一条数据后,我们需要列表页同时刷新,但我们不希望改变原来列表页的页码和搜索条件。假如你用原生开发来实现这个,需要写很多业务逻辑保存上一个页面的搜索条件和页码这些参数,但假如你用vue开发,将变得非常简单。
(4)代码的可阅读性
vue天生具有组件化开发的能力,因此不同的功能基本都是写在不同的模块里面,因此代码的可阅读性非常高。当一个新手接手一个旧项目的时候,基本上可以做到一天就能定位到要修改的代码,进行修改,快速接手项目。
9、v-for 里面key的作用,如果没有一个唯一的值,那么要选什么值来作为key?
vue是通过比对组件自身新旧vdom进行更新的。
key的作用是辅助判断新旧vdom节点在逻辑上是不是同一个对象。
10、看代码说输出
(1)
(2)
// let x = 5; function setFn() { var x = 0; return function() { x = x + 1; return x; }; } const f1 = setFn(); const f2 = setFn(); f1(); // 1 f2(); // 1 f1(); // 2 f2(); // 2 const f3 = setFn(); const obj = {x: 10}; f3.call(obj); // 1
(2)
var a = 3; var obj = { a: 5, fn: function() { this.a = 10; } } var fn2 = obj.fn; fn2(); console.log(a); // 10 console.log(obj.a); // 5
11、算法题
数据数组 list,元素格式如:
[{id: 'id_a', name: 'foo', delay: 2},{id: 'id_b', name: 'bar', delay: 4} , {id: 'id_c', name: 'baz', delay: 1}... {id: 'id_x', name: 'str_x', delay: 3} ... ];
数组 idList: ['id_b', 'id_x' , ... 'id_a', 'id_c' ...] ,其元素是不定数量且打乱顺序的 id ;
要求得到一个字符串 `bar-str_x ... -foo-baz ...`,即按 idList 中的 id 的顺序拼接的 list 中元素的 name。
我的思路:
首先创建一个obj对象,遍历idList里面的id,对应数据数组list里面的数据,将id和name存入obj中,比如
obj ={
' id_a': 'foo',
'id_b':'bar',
...
}
然后再按顺序将name连接起来。
二面:
1、自我介绍+项目介绍
2、浏览器缓存:强缓存和协商缓存
强制缓存
强制缓存在缓存数据未失效的情况下(即Cache-Control的max-age没有过期或者Expires的缓存时间没有过期),那么就会直接使用浏览器的缓存数据,不会再向服务器发送任何请求。强制缓存生效时,http状态码为200。这种方式页面的加载速度是最快的,性能也是很好的,但是在这期间,如果服务器端的资源修改了,页面上是拿不到的,因为它不会再向服务器发请求了。这种情况就是我们在开发种经常遇到的,比如你修改了页面上的某个样式,在页面上刷新了但没有生效,因为走的是强缓存,所以Ctrl + F5一顿操作之后就好了。
协商缓存
当第一次请求时服务器返回的响应头中没有Cache-Control和Expires或者Cache-Control和Expires过期还或者它的属性设置为no-cache时(即不走强缓存),那么浏览器第二次请求时就会与服务器进行协商,与服务器端对比判断资源是否进行了修改更新。如果服务器端的资源没有修改,那么就会返回304状态码,告诉浏览器可以使用缓存中的数据,这样就减少了服务器的数据传输压力。如果数据有更新就会返回200状态码,服务器就会返回更新后的资源并且将缓存信息一起返回。
3、vue dom-diff算法
是一种通过同层的树节点进行比较的高效算法,避免了对树进行逐层搜索遍历,所以时间复杂度只有 O(n)。
diff 算法的在很多场景下都有应用,例如在 vue 虚拟 dom 渲染成真实 dom 的新旧 VNode 节点比较更新时,就用到了该算法。
4、v-for 中的key(第二次被问到)
vue是通过比对组件自身新旧vdom进行更新的。
key的作用是辅助判断新旧vdom节点在逻辑上是不是同一个对象。
5、v-if为什么不用key
6、宏任务和微任务
| 宏任务(macrotask) | 微任务(microtask) |
谁发起的 | 宿主(Node、浏览器) | JS引擎 |
具体事件 | 1. script (可以理解为外层同步代码) 2. setTimeout/setInterval 3. UI rendering/UI事件 4. postMessage,MessageChannel 5. setImmediate,I/O(Node.js) | 1. Promise 2. MutaionObserver 3. Object.observe(已废弃;Proxy 对象替代) 4. process.nextTick(Node.js) |
谁先运行 | 后运行 | 先运行 |
会触发新一轮Tick吗 | 会 | 不会 |
7、debounce函数
你尽管触发事件,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行。
总之,就是要等你触发完事件 n 秒内不再触发事件,我才执行。
function debounce(func, wait) { var timeout; return function () { var context = this; var args = arguments; clearTimeout(timeout) timeout = setTimeout(function(){ func.apply(context, args) }, wait); } }
8、最长回文子串(LeetCode第五题)
9、实习时间
10、为什么学习前端,怎么学习的
11、网站怎么实现自动登录
方法一:cookie和session配合使用
- 首先,用户登录输入用户名和密码,浏览器发送post请求,服务器后台获取用户信息,查询数据库验证用户信息是否正确。
- 如果验证通过,就会创建session来存储相关信息,并且生成一个cookie字符串,把sessionID放在cookie里面。然后返回给浏览器。
- 当用户下一次发起请求时,浏览器会自动携带cookie去请求服务器,服务器识别后,通过里面的sessionID,就可以直接读取session中的用户信息。
这样,用户就可以直接访问,不需要再输入用户名和密码来验证身份。
方法二:Token
Token是在服务端产生的。
- 如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回Token 给前端。
- 前端可以在每次请求的时候带上 Toke 证明自己的合法地位。
如果这个Token在服务端持久化(比如存入数据库),那它就是一个永久的身份令牌。
12、使cookie不能跨域获得,通过设置cookie的什么属性(应该是考CRSF的解决方法)
SameSite 属性可以让 Cookie 在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)。
属性值
SameSite 可以有下面三种值:
- Strict仅允许一方请求携带 Cookie,即浏览器将只发送相同站点请求的 Cookie,即当前网页 URL 与请求目标 URL 完全一致。
- Lax允许部分第三方请求携带 Cookie
- None无论是否跨站都会发送 Cookie
13、进程和线程的区别 (八股文常考题了)
14、JS的垃圾回收机制
标记-清除
第一阶段:标记。从根结点出发遍历对象,对访问过的对象打上标记,表示该对象可达。
第二阶段:清除。对那些没有标记的对象进行回收,这样使得不能利用的空间能够重新被利用。
15、position的值有哪些,relative和absolute的区别
其值可以分为五个:
static 静态定位
relative 相对定位
absolute 绝对定位
fixed 固定定位
sticky 粘性定位
相对定位的特点:
1. 它是相对于自己原来的位置来移动的(移动位置的时候参照点是自己原来的位置)。
2. 原来在标准流的位置继续占有,后面的盒子仍然以标准流的方式对待它。
绝对定位的特点:
1.子级绝对定位,不会占有位置,可以放到父盒子里面的任何一个地方,不会影响其他的兄弟盒子。
2.父盒子需要加定位限制子盒子在父盒子内显示。
3.父盒子布局时,需要占有位置,因此使用相对定位。
三面:
1、自我介绍+项目介绍
2、对自己来说收获最大的一个项目
对这个项目展开问了一些问题,如果被问到这个,一定要说自己最了解的那个项目,可能会针对里面的某些部分问得比较深。
比如我说了自己做的一个购物网站,然后介绍了一些项目中的亮点,面试官就针对这些亮点问了一些细节部分。
3、场景题:
如何获取到页面所有dom元素,并给出标签数量第m多的标签名。
答:从根节点出发,遍历所有dom节点,记录标签名和数量,然后对数量排序,输出第m多的那个标签名。
然后面试官就给了个数据结构
let node = {
tagName:'div',
childrens:[{..},{
tagName:'div',
childrens:[{...},{...}]
}...]
}
实现函数:find(node,m);
然后实现过程比较拉胯,最后面试官说这次面试结果可能不太理想,我😔😔
动手实践这方面还是需要加强,一些常见的场景题也需要去了解学习。
总结:
虽然三面凉了,但是字节整体的面试给我的感觉是非常棒的,面试官都非常和蔼,全程交流都比较友善,这点对于刚开始面试的人真的非常非常友好,不会觉得很有压力。
我记得一面的时候,面试官问了Object.defineProperty和Proxy的区别之后,说这是对对象进行监听,如果要对数组进行监听怎么办,我答不上来,面试官还告诉我可以怎么这么做,体验真的非常好。
继续复习刷题,秋招正式批再见!
另外想再补充一些在面试过程中被问到的出其不意的问题
有时候面试官问一个知识点,我能答出大概的意思,但是就怕面试官突然问一个小细节,而平时容易忽视细节的我就很猝不及防了😂😂
1、从输入URL到页面展示全过程
这可以说是非常常见的一个问题了,除了常规的浏览器缓存、TCP三次、四次握手、DOM渲染、JS阻塞等等,我还被问到了一些延伸的问题:
(1)http协议可以用基于udp协议吗?
(1)http协议可以用基于udp协议吗?
(2)JS异步调用时,如果在执行宏任务的过程中出现新的微任务,此时应执行?
(3)async和await属于微任务还是宏任务?
(4)有些服务器设置了强缓存的有效时间很长(比如一年),有什么缺点?(可能不能及时将服务器上更新的内容同步更新到浏览器)那怎么解决?(Ctrl+F5强制刷新)那移动端怎么办?
还有其他方面的一些问题:
2、图片的请求(src)会不会阻塞dom的渲染?
3、px转换为rem不是整数计算的时候怎么办?
4、如果精灵图很大,当前页面只需要其中的某一部分,该怎么做?
5、vue的双向数据绑定过程中,组件在什么时候添加依赖?(不知道是不是想问在组件的哪个生命周期函数里)
6、还有三面面试官问我什么是服务器、什么是http请求、什么是cookies?(感觉自己解释的时候思路混乱,也不知道面试官有没有get到🤣)