一、浏览器的工作原理
比方在浏览器中输出网址,而后dns进行解析,解析出的就是服务器的一个ip地址。服务器返回一个html文件,浏览器内核在解析html文件的过程中,遇到link标签和script标签援用的css文件和JavaScript文件就会去下载下来。
二、浏览器内核
- 咱们常常会说:不同的浏览器有不同的内核组成:
Gecko:晚期被Netscape和Mozilla Firefox浏览器浏览器应用;
Trident:微软开发,被IE4~IE11浏览器应用,然而Edge浏览器曾经转向Blink;
Webkit:苹果基于KHTML开发、开源的,用于Safari,Google Chrome之前也在应用;
Blink:是Webkit的一个分支,Google开发,目前利用于Google Chrome、Edge、Opera等;
等等...
- 事实上,咱们常常说的浏览器内核指的是浏览器的排版引擎:
排版引擎(layout engine),也称为浏览器引擎(browser engine)、页面渲染引擎(rendering engine) 或样版引擎。
三、浏览器渲染过程
浏览器内核的 HTML Parse 将 HTML 转化为DOM树(DOM Tree),DOM 的 JavaScript 代码能够对DOM树(DOM Tree)进行操作(JavaScript代码是由JavaScript引擎执行的)。CSS Parse 将css转化为CSS规定(Style Rules)。而后 DOM树(DOM Tree)和CSS规定(Style Rules)通过附加(Attachment)生成渲染树(Render Tree),在 布局引擎(Layout)具体操作下,进行绘制(Painting),浏览器就能够进行展现(Dispaly)。之所以须要布局引擎(Layout),是因为浏览器在不同状态下布局有所不同。
四、意识JavaScript引擎
- 为什么须要JavaScript引擎呢?
咱们后面说过,高级的编程语言都是须要转成最终的机器指令来执行的;
事实上咱们编写的JavaScript无论你交给浏览器或者Node执行,最初都是须要被CPU执行的;
然而CPU只意识本人的指令集,实际上是机器语言,能力被CPU所执行;
所以咱们须要JavaScript引擎帮忙咱们将JavaScript代码翻译成CPU指令来执行;
- 比拟常见的JavaScript引擎有哪些呢?
SpiderMonkey:第一款JavaScript引擎,由Brendan Eich开发(也就是JavaScript作者);
Chakra:微软开发,用于IT浏览器;
JavaScriptCore:WebKit中的JavaScript引擎,Apple公司开发;
V8:Google开发的弱小JavaScript引擎,也帮忙Chrome从泛滥浏览器中怀才不遇;
等等…
- JavaScript是一门高级编程语言:
机械语言————>汇编语言————>高级语言
五、浏览器内核和JS引擎的关系
这里咱们先以WebKit为例,WebKit事实上由两局部组成的:
WebCore:负责HTML解析、布局、渲染等等相干的工作;
JavaScriptCore:解析、执行JavaScript代码;
另外一个弱小的JavaScript引擎就是V8引擎。
六、V8引擎原理
- 咱们来看一下官网对V8引擎的定义:
V8是用C ++编写的Google开源高性能JavaScript和WebAssembly引擎,它用于Chrome和Node.js等。
它实现ECMAScript和WebAssembly,并在Windows 7或更高版本,macOS 10.12+和应用x64,IA-32, ARM或MIPS处理器的Linux零碎上运行。
V8能够独立运行,也能够嵌入到任何C ++应用程序中。
- V8引擎架构
Parse模块会将JavaScript代码转换成AST(形象语法树),这是因为解释器并不间接意识JavaScript代码
如果函数没有被调用,那么是不会被转换成AST的。PreParse(预解析),并不是一开始所有代码都须要执行,所以V8引擎就实现了Lazy Parsing(提早解析)的计划,它的作用是将不必要的函数进行预解析,也就是只解析暂 时须要的内容,而对函数的全量解析是在函数被调用时才会进行;
Ignition是一个解释器,会将AST转换成ByteCode(字节码)
同时会收集TurboFan优化所须要的信息(比方函数参数的类型信息,有了类型能力进行实在的运算); 如果函数只调用一次,Ignition会执行解释执行ByteCode;
TurboFan是一个编译器,能够将字节码编译为CPU能够间接执行的机器码
如果一个函数被屡次调用,那么就会被标记为热点函数,那么就会通过TurboFan转换成优化的机器码,进步代码的执行性能; 然而,机器码实际上也会被还原为ByteCode,这是因为如果后续执行函数的过程中,类型产生了变动(比方sum函数原来执行的是 number类型,起初执行变成了string类型),之前优化的机器码并不能正确的解决运算,就会逆向的转换成字节码。
七、执行上下文
<script> var name = 'why' foo(123) function foo (num) { console.log(m) var m = 10 var n = 20 function bar () { console.log(name) } bar() }</script>复制代码
- 全局代码执行前的解析(红色框内)
- 全局代码执行和foo函数执行体执行前的解析(红色框内)
- foo函数执行体执行(红色框内)
- bar函数执行体执行前的解析
- bar函数执行体执行
因为bar函数内无name属性,所以向上到父级作用域中找 (看函数定义时的地位,其所在的上一层作用域为父级作用域,不断看调用地位)。如果在GO或之前找到,则输入name值,否则报出undefined。
bar函数执行体执行完后,则函数执行上下文(FEC)退出ECS执行上下文栈;foo函数执行体执行完后;函数执行上下文(FEC)一样退出ECS执行上下文栈。
基于晚期ECMA的版本标准:
每一个执行上下文会被关联到一个变量对象(variable object,VO),在源代码中的变量和函数申明会被作为属性增加到VO中。对与函数来说,参数也会被增加到VO中。
在最新的ECMA的版本标准中,对于一些词汇进行了批改:
每一个执行上下文会被关联到一个变量环境(VariableEnvironment),在执行代码中的变量和函数申明会被作为环境记录(Environment Record)增加到变量环境中。对与函数来说,参数也会被环境记录增加到变量环境中。
八、作用域晋升面试题
倡议:要是像我一样这种基础薄弱的人来说,做题时还是倡议画一下执行上下文来了解,这样更加加深印象和了解!!!
<script> var n = 100 function foo () { n = 100 } foo() console.log(n)//100</script>复制代码
<script> function foo () { console.log(n)//undefined var n = 200 console.log(n)//200 } var n = 100 foo()</script>复制代码
<script> var n = 100 function foo1 () { console.log(n)//100 } function foo2 () { var n = 200 console.log(n)//200 foo1() } foo2() console.log(n)//100</script>复制代码
<script> var n = 100 function foo () { console.log(n) return var n = 100//undefined } foo()</script>复制代码
<script> function foo () { var a = b = 100 // b=100(该赋值语句在该函数作用域到全局作用域之中找不到,则增加到全局作用域中) // var a=100 } foo() console.log(a)//报错 a is not defined console.log(b)//100</script>
最初
如果你感觉此文对你有一丁点帮忙,点个赞。或者能够退出我的开发交换群:1025263163互相学习,咱们会有业余的技术答疑解惑
如果你感觉这篇文章对你有点用的话,麻烦请给咱们的开源我的项目点点star:http://github.crmeb.net/u/defu不胜感激 !
PHP学习手册:https://doc.crmeb.com
技术交换论坛:https://q.crmeb.com