function f() { console.log(a); // ? var a = 1; } f();
简单简单,打印结果是 undefined。为啥?由于变量提高,变量 a 的声明被提高到当前做用域的顶部了。也就是能够想象成这样:函数
function f() { var a; console.log(a); a = 1; }
此外,还有函数提高,和变量提高相似:this
f1(); f2(); function f1() { console.log(1) } function f2() { console.log(2) }
f1 和 f2 被提高了,因此不会报错。不少文章中都会常常提到提高这个概念。
可是,这是真的吗?JS 解释器在执行代码时还顺带帮你变更一下代码的顺序?想一想也以为不太可能嘛~那么究竟是咋回事捏?code
JavaScript 的可执行代码(executable code)有三种,分别为 全局代码
、函数代码
以及 eval 代码
。鉴于 eval
不被推荐使用,咱就不理它。对象
当执行全局代码时,会建立一个全局执行上下文。当执行一个函数的时候,会建立一个函数执行上下文。全局执行上下文只会有一个,而函数执行上下文能够有不少不少个。JS 引擎会建立 执行上下文栈(execution context stack, ECS)
去管理这些执行上下文。咱们以函数执行上下文为例。ip
对于一个执行上下文而言,能够抽象化为这样一个对象:作用域
contextObject = { VariableObject, ScopeChain, thisValue }
其中变量对象(Variable Object,VO)是包含与当前执行上下文相关的数据做用域,它存储着当前上下文中定义的变量声明、函数声明(注意:不包含函数表达式),另外函数执行上下文中还会有参数。io
变量对象是一个抽象概念,在不一样的执行上下文中会以不一样的对象呈现。好比在全局上下文中,变量对象就是全局对象自己(这也是为何咱们可以经过全局对象的属性名称引用全局变量)。而对于函数执行上下文,是用活动对象(Activation Object,AO)来表示变量对象的,咱们访问的即是活动对象上的属性。console
执行上下文中的代码会被分出两个阶段进行处理,分别为:table
此时进入执行上下文,但在开始执行代码以前。此时的变量对象:function
了解了第一个阶段,那么来看个例子:
function f(a, b) { var c = 3; function d() {}; var e = function() {}; (function f() {}); } f(1, 2);
当执行 f 函数时,进入执行上下文,咱们能够描述出此时的 AO:
AO = { arguments: { 0: 1, 1: 2, length: 2 }, a: 1, b: 2, c: undefined, d: reference to function() {}, e: undefined, } // 注意:(function f() {}) 为函数表达式,不会被处理
执行阶段就相对简单,代码顺序执行,而且会根据代码去修改变量对象中的值,因此当函数执行完后的 AO 会是:
AO = { arguments: { 0: 1, 1: 2, length: 2 }, a: 1, b: 2, c: 3, d: reference to function() {}, e: reference to function() {}, }
综上所述:
console.log(f); // ? function f() {} var f = 1;
打印结果会是一个函数对象。由于第一个阶段中先处理函数声明后处理变量声明,当处理变量声明时变量对象中已经存在同名属性 f,不会作任何事直接跳过,因此 f 的值会是一个函数。
你也能够多找几道题尝试分析以加深理解。固然,平常工做中仍是直接想成提高来得方便些,只是但愿经过本文能让你了解到一些背后的事情。最后,若是文中有任何错误或不足之处,还请指出~