Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript基础—变量提升与函数提升 #2

Open
mobei95 opened this issue Nov 18, 2019 · 0 comments
Open

JavaScript基础—变量提升与函数提升 #2

mobei95 opened this issue Nov 18, 2019 · 0 comments
Assignees

Comments

@mobei95
Copy link
Owner

mobei95 commented Nov 18, 2019

在上一篇文章(变量声明)中提到了变量提升,但是并没有详细的探讨什么是变量提升,为什么会变量提升等问题,今天这篇文章就主要探讨这两个问题

什么是变量提升

先引用MDN的解释

变量提升(Hoisting)被认为是, Javascript中执行上下文 (特别是创建和执行阶段)工作方式的一种认识。

通俗一点的解释就是:函数及变量的声明都将被提升到当前作用域的最顶部也就是变量可以先使用再声明

看一段代码

num = 10
console.log(num) // 10
var num

foo() // 'foo'
function foo(){
    console.log('foo')
}

运行上面的代码可以看到,变量num和函数foo都是在代码定义之前调用的,按照顺序执行的原则,在定义之前调用应该报错才对,但是这里却是正常执行,这就说明JavaScript并不是严格的顺序执行,而是存在变量提升

JavaScript先把变量和函数定义提升到代码顶部,所以上面例子中的代码其实际的执行顺序应该是这样的:

var num
function foo() {
    console.log('foo')
}

num = 10
console.log(num)
foo()

为什么会提升

要深入了解变量提升的原因需要引入一个新的概念:执行上下文(关于什么是执行上下文,可以参考这篇文章:JavaScript深入之执行上下文栈)

执行上下文

执行上下文是JavaScript引擎在执行一段代码之前(编译阶段)的准备工作,也就是常说的编译内容;执行上下文中主要包含了三个非常重要的属性,分别是变量对象,作用域链,this;

执行上下文的调用可以分为两个阶段

1. 创建阶段(代码正式执行之前)

a. 创建作用域链
b. 创建变量,函数及参数(创建变量对象)
c. 求‘this’的值

2. 代码执行阶段

指派变量的值和函数的引用,执行代码

因为这里主要探索变量提升的原因,所以了解变量对象的创建过程,变量对象的创建过程大概分为这几个阶段:

  1. 创建arguments对象,初始化参数名称和值;
  2. 扫描执行上下文中的函数声明
    1. 为执行上下文中的每一个函数在变量对象上创建一个属性(这个属性的key就是函数的名称,value是函数在内存中的引用)
    2. 如果变量对象上已经存在这个函数名称,则重写函数的引用地址
  3. 扫描执行上下文中的变量声明
    1. 为执行上下文中的每个变量在变量对象上创建一个属性(这个属性的key就是变量名称,value值默认为undefined)
    2. 如果变量名称已经存在,则不做任何操作,直接跳过

看一个例子:

  function fun(i) {
      var a = 'hello'
      var b = function privateB() {
  
      }
      function c() {
  
      }
  }
  fun(1)
  
  // 当调用函数fun的时候,将创建一个执行对象,执行对象的内容如下:
  funExecutionContext = {
    // 创建作用域链
    scopeChain: {},
    
    // 创建变量对象
    variableObject: {
        // 创建arguments对象并初始化
        arguments: {
            0: 1,
            length: 1
        },
        i: 1,
        
        // 扫描执行上下文中的函数声明
        c: pointer to function c(),
        
        // 扫描上下文中的变量声明
        a: undefined, 
        b: undefined
    },
    
    // 计算this
    this: {}
  }

如何避免变量提升

在ES6中新增了let和const两个关键词可以声明变量,这两个关键词声明的变量不存在变量提升,必须先定义后使用

总结

  1. 变量提升是指函数和变量声明被提升到代码的最顶部,可以在定义之前调用;
  2. 提升的原因是JavaScript在执行上下文的编译阶段就创建了函数和变量;
  3. 通过ES6的let和const关键词可以避免变量声明提升,但不能避免函数声明提升。

end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant