js变量提升

211 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

提到变量提升,这些知识,需要你提前知晓~

执行上下文(Execution Context),又叫执行环境。解释器遇到可执行代码时,就会进入一个执行上下文,可以理解为当前代码的执行环境。 执行上下文有三种分类:

  1. 全局执行上下文

  2. 函数执行上下文

  3. eval函数执行上下文(很少用,忽略)

在JavaScript执行过程中, 全局执行上下文(global excution context)只有一个,在全局执行上下文中,会创建一个全局window对象(浏览器的情况下)并设置this的值为这个全局对象。

而函数执行上下文可以有多个。每当函数被调用时,就会创建一个新的函数执行上下文,这些执行上下文最后构成一个执行上下文栈(Excution context stack,ECS)来管理执行顺序。因为js是单线程的,所以只有当栈顶执行完出栈了,才能执行下一个上下文。

举个例子: 在这里插入图片描述

程序第一个进入的总是默认的全局执行上下文,所以说它总在ECS的底部,接着是函数执行上下文fn1、fn2,如图所示: 在这里插入图片描述

每个Excution Context有三个属性,变量对象(Variable object,VO),作用域链(Scope chain)和this。如下所示: 在这里插入图片描述 变量对象(Variable Object,VO),里面都有些啥?var声明的变量,声明的函数和函数的形参。有两种特殊情况不在VO中,1、函数表达式不在VO中;2、不是var声明的变量不在VO中,这种变量相当于给window添加了一个属性。

活动对象(Activation object),在函数执行上下文中,VO是不能直接访问的,此时由激活对象(Activation Object,缩写为AO)扮演VO的角色。激活对象 是在进入函数上下文时刻被创建的。 AO其实就是VO,只是他俩处于执行上下文的不同生命周期而已。

一个执行上下文的声明周期可以分为两个阶段。1、创建阶段,创建变量对象、作用域链以及设置this的值。2、激活/执行代码阶段,完成变量的赋值,函数的引用以及执行其他的代码。

下面让我们来举个例子,加深印象吧。

   
      function fn1() {
        var a = 1;
        var b = 2;
        var fn2 = function(){

        }
      }
      fn1();

创建阶段(用伪代码的形式展示):

fn1ExcutionContext = {
	variableObject:{
		a:undefined,
		b:undefined,
		fn2:undefined
	}
	scopeChain:{...},
	this:{...}
	
}

激活/执行阶段,代码更新为:

fn1ExcutionContext = {
	ActiveObject:{
		arguments: {...},
		a:1,
		b:2,
		fn2:<fn2 reference>
	}
	scopeChain:{...},
	this:{...}
	
}

小tips:

      console.log(fn1);
      
      function fn1() {
        
      }
      var fn1 = 1;

这段代码你认为会输出什么呢?如果你认为是1,那就错啦。注意,函数的声明提前优先级高于变量声明提前哦。 读到这里,是不是觉得变量提升问题就是小菜一碟。