文末
如果30岁以前,可以还不知道自己想去做什么的话,那30岁之后,真的觉得时间非常的宝贵,不能再浪费时间在一些碎片化的事情上,比如说看综艺,电视剧。一个人的黄金时间也就二,三十年,不能过得浑浑噩噩。所以花了基本上休息的时间,去不断的完善自己的知识体系,希望可以成为一个领域内的TOP。
同样是干到30岁,普通人写业务代码划水,榜样们深度学习拓宽视野晋升管理。
这也是为什么大家都说30岁是程序员的门槛,很多人迈不过去,其实各行各业都是这样都会有个坎,公司永远都缺的高级人才,只用这样才能在大风大浪过后,依然闪耀不被公司淘汰不被社会淘汰。
269页《前端大厂面试宝典》
包含了腾讯、字节跳动、小米、阿里、滴滴、美团、58、拼多多、360、新浪、搜狐等一线互联网公司面试被问到的题目,涵盖了初中级前端技术点。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
前端面试题汇总
JavaScript
一、前言
在之前创建的《JVM》专栏中,讲解了Java
虚拟机中垃圾回收机制。同JVM
具有垃圾回收机制相似,JavaScript
同样具有垃圾回收机制。
众所周知,应用程序在运行过程中需要占用一定的内存空间,且在运行过后就必须将不再用到的内存释放掉,否则就会出现下图中内存占用持续升高的问题,一方面会影响程序的运行速度,另一方面严重的话会导致整个应用程序的崩溃。
二、JavaScript 中的内存管理
- 内存:由可读写单元组成,表示一片可操作空间;
- 管理:人为的去操作一片空间的申请、使用和释放;
- 内存管理:开发者主动申请空间、使用空间、释放空间;
- 管理流程:申请-使用-释放;
部分语言(例如C
语言)需要手动去释放内存,但是会很麻烦,所以很多语言,例如JAVA
都会提供自动内存管理机制,称为“垃圾回收机制”,JavaScript
也提供了垃圾回收机制(Garbage Collecation
),简称GC机制。
三、全停顿(Stop The World )
在介绍垃圾回收算法之前,我们先了解一下「全停顿」。垃圾回收算法在执行前,需要将应用逻辑暂停,执行完垃圾回收后再执行应用逻辑,这种行为称为 「全停顿」(Stop The World
)。例如,如果一次GC
需要50ms,应用逻辑就会暂停50ms。
全停顿的目的,是为了解决应用逻辑与垃圾回收器看到的情况不一致的问题。
举个例子,在自助餐厅吃饭,高高兴兴地取完食物回来时,结果发现自己餐具被服务员收走了。这里,服务员好比垃圾回收器,餐具就像是分配的对象,我们就是应用逻辑。在我们看来,只是将餐具临时放在桌上,但是服务员看来觉得你已经不需要使用了,因此就收走了。你与服务员对于同一个事物看到的情况不一致,导致服务员做了与我们期望违背的事情。因此,为避免应用逻辑与垃圾回收器看到的情况不一致,垃圾回收算法在执行时,需要停止应用逻辑。
3.1 JavaScript 中的垃圾回收
JavaScript
中内存会被判定为垃圾的情形如下:
- 对象不再被引用;
- 对象不能从根上访问到;
常见的GC
算法如下:
- 引用计数
- 标记清除
- 标记整理
- 分代回收
3.1.1 引用计数
早期的浏览器最常使用的垃圾回收方法叫做"引用计数"(reference counting
):语言引擎有一张"引用表",保存了内存里面所有资源(通常是各种值)的引用次数。如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。
const user1 = {age: 11}
const user2 = {age: 22}
const user3 = {age: 33}
const userList = [user1.age, user2.age, user3.age]
上面这段代码,当执行过一遍后,user1、user2、user3都是被userList引用的,所以它们的引用计数不为零,就不会被回收
function fn() {
const num1 = 1
const num2 = 2
}
fn()
上面代码中fn函数执行完毕,num1、num2都是局部变量,执行过后,它们的引用计数就都为零,所有这样的代码就会被当做“垃圾”,进行回收。
引用计数算法有一个比较大的问题: 循环引用
function objGroup(obj1, obj2) {
obj1.next = obj2
obj2.prev = obj1
return {
o1: obj1,
o2: obj2,
}
}
let obj = objGroup({name: 'obj1'}, {name: 'obj2'})
console.log(obj)
上面的这个例子中,obj1和obj2通过各自的属性相互引用,所有它们的引用计数都不为零,这样就不会被垃圾回收机制回收,造成内存浪费。
引用计数算法其实还有一个比较大的缺点,就是我们需要单独拿出一片空间去维护每个变量的引用计数,这对于比较大的程序,空间开销还是比较大的。
引用计数算法优点:
- 引用计数为零时,发现垃圾立即回收;
- 最大限度减少程序暂停;
引用计数算法缺点:
- 无法回收循环引用的对象;
- 空间开销比较大;
3.1.2 标记清除(Mark-Sweep)
核心思想:分标记和清除两个阶段完成。
- 遍历所有对象找标记活动对象;
- 遍历所有对象清除没有标记对象;
- 回收相应的空间。
标记清除算法的优点是:对比引用计数算法,标记清除算法最大的优点是能够回收循环引用的对象,它也是v8引擎使用最多的算法。
标记清除算法的缺点是:
上图我们可以看到,红色区域是一个根对象,就是一个全局变量,会被标记;而蓝色区域就是没有被标记的对象,会被回收机制回收。这时就会出现一个问题,表面上蓝色区域被回收了三个空间,但是这三个空间是不连续的,当我们有一个需要三个空间的对象,那么我们刚刚被回收的空间是不能被分配的,这就是“空间碎片化”。
3.1.3 标记整理(Mark-Compact)
为了解决内存碎片化的问题,提高对内存的利用,引入了标记整理算法。
标记整理可以看做是标记清除的增强。标记阶段的操作和标记清除一致。
清除阶段会先执行整理,移动对象位置,将存活的对象移动到一边,然后再清理端边界外的内存。
标记整理的缺点是:移动对象位置,不会立即回收对象,回收的效率比较慢。
3.1.4 增量标记(Incremental Marking)
为了减少全停顿的时间,V8
对标记进行了优化,将一次停顿进行的标记过程,分成了很多小步。每执行完一小步就让应用逻辑执行一会儿,这样交替多次后完成标记。
长时间的GC
,会导致应用暂停和无响应,将会导致糟糕的用户体验。从2011年起,v8
就将「全暂停」标记换成了增量标记。改进后的标记方式,最大停顿时间减少到原来的1/6。
四、v8 引擎垃圾回收策略
- 采用分代回收的思想;
- 内存分为新生代、老生代;
针对不同对象采用不同算法:
- 新生代:对象的存活时间较短。新生对象或只经过一次垃圾回收的对象。
- 老生代:对象存活时间较长。经历过一次或多次垃圾回收的对象。
V8
堆的空间等于新生代空间加上老生代空间。且针对不同的操作系统对空间做了内存限制。
类型 \ 系统位数 | 64位 | 32位 |
---|---|---|
老生代 | 1400MB | 700MB |
新生代 | 32MB | 700MB |
限制内存的原因:
- 针对浏览器来说,这样的内存是足够使用的。
- 针对浏览器的GC机制,经过不断的测试,如果内存再设置大一点,
GC
回收的时间就会达到用户的感知,会造成感知上的卡顿。
4.1 回收新生代对象
回收新生代对象主要采用复制算法(Scavenge 算法
)加标记整理算法。而Scavenge 算法
的具体实现,主要采用了Cheney算法
。
Cheney算法
将内存分为两个等大空间,使用空间为From
,空闲空间为To
。
检查From
空间内的存活对象,若对象存活,检查对象是否符合晋升条件,若符合条件则晋升到老生代,否则将对象从 From
空间复制到 To
空间。若对象不存活,则释放不存活对象的空间。完成复制后,将 From
空间与 To
空间进行角色翻转。
4.1.1 对象晋升机制
一轮GC
还存活的新生代需要晋升。
当对象从From
空间复制到 To
空间时,若 To
空间使用超过 25%,则对象直接晋升到老生代中。设置为25%的比例的原因是,当完成 Scavenge
回收后,To
空间将翻转成From
空间,继续进行对象内存的分配。若占比过大,将影响后续内存分配。
4.2 回收老生代对象
回收老生代对象主要采用标记清除、标记整理、增量标记算法,主要使用标记清除算法,只有在内存分配不足时,采用标记整理算法。
- 首先使用标记清除完成垃圾空间的回收;
- 采用标记整理进行空间优化;
- 采用增量标记进行效率优化;
4.2.1 新生代和老生代回收对比
- 新生代由于占用空间比较少,采用空间换时间机制。
- 老生代区域空间比较大,不太适合大量的复制算法和标记整理,所以最常用的是标记清除算法,为了就是让全停顿的时间尽量减少。
4.3 内存泄漏识别方法
我们先写一段比较消耗内存的代码:
<button class="btn">点击</button>
<script>
const btn = document.querySelector('.btn')
const arrList = []
btn.onclick = function() {
for(let i = 0; i < 100000; i++) {
const p = document.createElement('p')
// p.innerHTML = '我是一个p元素'
document.body.appendChild(p)
}
arrList.push(new Array(1000000).join('x'))
}
</script>
使用浏览器的Performance
来监控内存变化
点击录制,然后我们操作消耗性能的操作,操作完成之后,点击stop
停止录制。
然后我们看一看是哪些地方引起了内存的泄漏,我们只需要关注内存即可。
可以看到内存在短时间内消耗的比较快,下降的小凹槽,就是浏览器在进行垃圾回收。
专业技能
一般来说,面试官会根据你的简历内容去提问,但是技术基础还有需要自己去准备分类,形成自己的知识体系的。简单列一下我自己遇到的一些题
- HTML+CSS
- JavaScript
- 前端框架
- 前端性能优化
- 前端监控
- 模块化+项目构建
- 代码管理
- 信息安全
- 网络协议
- 浏览器
- 算法与数据结构
- 团队管理
- 开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
最近得空把之前遇到的面试题做了一个整理,包括我本人自己去面试遇到的,还有其他人员去面试遇到的,还有网上刷到的,我都统一的整理了一下,希望对大家有用。
其中包含HTML、CSS、JavaScript、服务端与网络、Vue、浏览器等等
由于文章篇幅有限,仅展示部分内容