变量提升:JavaScript是按照顺序执行的吗?
观察代码,并看到他的输出结果,我们发现:
- 在变量定义前使用,并没有报错,且值为undefined,不是定义的值;
- 在函数定义前使用,不会报错,且函数正常执行;
变量提升
所谓变量提升,是指在JavaScript代码执行过程中,JavaScript引擎把变量的声明部分和函数的声明部分提升到代码“开头”的行为。变量被提升后,会给变量设置默认值undefined。
JavaScript代码的执行流程
JavaScript是先编译,再执行的。
实际上,变量和函数声明在代码的位置是不会变的,它是在编译阶段被JavaScript引擎放入内存中。在编译阶段,可以分为两部分:变量提升部分代码、执行部分代码
输入一段代码,经过编译后,会生成两部分内容:执行上下文和可执行代码。
执行上下文是JavaScript执行一段代码时的运行环境,比如调用一个函数,就会进入这个函数的执行上下文,确定该函数在执行期间用到的诸如this、变量、对象以及函数。
JavaScript引擎到了“可执行代码”阶段,就会按照顺序一行一行地执行。
当代码中出现相同的变量或函数时,在编译阶段,第二个函数会覆盖掉第一个,然后再执行阶段就会找到这个函数并执行,这个时候用的就是第二个函数。综上,一段代码如果定义了两个相同名字的函数,最终生效的是最后一个函数。
总结:
- Javascript代码执行过程中,需要先做变量提升,而之所以需要实现变量提升,是因为Javascript代码在执行之前需要先编译
- 在编译阶段,变量和函数会被存放到变量环境中,变量的默认值会被设置为undefined;在代码执行阶段, Javascript引擎会从变量环境中去查找自定义的变量和函数。
- 如果在编译阶段,存在两个相同的函数,那么最终存放在变量环境中的是最后定义的那个,这是因为后定义的会覆盖掉之前定义的。
调用栈:为什么JavaScript代码会出现栈溢出?
调用栈是用来管理函数调用关系的一种数据结构。如图,调用栈:
调用栈是有大小的,当入栈的执行上下文超过一定数目,JavaScript引擎就会报错,这种错误就是栈溢出。常见于写递归的时候。
块级作用域:为什么要引入let和const?
作用域
作用域是指在程序中定义变量的区域,该位置决定了变量的生命周期。通俗地理解,作用域就是变量与函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期。
变量提升所带来的问题
- 变量容易在不被觉察的情况下被覆盖掉
- 本应销毁的变量没有被销毁
ES6通过引入let和const关键字,使JavaScript拥有了块级作用域。作用块内声明的变量不影响块外面的变量。
作用域链和闭包:相同变量,javaScript是如何选择的?
词法作用域
词法作用域就是指作用域是由代码中函数声明的位置来决定的,所以词法作用域是静态的作用域,通过它就能预测代码在执行过程中如何查找标识符。词法作用域是代码阶段就决定好的,和函数是怎么调用的没有关系。
闭包
在JavaScript中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但内部函数引用外部函数的变量依然保存在内存中,我们就把这些变量的集合称为闭包。
- 如果闭包一直使用,那么它可以作为全局变量而存在;
- 如果使用频率不高,而且占用内存比较大,那就作为局部变量;
this:从执行上下文来理解
this是和执行上下文绑定的。
全局执行上下文中的this
全局执行上下文中的this是指向window对象。
函数执行上下文中的this
如何设置函数执行上下文中的this呢?
- 通过函数的call/apply/bind方法设置
- 通过对象调用方法设置
- 在全局环境中调用一个函数,函数内部的this指向的是全局变量window
- 通过一个对象来调用其内部的一个方法,该方法的执行上下文中的this指向对象本身
- 通过构造函数中设置
this的设计缺陷以及对应方案
- 嵌套函数中的this不会从外层函数中继承
- 声明self用来保存this
- 使用ES6中的箭头函数
- 普通函数中的this默认指向全局对象window