浏览器- V8引擎- js中作用域及作用域链

135 阅读4分钟

浏览器渲染过程

1.1浏览器解析渲染过程图

browser-h-01.png

解析:首先会根据输入的域名 通过dms(专门做域名解析的)服务器解析转换到对应的ip地址 然后找到服务器地址 返回一个入口(index.html)再其次通过htmlParser(词法分析/语法分析)生成DOMTree 通过浏览器的LAYOUT布局 生成渲染tree最后绘制出来

1.1小程序架构图

browserCore.png

这个就是小程序的架构图,wxJS和wxCss都会添加到渲染层和逻辑层

2.1 V8引擎的原理

V8用的是c++的开源高性能js和WebAssembly引擎 用于Chrome和node.js

V8-h-img01.png

js源代码会通过Parse(词法分析/语法分析)生成一个ast抽象语法树(因为转换成ast可以对其方便操作 比如:es5代码/字节码) 通过lgntion(V8里面的一个库)转换成字节码(因为不同的环境有不同的cpu 他们执行的机器指令是不一致的) 将字节码转换成平台的执行代码 运行结果 在lgntion转换的过程中 会使用TurboFan来收集信息 如果一个函数被多次调用 他将会变成热点函数 通过TurboFan来直接转换成机械码输出,如果热点函数里面的参数类型发生了变化 他会走bytecode转换成字节码 然后输出

**eg: 词法分析/语法分析 const name = 19 他会将代码切割分成 tokens:[{type:'keyword',value:'cosnt'},{type:'identifiler',value:'name'}]

ast: astexplorer.net/

TurboFan V8官方文档 :v8.dev/blog/turbof…

在webpack 中babel插件 如何将ts转换成js vue中template如何转换的 ts: ts->ast(经过修改形成新的)-> generate code(代码生成)->js vue template : ->ast ->createVnode(创建新的虚拟节点)**

这是官方给的一张图 browser-h-02.png

其中差别就是多了一个PreParser 一个热解析 因为不是所有函数都一开始被执行 比如函数内部嵌套函数 所以需要Lazy Parsing(延迟解析) 把不必要的函数进行预解析 当函数全量解析到了 才会形成调用 这就是优化了网页的运行效率

JS引擎执行

解析过程

1.编译过程

  • 在js解析创建ast的时候 V8引擎内部会创建一个全局的对象(Global Object建成GO)
  • 在js引擎中会分为两个部分 1,编译代码 2.执行代码
  • 在js内部有个执行上下文调用栈(Execution Context Stack 简称ECS)
  • 在编译代码 他会通过parser转换成ast语法树的过程 会将全局定义的变量加入GO中但是不会赋值 他们的值都是undefined 2.执行代码 (代码编成机器指令)
  • V8为了执行代码 它在内部会有个执行上下文调用栈还会有个全局执行上下文(Global Execution Contest (GEC)) (全局执行代码才会加入里面)
  • 在全局执行代码中会**创建一个 VO 他的值是等于GO **然后开始执行代码 (给go里面的对象赋值)
  • 在编译期间遇见函数 (因为函数会创建一个自己的内存空间) 他就会在内存空间中创建一个单独空间(就有了一个单独的内存地址(0xa00)里面包含了 父级作用域 [{scope}]:parent scope 和函数的代码块)
  • 当我们执行代码时候 他会把执行的代码放在执行上下栈里面 创建一个函数执行上下文 Functional Execution Context (FEC) 它的vo指向着AO(活跃对象)也就是函数内部定义活使用的变量,scope chain(作用域链) :AO+parent.scope(上层作用域),this值的绑定. 他会压在EC stack里. 一旦函数执行完毕 他会把函数上下文从上下问栈弹出 并且销毁这个FEC 销毁它对应的AO
  • 变量的查找规则:查找路径是延着作用域链 一层一层寻找 直到window 也就是(go)
  • console.log(window) 其实就是在全局执行代码空间中找到go 全局变量对象

ECMA文档最新和早期

最新 ECstack-new.jpg

早期 ECstack-old.jpg

在早期的文档中 它是把函数变量直接声明作为一个属性添加了 最新的文档 他是把它声明一条环境记录添加到变量环境中