1.编程语言发展:
机器语言;汇编语言;高级语言;
高级语言需要转换成机器语言才能运行
2.从链接到页面:
- 输入链接
- DNS解析(域名转换成IP)
- 请求html文件
- 解析html文件(下载css、js等资源)
- 页面渲染
3.浏览器内核
- 浏览器内核又称为:排版引擎、浏览器引擎、页面渲染引擎、样板引擎
- 主要浏览器引擎:
- Webkit:苹果基于KHTML开发,用于Safari
- Blink:Google开发,目前应用于Google Chrome、Edge、Opera等
4.浏览器渲染过程
5.JavaScript引擎
将JavaScript代码翻译成CPU指令来执行
常见JavaScript引擎:
- JavaScriptCore:苹果开发,WebKtit、小程序中使用
- V8:Google开发,Chrome中使用
6.浏览器内核与JavaScript引擎之间的关系
以WebKit为例:
- WebCore:负责HTML解析、布局、渲染等等相关的工作;
- JavaScriptCore:解析、执行JavaScript代码;
V8引擎原理
V8是用C++编写的Google开源高性能JavaScript和WebAssembly引擎,它用于Chrome和Node.js等
JavaScript代码执行过程:
- 解析:词法分析
- 语法分析:生成AST(抽象语法树)astexplorer.net/
- 解释器:将AST转换成字节码(字节码是跨平台的,可在不同的cpu上解释执行)
- 执行字节码
Parse模块会将JavaScript代码转换成AST(抽象语法树),这是因为解释器并不直接认识JavaScript代码
- 如果函数没有被调用,那么是不会被转换成AST的;
- Parse的V8官方文档:v8.dev/blog/scanne…
Ignition是一个解释器,会将AST转换成ByteCode(字节码)。
- 同时会收集TurboFan优化所需要的信息;
- Ignition的V8官方文档:v8.dev/blog/igniti…
TurboFan是一个编译器,可以将字节码编译为CPU可以直接执行的机器码;
- 如果一个函数被多次调用,那么就会被标记为热点函数,那么就会经过TurboFan转换成优化的机器码,提高代码的执行性能;
- 但是,机器码实际上也会被还原为ByteCode,这是因为如果后续执行函数的过程中,类型发生了变化(比如sum函数原来执行的是number类型,后来执行变成了string类型),之前优化的机器码并不能正确的处理运算,就会逆向的转换成字节码;
- TurboFan的V8官方文档:v8.dev/blog/turbof…
JavaScript代码执行方式:
- 全局对象VO:代码执行前,在堆内存中创建全局对象:Global Object(GO),GO中的变量所有作用域均可访问。GO中包含JavaScript内置属性和全局定义的变量。
- 执行上下文栈ECS:创建执行上下文栈(ECS),用于存放执行上下文。执行上下文栈的最底层是全局执行上下文。
- 执行上下文:JavaScript代码的执行环境,其中包含变量对象(VO)、词环境、可执行代码等。
- 全局执行上下文GEC:在执行上下文栈中压入全局执行上下文,全局执行上下文中的VO指向GO;在代码执行前(在parser转成AST的过程中),会将
全局定义的变量、函数等加入到GlobalObject中,但是并不会赋值(函数的话会将函数保存的地址赋给变量),这个过程也称之为变量的作用域提升。 - 函数执行上下文:在代码执行过程中,遇到要执行的的函数时,会在执行上下文栈中创建函数执行上下文,当函数是行结束后,会将该函数的执行上下文栈弹出。
作用域链(scope chain)
当查找一个变量时,会沿着作用域链查找。
作用域链生成时机:编译时,所以和函数定义的环境有关,和函数执行的环境无关。
// 例子1:
var age = 100
foo(123)
function foo(num) {
console.log(m)
var m = 10
var n = 20
function bar() {
console.log(age)
}
bar()
}
// 打印结果:100
例子1执行栈及作用域链分析:
// 函数的父级作用域和函数定义的环境有关,和函数执行的环境无关。
// 例子2:
var message = "Hello Global"
function foo() {
console.log(message)
}
function bar() {
var message = "Hello Bar"
foo()
}
bar()
// 打印结果:"Hello Global"
一些练习:
//练习1:
var n = 100
function foo() {
n = 200
}
foo()
console.log(n)
// 打印结果:200
//练习2:
function foo() {
console.log(n)
var n = 200
console.log(n)
}
var n = 100
foo()
// 打印结果:
// undefined
// 200
//练习3:
var a = 100
function foo() {
console.log(a)
return
var a = 200
}
foo()
// 打印结果:undefined
//练习4:
function foo() {
m = 100
}
foo()
console.log(m)
// 打印结果:100
//练习5:
function foo() {
var a = b = 10
// => 转成下面的两行代码
// var a = 10
// b = 10
}
foo()
// console.log(a)
console.log(b)
// 打印结果:
// 打印a会报错:a is not defined
// 打印b:10