Javascript执行机制
变量提升(Hoisting)
- 是指在Javascript执行过程中,Javascript引擎会将变量声明部分和函数声明部分提升到代码开头的“行为”。变量提升后,会对变量设置默认值,默认值为undefined
- JavaScript代码的执行过程
- 实际上变量和函数声明在代码里的位置是不会改变的,而且是在编译阶段被JavaScript引擎放到内存中
1. 编译阶段
1. 执行上下文:JavaScript执行一段代码的运行环境
1. 变量环境
2. 词法环境
2. 可执行代码
2. 执行阶段
- 一段代码中如果定义了两个相同名字的函数或变量,在编译阶段,JavaScript引擎会把最后一次定义的变量或函数覆盖之前定义的同名变量或函数,最终生效的是最后一个变量或函数。
调用栈
- 编译并创建执行上下文:
1. 全局代码,全局上下文只有一份
2. 执行函数的时候,函数执行上下文
3. eval函数
- 调用栈是用来管理函数调用关系的一种数据结构
- 先进后出
- JavaScript引擎会将执行上下文压入栈中,通常把这种管理执行上下文的栈称为执行上下文栈,又称调用栈。
- 代码的执行过程:
1. 创建全局上下文,并将其压入栈底
2. 调用函数fn,创建一个执行上下文,并压入栈中
3. 在fn函数中调用另一个函数fn2,JavaScript引擎会继续创建一个执行上下文,并压入栈顶
4. 当fn2函数执行完毕后,JavaScript引擎会将该执行上下文弹出栈
5. 当分配的调用栈空间被占满时,会引发“堆栈溢出”问题。
作用域
- 作用域是指在程序中定义变量的区域,该位置决定了变量的生命周期。作用域就是变量的与函数的可访问范围,即作用域决定函数和变量的可见性和生命周期。
- ES6之前:
- 全局作用域:对象在代码的任何位置都可以访问到,生命周期伴随页面的生命周期。
- 函数作用域:函数内部定义的变量或函数。只能在函数内部使用,生命周期随着函数执行结束之后销毁。
- es6
- 块级作用域
- Let
- Const
- JavaScript如何支持块级作用域
1. 编译并创建执行上下文
1. 函数内部通过var定义的变量,在编译阶段全部存放在变量环境中,通过let/const定义的变量,在编译阶段会被存放在词法环境中。
2. 函数内部的块级作用域内部,通过let/const创建的变量,并没有被存放在词法环境中。
2. 继续执行代码
1. 函数内部的块级作用域内部通过的let/const创建的变量,会被存放在词法环境的一个单独的区域中,这个区域的变量并不影响作用域外部的变量。
3. 词法环境内部维护了一个小型栈结构。当块级作用域执行完成之后,该作用域的信息就会被弹出。
4. 当执行到作用域使用变量时,会从词法环境由栈顶到栈底向下查询,如果再词法环境中没有查到,那么继续在变量环境中继续查找。
作用域链
- 在每个执行上下文的变量中,都包含了一个外部引用,用来指向外部的执行上下文,我们把这个外部引用称为outer。
- 当代码中使用了一个变量时,会在当前的执行上下文中查找,如果没有查找到,JavaScript引擎就会继续在outer所指向的执行上下文中查找。这个查找的链条就是作用域链。
词法作用域链
- 词法作用域链是指作用域是由代码中函数声明的位置来决定的,所以词法作用域是静态的作用域,通过它就能够预测代码在执行过程中如何查找标识符。
- 词法作用域是代码编译阶段就决定好的,和函数是怎么调用的没有关系。
闭包
- 在JavaScript中,根据词法作用域的规则,内部函数总是可以访问外部函数声明的变量,当通过调用一个外部函数返回一个内部函数后,即使外部函数执行结束了,但是内部函数引用的外部函数的变量依然保存在内存中,我们就把这些变量的集合称为闭包。比如外部函数是foo,那么这些变量的集合就称为foo函数的闭包。
- 闭包是怎么回收的?
- 如果引用闭包的函数是一个全局变量,这个闭包就会一直存在直到页面关闭。如果这个闭包以后不再使用的话,就会造成内存泄漏。
- 如果引用闭包的函数是一个局部变量,等函数销毁后,在下次JavaScript引擎执行垃圾回收时,会判断闭包这块内容不再使用,那么JavaScript引擎的垃圾回收器就会回收这块内存。
- 如果闭包会一直使用,那么它可以作为全局变量一直存在;如果使用频率不高,而且占用内存有比较大的话,那就尽量让他成为一个局部变量。
This
- this和执行上下文是绑定的
- 全局执行上下文中的this
- 全局执行上下文中的this是指向window对象
- 作用域的最底端包含了window对象
- 函数执行上下文中的this
- 默认调用函数,其执行上下文的this也是指向window对象
- 修改函数this的指向:
1. 通过函数的call/apply/bind方法设置
2. 通过对象调用的方式设置
- 使用对象来调用其内部的一个方法,该方法的this指向对象本身
3. 通过构造函数中设置
- 问题一:嵌套函数中的this不会从外层函数中继承
1. 把外层this保存为一个变量,在传递给嵌套函数
2. 嵌套函数修改为箭头函数
- 问题二:普通函数中的this默认指向全局对象window
- 最好的方式是采用call方法
- 严格模式下,默认执行一个函数,其执行上下文中的this值是undefined
此文章为4月Day7学习笔记,内容来源于极客时间《浏览器原理》,学习使我快乐,每天进步一点点💪💪
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net