JavaScript进阶知识点作用域详解
JavaScript进阶讲解一
接下来,我会给大家讲解js中让人让人迷惑的知识点,比如: 作用域、函数、闭包、面向对象、ES新特性、事件循环、微任务、宏任务、内存管理、Promise、await、 asnyc、防抖、节流等等。
一、浏览器的内核
1.常见的浏览器内核
Gecko:早期被Netscape和Mozilla Firefox浏览器浏览器使用。 Trident:微软开发,被IE4~IE11浏览器使用。 Webkit:苹果基于KHTML开发、开源的,用于Safari,Google Chrome之前也在使用。 Blink:是Webkit的一个分支,Google开发,目前应用于Google Chrome、Edge、Opera等。
二、JavaScript引擎
2.1.为什么需要JavaScript引擎?
高级的编程语言都是需要转成最终的机器指令来执行,我们编写的JavaScript无论你交给浏览器或者Node执行,最后都是需要被CPU执行,但是CPU只认识自己的指令集,也就是所谓的机器语言,才能被CPU所执行,所以我们需要JavaScript引擎帮助我们将JavaScript代码翻译成CPU指令来执行。
2.2.常见的JavaScript引擎
SpiderMonkey、Chakra、JavaScriptCore、V8... 现使用最多的是v8引擎
三、V8引擎
3.1.官方定义
- V8是用C ++编写的Google开源高性能JavaScript和WebAssembly引擎,它用于Chrome和Node.js等。
- 它实现ECMAScript和WebAssembly,并在Windows 7或更高版本,macOS 10.12+和使用x64,IA-32, ARM或MIPS处理器的Linux系统上运行。
- V8可以独立运行,也可以嵌入到任何C ++应用程序中。
3.2.解析过程图示
四、JS的执行过程
- 初始化全局对象(GO -》 Global Object): js引擎在执行代码之前,会在堆内存中创建一个全局对象,将window属性指向自己,也会将Date、Array、String、Number、setTimeout、和你自己定义的全局变量这些放到GO中(当然你自己定义的还未执行 所以值是underfind)(这也是为什么我们可以使用window.及Data这些函数或类的原因)
- 执行上下文栈(ECS -》Execution Context Stack): 它是用于执行代码的调用栈,执行的是全局的代码块(GEC -》 Global Execution Context),也就是说GEC 会被放到ECS中执行
- GEC(这里面就有VO,这里指向GO)被放入到ECS中
- GEC开始执行代码(从上往下依次执行)
4.1 普通代码执行
其实在GEC开始执行代码后 如果只是一些变量,还是很好理解的,比如
console.log(a); // undefined var a = 100
这里为什么不报错 而是undefined,其实我们上面已将说的很明白了,因为在创建GO对象的时候 我们定义的全局变量会被添加到GO中 且值是undefined。这也是var的作用域提升。
4.1 函数如何执行?
如果我们执行时遇到函数怎么办呢?
foo() function foo() { console.log(100); } // foo()
看上面函数 不论我们foo在哪里调用 他都是可以正确执行的。而不会和变量那样显示 undefined或者报错,这是为什么呢? 其实在GO创建时(编译时,代码还未开始执行),当他遇到有函数的定义时,就会根据函数体创建一个函数执行上下文(FEC,在这里也会有个VO,这里的VO指向AO)并且压入到ESC中,存的是一个内存地址,不在是undefined。 所以当代码开始执行时 执行到foo()时,他就能在GO中找到那个内存地址
五、作用域提升理解undefined
var n = 100 function foo() { n = 200 } foo() console.log(n); // 200
var n = 100 function foo() { console.log(n); // undefined return var n = 200 } foo()
第一个大家应该都知道,所以不赘述,我们主要来看看为什么第二个打印的是undefined。看下图可得,在编译时,我们的函数会指向一个内存地址,开辟一个空间(AO),所以代码执行时,他会在AO中查找,找不到会在上一级查找(作用域链)
function foo() { console.log(a);// undefined var a = 100 console.log(a);// 100 } var a = 100 foo()
function foo() { console.log(a);// 100 } var a = 100 foo()
我们在来看这两个,相信大家已经明白了第一个输出的原因,我们再来看看第二个为什么是100,而不是undefined,其实这个原因很简单,他就是作用域链,很明显我们的AO中没有a的定义,所以他会在上一层中找, 而这里的上一层就是GO,此时GO中的a已经是100了 所以找到的a就是100。
大家来思考下下面这个会是什么呢?
var a = 1 function foo1() { console.log(a); } function foo2() { var a = 2 console.log(a); foo1() } foo2() console.log(a);