变量提升(Hoisting)

所谓的变量提升,是指在 JavaScript 代码执行过程中,JavaScript 引擎把变量的声明部分和函数的声明部分提升到代码开头的“行为”。变量被提升后,会给变量设置默认值,这个默认值就是我们熟悉的 undefined。

cefe564dbff729e735a834fd9e3bd0d5.png

执行流程

649c6e3b5509ffd40e13ce9c91b3d91e.png

编译阶段

0655d18ec347a95dfbf843969a921a13.png

  • 执行上下文(Execution context)
  • 可执行代码

    执行阶段

    调用栈

    调用栈就是用来管理函数调用关系的一种数据结构。

  1. var a = 2
  2. function add(b,c){
  3. return b+c
  4. }
  5. function addAll(b,c){
  6. var d = 10
  7. result = add(b,c)
  8. return a+result+d
  9. }
  10. addAll(3,6)

ccfe41d906040031a7df1e4f1bce5837.png
函数执行上下文是函数被调用的时候才会被创建的,js 引擎会编译函数,将函数的执行上下文压入栈中

栈溢出(stack overflow)

块级作用域

作用域(scope)

作用域是指在程序中定义变量的区域,该位置决定了变量的生命周期。通俗地理解,作用域就是变量与函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期。

JS 是如何支持块级作用域的

级作用域就是通过词法环境的栈结构来实现的,而变量提升是通过变量环境来实现,通过这两者的结合,JavaScript 引擎也就同时支持了变量提升和块级作用域了。

06c06a756632acb12aa97b3be57bb908.png

作用域链和闭包

作用域链

每个执行上下文中都有 outer,它指向定义时所在的执行上下文, outer 将不同的执行上下文串联起来,形成作用域链 JavaScript 语言的作用域链是由词法作用域决定的,而词法作用域是由代码结构来确定的。

  1. function bar() {
  2. console.log(myName)
  3. }
  4. function foo() {
  5. var myName = "极客邦"
  6. bar()
  7. }
  8. var myName = "极客时间"
  9. foo()

20a832656434264db47c93e657e346a7.png

词法作用域

词法作用域就是指作用域是由代码中函数声明的位置来决定的,所以词法作用域是静态的作用域,通过它就能够预测代码在执行过程中如何查找标识符。 词法作用域是代码编译阶段就决定好的,和函数是怎么调用的没有关系。

25053af5ae30c8be991fa14631cde0a7.png

闭包

在 JavaScript 中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,我们就把这些变量的集合称为闭包。

  1. function foo() {
  2. var myName = "极客时间"
  3. let test1 = 1
  4. const test2 = 2
  5. var innerBar = {
  6. getName:function(){
  7. console.log(test1)
  8. return myName
  9. },
  10. setName:function(newName){
  11. myName = newName
  12. }
  13. }
  14. return innerBar
  15. }
  16. var bar = foo()
  17. bar.setName("极客邦")
  18. bar.getName()
  19. console.log(bar.getName())

50e4ba60fc7e420e83b35b95e379b246.png

this

this 是和执行上下文绑定的

b398610fd8060b381d33afc9b86f988d.png

参考资料

  1. 浏览器工作原理与实践