函数声明与赋值
的行为经常是开发者在学习中遇到的难点。本文通过对几道经典的代码解析题目进行详细剖析,来帮助你更好地理解 JavaScript 的执行顺序和作用域链。我们会一一讨论题目中的预解析结果、具体的输出
以及背后的原因。
JavaScriptvar x = 20;
function fn() {
if (x) {
var x = 30;
}
console.log(x); // 这里打印值是多少?
}
fn();
console.log(x); // 这里打印值是多少?
fn
中的 var x
也会被提升到函数顶部。预解析后的代码结构如下:var x; // 全局变量声明提升,初始值为 undefined
function fn() {
var x; // 局部变量声明提升,初始值为 undefined
if (x) {
x = 30; // 不会执行,因为 x 是 undefined,条件为 false
}
console.log(x); // 输出 undefined
}
// 执行阶段
x = 20; // 全局变量赋值
fn(); // 调用函数 fn,进入局部作用域
console.log(x); // 输出 20
var x
提升到全局作用域顶部,初始值为 undefined
。fn
提升到全局作用域顶部,完整保留。fn()
函数:
fn
内的 var x
被提升到函数顶部,初始值为 undefined
。if (x)
语句中,x
的值为 undefined
,所以条件为 false
,不会进入 if
块。console.log(x)
输出的是局部变量 x
的值,即 undefined
。console.log(x)
输出全局变量的值 20
。fn()
内的 console.log(x)
输出:undefined
。console.log(x)
输出:20
。x
遮蔽了全局变量 x
,即使局部变量未赋值。if (x)
判断时,使用的是函数作用域中的局部 x
,初始值是 undefined
。function fun(param) {
console.log(param); // 这里打印值是多少?
var param = function () {
console.log(1);
};
console.log(param); // 这里打印值是多少?
}
fun(5);
function fun(param) {
// 参数 `param` 被提升,作为函数的局部变量,初始值为传递的参数值 5
console.log(param); // 输出 5
param = function () { // 赋值操作覆盖了参数的值
console.log(1);
};
console.log(param); // 输出函数体
}
// 执行阶段
fun(5);
fun(5)
时,参数 param
被赋值为 5
。console.log(param)
: 5
,因为此时 param
的值是传递的参数。param = function() { console.log(1); }
: param
被赋值为一个函数,覆盖了之前的参数值。console.log(param)
: function () { console.log(1); }
,因为此时 param
已经被覆盖为该函数。console.log(param)
输出:5
。console.log(param)
输出:function () { console.log(1); }
。
var
声明。var param
的声明被忽略,但赋值操作仍然执行,因此覆盖了参数 param
的值。var foo = 1;
function bar() {
function foo() {}
foo = 10;
console.log(foo); // 这里打印值是多少?
}
bar();
console.log(foo); // 这里打印值是多少?
var foo; // 全局变量声明,初始值 undefined
function bar() {
function foo() {} // 局部变量 foo,初始值为函数
foo = 10; // 覆盖局部变量 foo 的值
console.log(foo); // 输出 10
}
// 执行阶段
foo = 1; // 全局变量赋值
bar(); // 调用 bar 函数
console.log(foo); // 输出 1
var foo
提升,初始值为 undefined
。bar
提升到全局作用域顶部。bar
函数中,function foo()
声明被提升到 bar
内部顶部,声明了一个局部变量 foo
,初始值为函数。bar()
时,局部变量 foo
(最初是一个函数)被重新赋值为 10
。console.log(foo)
输出 10
,因为局部变量 foo
已被赋值为 10
。console.log(foo)
输出全局变量的值 1
,因为全局变量 foo
没有被 bar
内部的操作影响。bar()
内的 console.log(foo)
输出:10
。console.log(foo)
输出:1
。
function foo()
在 bar
中被提升,相当于声明了一个局部变量。foo = 10
是对局部变量的重新赋值,不会影响全局变量。在 JavaScript 中,函数声明的提升优先于变量声明。这意味着函数会被完整地提升到作用域的顶部,并且在变量声明之前可以使用。但变量声明只会被提升声明,赋值操作仍然保留在原地。
在题目三中,function foo()
实际上相当于声明了一个局部变量 foo
,并将其初始化为一个函数对象。正因如此,当后续的 foo = 10
进行赋值时,修改的是这个局部变量,而不是全局的 foo
。
这说明函数声明不仅仅是定义一个可调用的代码块,它也会在当前作用域中创建一个变量,这个变量的名字与函数名相同。
当函数内部声明一个与外部变量同名的变量时,这个内部变量会遮蔽外部的变量,这就是所谓的作用域链中的遮蔽现象。遮蔽意味着在当前作用域中,同名的外部变量变得不可访问,除非使用特定的方式(例如 this
或全局对象)访问全局变量。
通过以上几个例子,我们深入理解了 JavaScript 中的作用域、变量提升、函数声明与变量声明之间的关系
。关键点包括:
var
声明。
预解析过程
有助于理解代码的执行顺序,尤其是复杂的作用域和变量提升
问题。扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有