JavaScript变量提升与函数提升机制深度解析
本文深入解析JavaScript中变量提升与函数提升的底层机制。通过词法分析与执行阶段,阐明var声明与函数声明在预编译期间的行为差异。重点对比函数声明与函数表达式的提升区别,并详细推导同名变量与函数共存时的覆盖逻辑与最终输出结果,帮助开发者避免常见陷阱。
JavaScript代码的执行过程主要分为两个阶段:词法分析(预编译)与执行阶段。
- 在词法分析阶段,JavaScript引擎会对代码进行解析,主要处理三个部分:形参分析、变量声明分析以及函数声明分析。这一过程将开发者编写的源代码转换为引擎可执行的内部表示形式。
- 随后进入执行阶段,此时代码才开始真正运行。
变量提升是指变量声明的行为被移动到了当前作用域的顶部。
- 需要注意的是,只有变量的声明部分会被提升,而初始化赋值操作不会提升,仍保留在原地。
- 因此,无论变量声明写在哪里,它都会被提升到当前作用域的最顶端。
示例1展示了变量提升的基本行为。
console.log(num)
var num
num = 6
经过预编译处理后,代码逻辑变为:先声明变量num,然后执行console.log(num),最后进行赋值num = 6。
var num
console.log(num) // undefined
num = 6
示例2进一步验证了变量提升的特性。
num = 6
console.log(num)
var num
预编译后,变量num被提升至作用域顶部,此时console.log(num)输出undefined,随后执行赋值操作num = 6。
var num
num = 6
console.log(num) // 6
示例3展示了未声明直接赋值的特殊情况。
function bar() {
if (!foo) {
var foo = 5
}
console.log(foo) // 5
}
bar()
预编译后,由于没有var声明,直接赋值num = 6会创建全局变量(非严格模式下)或报错(严格模式下),随后输出num。
function bar() {
var foo // if语句内的声明提升
if (!foo) {
foo = 5
}
console.log(foo)
}
bar()
函数提升是指函数声明的行为被移动到了当前作用域的顶部。
- 与变量不同,函数声明的整体(包括函数体)都会被提升。
- 然而,函数表达式(即通过var或let/const赋值的方式定义函数)不会被提升,只有变量名会被提升。
示例1展示了函数声明的可提升性。
console.log(square(5)) // 25
function square(n) {
return n * n
}
预编译后,函数square被提升至作用域顶部,因此可以在定义之前调用并输出结果25。
function square(n) {
return n * n
}
console.log(square(5))
示例2展示了函数表达式不可提升的情况。
console.log(square) // undefined
console.log(square(5)) // square is not a function
var square = function(n) {
return n * n
}
预编译后,变量square被提升但值为undefined,此时调用square(5)会报错,因为square尚未被赋值为函数。
var square
console.log(square)
console.log(square(5))
square = function() {
return n * n
}
示例3展示了函数与变量同名时的复杂情况。
function bar() {
foo() // 2
var foo = function() {
console.log(1)
}
foo() // 1
function foo() {
console.log(2)
}
foo() // 1
}
bar()
预编译后,函数foo被提升,随后变量foo也被声明。由于函数声明优先级高于变量声明,最终foo指向函数。但在执行阶段,变量赋值覆盖了函数引用。
function bar() {
var foo
foo = function foo() {
console.log(2)
}
foo() // 2
foo = function() {
console.log(1)
}
foo() // 1
foo() // 1
}
当变量与函数同名时,函数提升的优先级高于变量提升,且不会被同名的变量声明覆盖,但会被同名的变量赋值覆盖。
示例1展示了函数与变量同名时的输出结果。
console.log(foo) // 会打印出函数
function foo() {
console.log('foo')
}
var foo = 1
示例2展示了函数与变量同名且存在赋值时的输出结果。
var foo = 'hello' // hello
;(function(foo) {
console.log(foo)
var foo = foo || 'world'
console.log(foo) //hello
})(foo)
console.log(foo) // hello
预编译后,函数foo和变量foo都被提升。由于函数提升优先级高,foo指向函数。但在执行阶段,变量赋值覆盖了函数引用。
var foo = 'hello'
;(function(foo) {
var foo
foo = 'hello' // 传入参数的foo值
console.log(foo) // hello
foo = foo || 'world' // foo有值为hello,所以没有赋值为world
console.log(foo) // hello
})(foo)
console.log(foo) // hello, 打印的事全局作用域下的var foo = 'hello'
本节探讨变量提升与函数提升的顺序问题,这是笔试中常见的考点。
在最近的笔试中,遇到了一道考察变量提升与函数提升顺序的题目。此前仅知道var定义的变量和函数声明都会提升,但未深入研究其顺序及详细过程。
经过查阅资料并自行验证,得出以下结论:函数提升优先级高于变量提升,且不会被同名变量声明时覆盖,但会被同名变量赋值后覆盖。
请看下面这段代码的执行过程。
console.log(a) // ƒ a(){} 变量a赋值前打印的都会是函数a
var a=1;
function a(){}
console.log(a) // 1 变量a赋值后打印的都会是变量a的值
首先,变量和函数声明都会提升,但函数提升优先级更高。提升后,变量a只是声明而未赋值,因此输出的是函数a。详细过程如下:
function a(){} // 函数声明提升 a-> f a (){}
var a; // 变量提升
console.log(a) // 此时变量a只是声明没有赋值所以不会覆盖函数a --> 输出函数a f a (){}
a=1; //变量赋值
console.log(a) // 此时变量a赋值了 --> 输出变量a的值 1
总结:由于函数声明和变量都会提升,如果函数与变量同名,那么在变量赋值前打印的都会是函数,在变量赋值之后打印的都是变量的值。
现在让我们再看一段代码。
a(); // 2
var a = function(){ // 看成是一个函数赋值给变量a
console.log(1)
}
a(); // 1
function a(){
console.log(2)
}
a(); // 1
这段代码旨在说明只有函数声明才会提升,函数表达式不会提升。因此在函数表达式后面的代码会输出1,因为变量a赋值后把提升的函数a覆盖了。详细过程如下:
function a(){ // 函数提升
console.log(2)
}
var a; // 变量提升
a(); // 2
a = function(){ // 变量a赋值后覆盖上面的函数a
console.log(1)
}
a(); // 1
a(); // 1
再看一段代码。
a();
function a(){
console.log(1)
}
function a(){
console.log(2)
}
打印结果是2,道理很简单:先声明的会被后声明的覆盖。
总结。
到此这篇关于JavaScript中变量提升和函数提升的文章就介绍到这了,更多相关js变量提升和函数提升内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
JS合并多个数组的三种方法:concat、spread与flat
2026-11-05 17:39:45
JavaScript怎么准确判断数据类型:typeof、instanceof与Object.prototype.toString
2026-11-05 17:09:21
JS中将数组和对象混合组装为JSON字符串
2026-11-05 16:41:42
JS运算符优先级与结合性的区别是什么?
2026-11-05 13:22:02
JS Map怎么只遍历Key或Value
2026-11-05 13:00:36
