- 浏览器如何执行javascript代码?🤔
在我们浏览器里输入url地址时,浏览器会先解析。如果是域名,则会获取对应的ip地址,如果有资源存在缓存中,先判断是否过期,没过期直接去缓存里取。过期后就会像正式服务器发起请求,一般网页会返回一个html文件,然后浏览器去解析HTML。
- 浏览器哪些部分负责解析呢?解析完怎么渲染呢?🤔
浏览器内核: 排版引擎(layout engine),页面渲染引擎(rendering engine)
常见的浏览器内核: webkit,由两部分组成webcore和jscore。webcore常用于html解析,布局渲染,等;jscore负责解析js代码
渲染流程(无js):
- 解析html,形成dom tree
- 解析css,形成css规则
- dom树和css规则进行连接合合并之后进行layout排版,形成 render树
- 生成render树之后就可以paint绘制,最后进行display
经典流程图
- 如果在渲染的过程中,遇到js时,怎么解析呢?😲
js是高级语言,需要转成机器指令给cpu执行。
常见的js引擎:
- jscore: webkit内核中的js引擎,苹果公司开发(原生小程序用jscore来解析js代码)
- v8引擎:谷歌开发
- 首先对js代码进行词法分析和语法分析,把js代码转换成ast抽象语法树(astexplorer.net/)
- Ignition是一个解释器,会将AST转换成ByteCode(字节码)
- TurboFan是一个编译器,可以将字节码编译为CPU可以直接执行的机器码;
如果一个函数被多次调用,那么就会被标记为热点函数,那么就会经过TurboFan转换成优化的机器码,提高代码的执行性能。
但是,机器码实际上也会被还原为ByteCode,这是因为如果后续执行函数的过程中,类型发生了变化(比如sum函数原来执行的是number类型,后来执行变成了string类型),之前优化的机器码并不能正确的处理运算,就会逆向的转换成字节码;
-
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,这也是作用域的提升。