一、浏览器原理-js执行原理

196 阅读3分钟
  1. 浏览器如何执行javascript代码?🤔

在我们浏览器里输入url地址时,浏览器会先解析。如果是域名,则会获取对应的ip地址,如果有资源存在缓存中,先判断是否过期,没过期直接去缓存里取。过期后就会像正式服务器发起请求,一般网页会返回一个html文件,然后浏览器去解析HTML。

  1. 浏览器哪些部分负责解析呢?解析完怎么渲染呢?🤔

浏览器内核: 排版引擎(layout engine),页面渲染引擎(rendering engine)

常见的浏览器内核: webkit,由两部分组成webcore和jscore。webcore常用于html解析,布局渲染,等;jscore负责解析js代码

渲染流程(无js):

  1. 解析html,形成dom tree
  2. 解析css,形成css规则
  3. dom树和css规则进行连接合合并之后进行layout排版,形成 render树
  4. 生成render树之后就可以paint绘制,最后进行display

经典流程图

  1. 如果在渲染的过程中,遇到js时,怎么解析呢?😲

js是高级语言,需要转成机器指令给cpu执行。

常见的js引擎:

  1. jscore: webkit内核中的js引擎,苹果公司开发(原生小程序用jscore来解析js代码)
  2. v8引擎:谷歌开发

  • 首先对js代码进行词法分析和语法分析,把js代码转换成ast抽象语法树(astexplorer.net/
  • Ignition是一个解释器,会将AST转换成ByteCode(字节码)
  • TurboFan是一个编译器,可以将字节码编译为CPU可以直接执行的机器码;

如果一个函数被多次调用,那么就会被标记为热点函数,那么就会经过TurboFan转换成优化的机器码,提高代码的执行性能。

但是,机器码实际上也会被还原为ByteCode,这是因为如果后续执行函数的过程中,类型发生了变化(比如sum函数原来执行的是number类型,后来执行变成了string类型),之前优化的机器码并不能正确的处理运算,就会逆向的转换成字节码;

  1. js全局代码执行和作用域提升

上面讲到了v8如何执行js代码,下面我们写个简单的代码,来探索一下js的执行过程。

var name = "wkb";

var num1 = 20;
var num2 = 30;

var result = num1 + num2;
console.log(result);

我们知道,v8会先解析js代码,在解析的过程中会创建一个全局对象GlobalObject(又称go)。

  • go里存放着v8自定义的对象以及方法等等,然后把我们定义的变量放到go中并赋值undefined,如下
var GlobalObject = {
  Date: Object,
  String: Object,
  Window: GlobalObject,
  name : undefined,
  num1 : undefined,
  num2 : undefined,
  result: undefined
}

这是解析的过程,然后v8开始执行我们的代码。

  • 为了执行js代码,v8内部会创建一个执行上下文栈Execution Context Stack(又称函数调用栈,ECS)。这个栈结构存放着函数执行顺序。

但是我们定义的是变量没有函数,那怎么将变量放入到执行上下文栈中呢?

  • v8引擎为了全局代码能执行,会创建一个全局执行上下文Global Execution Context Stack(GECS),其中这个全局执行上下文栈中有维护了一个对象variable object(vo)变量对象,用来存放定义的变量以及函数。

执行过程如下

了解了js全局代码的执行过程,我们来看一下作用域的提升。

var name = "wkb";
console.log(num1);
var num1 = 20;

如果我们在定义num1 之前打印num1会输出啥呢?

答案就是会输出undefined。因为我们知道,执行代码之前首先要编译代码,这时变量会被赋值到go里,并用undefined初始化,所以会输出undefined,这也是作用域的提升。