深入解析JavaScript变量提升机制:var与function声明行为

作者:异步漫步时间:2026-10-23 14:08:45浏览:166

本文详细解析JavaScript中的变量提升机制,区分var声明的变量提升与function声明的函数提升。通过代码示例说明编译器在编译阶段与执行阶段的行为差异,解释为何var声明仅提升变量名而不赋值,以及函数声明优先于变量声明的规则。同时探讨块级作用域中函数声明的特殊表现及多次声明的覆盖逻辑,帮助开发者避免常见陷阱。

JavaScript变量提升概述

变量提升

在JavaScript中,变量提升主要涉及两种声明方式:使用var关键字声明的变量以及使用function关键字声明的函数。

var声明的变量提升机制

使用var声明的变量

首先观察以下代码,变量a的值是多少?

代码1


console.log(a);

var a;

按照常规编程语言的执行逻辑,代码是自上而下运行的。按此思路,当执行到第2行时,变量a尚未定义,因此会抛出错误,提示a is not defined。

然而,实际运行结果却是undefined。

带着这个疑问,我们继续分析下面的代码。


var a;
console.log(a);

我们发现这两段代码的执行结果相同。这是否意味着无论是否使用var声明变量,结果始终为undefined,从而造成了变量提升的错觉?为了验证这一点,我编写了代码3。

代码3


console.log(a);

这次代码终于报错了。这证明JavaScript代码并非完全按照表面上的自上而下顺序执行,至少在变量声明方面存在特殊处理。

接着我们再看代码4。

代码4


console.log(a);
var a = 2;

基于变量提升的概念,预期答案应为2。然而实际结果依然是undefined,这是为什么呢?

此时,我们需要请出负责语法分析及代码生成的编译器来解释这一现象。

当编译器遇到var a = 2;时,它会将其解析为两个部分:声明var a和赋值a = 2。其中,声明部分在编译阶段进行,而赋值部分则留在执行阶段原地等待。

也就是说,上述代码在编译后的实际形态如下所示。


var a;
console.log(a);
a = 2;

因此,最终输出结果为undefined。

为了进一步澄清,我们再看这段代码5。

代码5


a = 2;
var a;
console.log(a);

大家应该已经明白这段代码执行时的真实顺序及结果了,答案是2。但需要注意的是,如果将第2行注释掉,结果依然是2。这与变量提升无关,而是严格模式与非严格模式的差异所致。在非严格模式下,允许开发者省略变量声明关键字;而在严格模式下,这样做会直接报错。

function声明的函数提升机制

使用function声明的变量

与var类似,function声明的函数同样会发生提升。


log(5);

function log(mes){
  console.log(mes)
}

基于之前的理解,这段代码的实际执行顺序如下。


function log(mes){
  console.log(mes)
}

log(5);

很好,这是正确的。那么再看下一段代码。


log(5);

var log = function(mes){
  console.log(mes)
}

这段代码会报错,提示log is not a function。这表明函数表达式不会发生提升,只有函数声明才会被提升。如果在最前面新增一行代码console.log(log),会先输出undefined。

因此,这里的真实执行顺序是。


var log;
log(); //这时候只是声明了log这个变量,并不是函数,却用函数的方法调用它,所以会报错,说这不是一个函数。
log = function(mes){
  console.log(mes)
}

函数作用域内的变量提升

在function内部使用var声明变量

虽然我们知道var声明的变量会提升,但具体提升到何种程度尚不明确。

在此之前,我们先看一段代码。


var a = 4;

function foo(){
  var a = 5;
  console.log(a);

}
foo();

console.log(a)

输出结果为5和4,先输出5,再输出4。

使用var声明的变量具有函数作用域,因此foo函数内部的a与外部的a互不影响。这正是我们期望的结果。

稍作修改后的代码。


function foo(){
  a = 5
  console.log(a);
  var a;
}
foo();

console.log(a)

输出结果为5,随后报错a is not defined。

第4行代码输出5,第9行报错。

这种情况表明,变量提升仅会将变量提升到其所在作用域的顶部,而不会提升到父级作用域。

因此可以得出一个结论:变量提升只会将变量提升到自己所在的作用域的顶部。

函数声明与变量声明的优先级

函数优先

既然var和function声明的变量都有提升功能,那么如果同一个标识符同时用这两种方式声明会发生什么?答案见标题:函数优先。

具体代码如下。


foo();

var foo;

function foo(){
  console.log(1)
}

foo = function(){
  console.log(2)
}

输出结果为1。

这段代码的实际处理逻辑如下。


function foo(){
  console.log(1)
}

foo();// 1

foo = function(){
  console.log(2)
}

仔细观察,var foo;消失了。这是因为引擎认为这是重复声明,因此将其忽略。

既然var声明无法与函数声明竞争,那么尝试多次声明同一个函数变量。


foo()
function foo(){
  console.log(1);
}
foo()
function foo(){
  console.log(2);
}
foo()
function foo(){
  console.log(3);
}

foo()

foo被声明了三次,调用了四次,每次调用结果均为3。这表明最后的函数声明会覆盖之前的函数声明。

但是var试图证明其存在价值。


foo()
function foo(){
  console.log(1);
}
var foo;
foo()
foo = function(){
  console.log(2);
}
foo()
function foo(){
  console.log(3);
}

foo()

仔细观察,中间部分代码有所修改,依次输出3,3,2,2。

虽然var foo被忽略,但后续的函数声明仍然有效。这段代码可以视为如下形态。


function foo(){
  console.log(3);
}

foo();//3
foo();//3
foo = function(){
  console.log(2);
}
foo();//2
foo();//2

块级作用域中的函数声明

在普通块内部声明函数

之前讨论的是在作用域内声明函数,现在探讨在块内部声明函数的情况。


function foo(){

  console.log(b); // undefined
  b(); //TypeError: b is not a function

  var a = true;

  if(a){
    function b(){
      console.log(2)
    }
    //下面这段代码和上面的结果一样
    // var b = function(){
 //      console.log(2)
 //    }
  }
  //b() --> 这里会被执行

}

foo()

从上述代码来看,b为undefined,证明该变量确实存在,但它不是一个函数。这种情况与使用函数表达式类似。

总结

  • 提升分为函数声明提升和变量声明提升。
  • 声明变量使用var,声明函数使用function。
  • 变量提升会将变量提升到自己所在作用域的顶部。
  • 函数表达式不存在提升机制。
  • 当函数声明和变量声明同时声明同一个标识符时,函数声明优先。
  • 多个函数声明同一个标识符时,最后一个声明覆盖先前的声明。

以上就是本文的全部内容,希望这些内容对大家的学习或工作具有一定的参考价值,感谢支持。

作者最新文章