常见问题
多层的list嵌套的情况的优化如何处理?
- flat打平
- 使用reduce
常见代码优化有哪些?
双目运算 > 三元表达式 > if else
浏览器引擎
内核以Blink为例
浏览器引擎 = 排版引擎 = 渲染引擎,解决的是页面渲染和布局的问题
引擎的作用:解析JS,转换为二进制,到CPU执行
layout engine
·浏览器内核有哪些?
Gecko (早期被网景和火狐使用)
Trident (微软开发,IE4-IE11使用;目前的Edge已经转向Blink)
Webkit(小程序架构) (苹果基于KHTML开发,开源,用于Safari,谷歌之前也有用)
WebCore:负责解析、布局、渲染
JsCore:解析、执行JS代码
Blink (Webkit的分支,谷歌开发,用于Chrome、Edge、Opera等)
js engine
·Js引擎有哪些?
Spider Monkey (Brendan Eich开发)
Chakra (微软,IE引擎)
JavaScriptCore (苹果开发,也是小程序引擎)
V8 (谷歌开发)
V8
C++编写、高性能
可解析js、WebAssembly
用于Chrome、Node
多环境可运行(Win7、Mac10+、x64、IA-32、ARM、MIPS的Linux)
可嵌入到任何C++程序
V8中的进程种类
- 1 主进程 Broswer
- 1 GPU进程 GPU
- 1 网络进程 Network
- N 渲染进程 Renderer
- N 插件进程 Plugin
V8处理JS的过程
通过词法和语法分析,解析JS,转换为AST,用于解析器解析(Ignition)
Blink JS源码->Stream语法转换->scaner词法分析->tokens->AST->PreParse (Recursion)(函数声明和函数调用分开,函数调用时进行全解析
Ignition解析,以适应不同的CPU指令集,转换为对应的Byte
中途对可优化部分标记,通过TurboFun对其直接转为二进制
HTML解析遇到JS时
JS代码通过JS引擎执行,遇到JS后优先解析JS
HTML->HTML parse + DOM => DOM Tree
CSS -> CSS parse => Style Rules
接下来进行attachment
(重排发生在这一阶段) DOM Tree + StyleRule -> Render Tree
(重绘发生在这一阶段) Render Tree + Layout => Paint => Render && display
内存管理
说说V8的内存机制?
V8的对象分配基于堆进行
空间大小限制:
64位:1.4G
32位:0.7G
区域不同:
新生代
老生代
V8为什么会限制内存使用的大小?
V8中,js对象通过堆内存进行分配,V8对堆内存可申请的大小做了限制
堆内存又分为新生代内存和老生代内存,其总和值就是v8的最大内存——64位是1.4G,32位是0.7G
表层原因:V8设计之处为浏览器服务,没有大内存场景的需要
深层原因:v8垃圾回收机制——垃圾回收的过程会使得js线程暂停
说说新生代和老生代的不同
GC算法不同
新生代:以Scavenge算法为主
老生代:以Mark-Sweep && Mark-Compact为主
子空间不同:
新生代:基于Scavenge算法, 其内存分为From空间和To空间;即新生代的内存块由两个semi space构成
老生代:一整块大内存
新生代内存如何变为老生代内存?这两种不同类型的内存如何联通?
两者是一个线性的关系
内存申请->新生代->老生代->GC回收
由新生代到老生代的逻辑,称为晋升
具体规则,满足任意一条即可晋升:
1. semi space From 对象是否经历过scavengeGC
2. semi space To 占用空间超过25%
具体讲讲scavenge算法?
scavenge的核心是Cheney算法,其本质上是一种复制
流程:
1. 将堆内存一分为二,分别为From空间和To空间,共两个semi space
2. 只有一个被启用(From),另一个闲置(To)
3. 分配对象时,先在From中分配
4. GC时的机制:先检查From空间的存活对象,将其转移至To空间中;施放From中的非激活对象
* 第四步完成后,其内容实际上发生了转变,所以本质上是一种复制
缺点:
堆内存一分为二
老生代的算法讲一下?
老生代中的对象,基本为高频使用的对象,对其使用scavenge并不合适
老生代对象特点:
1. 声明周期长
2. 存活对象多
算法:
1. 标记清除 Mark-Sweep
两个阶段:标记阶段 & 清除阶段
原理:遍历堆内存中的对象,标记出存活对象,清除非存活对象
问题:内存碎片
2. 标记整理 Mark-Compact
出现原因:解决标记清除带来的内存碎片问题
标记清除真的合适在老生代内存中进行GC吗?
无论是scavenge、Mark-Sweep、Mark-Compact,其在执行时都会进行全停顿,阻塞逻辑执行
针对老生代中对象的特点,全堆垃圾回收(full垃圾回收)的标记、清理、整理等动作造成的停顿带来的影响会很大
所以产生了增量标记,通过将任务拆分为小阶段,初始化-标记-整理,减少停顿时间
V8逐渐转移到增量式处理,而非一次性处理,包括延迟清理(Lazy Sweep),增量式整理(Incremental Compact),后续也会引入并行处理
如何理解V8的垃圾回收和性能之间的关系?
V8的垃圾回收特点和JavaScript在单线程上的执行情况,垃圾回收是影响性能的因素之一
高性能需要让垃圾回收尽量少地进行,尤其是全堆垃圾回收。
如果内存已经超过1.4G了还想申请更多的内存如何做?
使用Buffer
1.4G只是V8的极限,不是Node的极限
Buffer申请内存不归V8管,受V8GC限制的主要是heap memory
讲一讲内存泄露?
垃圾堆叠——内存泄露本质上就是该被GC回收的内存没有被回收,成为了常驻内存的对象,即老生代对象
常驻内存的对象有哪些?
闭包作用域内的引用
全局变量引用
这两者都无法被直接垃圾回收
如何查看内存情况?
查看内存有两种,分别是查看Node进程内存以及查看OS内存
process.memoryUsage()
os.totalmem && os.freemem
讲一讲delete?除了delete之外,还有什么能达到同样的效果?
delete释放常驻内存的对象,在后续的垃圾回中能够被自动回收
delete的弊端:
影响V8的性能
其它的方法——赋值删除
通过赋值为undefined或者null来进行,避免了v8的性能影响的同时,实现同样的效果
repaint & reflow
重绘与重排-repaint & reflow
- repaint: 重绘——绘制,针对外观
- reflow: 重拍——排列,针对DOM
GC
引用计数
循环引用问题
标记清除
基于Root Object,对每个Root Object进行定期遍历并查找没有标记的对象,对其回收;Root Object的指定避免了循环引用
V8 GC基于标记清除
标签语法
tag1: for(let i = 1; i <= 10; i++) {
tag2: for (let t = 1; t <= 10; t++) {
if(t % 2 ==0) {
console.log(i,t)
}
if(i + t > 16) {
break tag2;
}
}
}
WebWorker
创造独立的二级子环境,不能与DOM等依赖单线程交互的API操作,但可以与父环境交换信息
特点
- 底层以实际线程执行
- 并行
- 可用SharedArrayBuffer在多个环境共享信息
- 没有window的概念,全局对象为WokerGlobalScope,以self关键字暴露
- 创建性能开销大
- 内存占用大
分类
专用型:只能被创建它的页面使用
共享型
- 可以被多个不同的上下文使用,包括不同的页面
- 与创建共享工作者线程的脚本同源的脚本,都可以向共享工作者线程发送消息或从中接收消息。
服务型:拦截、重定向和修改页面发出的请求
琐碎知识点
valueOf返回原始值,如没有原始值则返回本身
toString返回字符串表示 (模板字符串、字符串拼接时会自动调用toString)
[] {}的valueOf值为本身
[] {}的toString值为"" [Object, Object]
URL一般格式
protocol//host:port/path?search_condition
计算精度
const a = 0.1
const b = 0.2
const c = 0.3
b - a == a // true
c - b == a // false
0.1和0.2在转换为二进制时存在无限循环。观察可以看到实质上都是1100的无限循环,而0.1是三个0,0.2是两个0,就会导致最后一位四舍五入进位丢失精度。
0.1在计算机中二进制表示为0.0001 1001 1001 ...
0.2在计算机中二进制表示为0.0011 0011 0011 ...
parseFloat
~('109283lsakdjf') 会自动处理前n个数字输出
~('0xA') === 0 字符串,同上
~('1214.213.1.41') 只解析第一个正确的浮点数格式 //1214.213
~('000930424.241') 自动删除最前方的0 // 930424.241
isNaN
isNaN(NaN) // true
isNaN('...') // true
isNaN('10') //false
数组
数组创建可以直接一连串逗号 [,,,,,],几个逗号长度为几
正则
[0-9-]和[0-9\-]是一样的,都会匹配到-
BOM
location.host 如果是80端口,默认不显示;其它的均显示host:port
location.hostname 只显示host
location.pathname 返回当前页面路径和文件名
原生JS禁用
设置disabled attribute