引入
浏览器工作原理
输入url 通过dns 解析成ip地址
建立tcp连接 发起http请求
一般返回html文件
浏览器解析文件
- 遇到css 再发请求获取css文件
- 遇见script 再发请求获取js文件
浏览器内核
作用: 解析这些文件然后运行渲染页面
目前 Edge Chrome使用 内核 Blink(基于WebKit)
分为两部分:
- 解析html 渲染引擎
- 解析js js引擎
浏览器渲染过程
解析html文件生成dom树
解析css文件生成样式
两者结合生成渲染树
在浏览器控制下调整布局,进行绘制渲染到页面上
在这个过程中,遇到js代码会阻塞html的解析
因为js可能会操作dom元素
js引擎—V8引擎
是用c++编写的js和WebAssembly引擎,可以作用于Chrome,Node.js
作用 : 将js代码编译成机器指令,供cpu执行
- 词法分析 | 语法分析 解析js代码
- 生成抽象语法树ast
- 转化器转为字节码
- 字节码通过汇编转成机器指令
如果有出现频率比较高的js代码,就收集起来
但是如果出现例如传入的数据类型不一样,就得在这个基础上重新生成字节码
(因此如果都是用ts的话,这部分的性能花销就不存在了)
v8引擎的预解析Lazy Parsing
在解析代码之前有一个预解析过程
这个过程:
将当前阶段不运行的代码,进行预解析,不转成抽象语法树
代码编译过程
- 创造全局的变量对象:
GO
GO
中存放了全局的代码,内置函数等 - 遇到函数定义,开辟函数的内存存储空间
当前的作用域
+js代码
在GO
中存放函数地址
代码执行过程
创建全局执行上下文,
压入执行上下文栈,
全局执行上下文中有
- 变量对象go:内部属性关联了函数与变量的声明
开始执行代码,给变量赋值,如果遇到函数执行,创建函数执行上下文
压入执行上下文栈
函数执行上下文中有 - 变量对象ao:内部关联了函数与变量
- 作用域链:当前作用域ao+父级作用域(在编译期间决定)
- this
当代码执行完毕,弹出执行上下文栈
编译
先生成go,若go中有函数,这在内存中开辟空间存储函数对象
执行
函数执行之前生成ao
内存管理
- 分配内存
- 使用内存
- 不用了释放内存
js内存分配
基本类型内存分配
栈结构
复杂类型内存分配
堆结构
js内存释放(垃圾回收)
代码执行过程中需要分配内存,内存空间是有限的
垃圾
: 不再使用的对象
js引擎的垃圾回收器怎么知道哪些是垃圾?
gc算法
引用计数算法
对象中存放计数器,默认初始值为0
当其他对象使用到这个对象,
这个对象的计数器就++,不再使用–
当计数器变为0,这个对象就被回收掉
循环引用:内存泄漏
标记清除算法
设置一个根对象,gc会定期的从这个对象开始找有引用到的对象做标记,对于没有引用到的对象,认为是不可用的对象,
清除掉
js额外补充
with
前情提要
作用域:全局作用域,函数作用域
var obj = {}
这个不形成作用域
with(){}
形成作用域
eval
eval 全局函数 将传入的字符串当作js代码执行
var jsString = 'var name = "test";console.log(name)'
eval(jsString) // test
严格模式
支持严格模式的浏览器会以更严格的方式对代码进行检测与执行
//代码文件顶部
"use strict";
常见的严格模式下的错误
- 意外的全局变量
- 不允许函数有相同的参数名称
- 静默错误
- 不使用with语句
- eval语句中不向上引用变量
- 独立调用函数的this指向undefined
手写
call apply bind
slice
柯里化函数
组合函数
继承实现方案
- 原型链继承
子类不能传参,修改引用相互影响,打印看不到继承的属性 - 组合继承
Father.call(this,属性),两次调用父类构造函数,父类多出undefined属性 - 寄生组合继承
Object.create(原型)