跳到主要内容

if 中的函数声明与存储混乱问题

在 JavaScript 中,函数声明与变量声明都会被提升(Hoisting)。但是,如果在 if 语句中声明函数,可能会导致一些意料之外的结果。下面我们通过一个例子来说明。

示例代码

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

if (true) {
a = 1;
console.log(a);

function a() {}
a = 2;
console.log(a);
}

console.log(a);

上面的代码执行后,输出结果为0 1 2 1。这是为什么呢?让我们一步步分析。

变量 a 的值被替换

我们把代码简化一下:

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

if (true) {
function a() {}
}

console.log(a); // 输出: ƒ a() {}

可以看到,原本a的值被函数声明替换了。这是因为函数声明会被提升到作用域的最顶端。

存储混乱问题

实际上,在内存中分别存在两个a,一个存放在栈内存,一个存放在堆内存。JavaScript 引擎会优先访问栈内存中的值。

让我们再次回到最初的例子:

var a = 0; // 变量a初始化为0,存放在栈内存
console.log(a); // 输出0

if (true) {
a = 1; // 把栈内存中a的值设为1
console.log(a); // 输出1

function a() {} // 函数a存放在堆内存

a = 2; // 把栈内存中a的值设为2
console.log(a); // 输出2
}

console.log(a); // 输出1

当 if 语句中出现函数声明时:

  1. 函数a会被提升到作用域顶部,替换掉原有的变量声明。但由于函数要存放在堆内存中,因此 JavaScript 引擎会在堆内存中新开辟一块空间存放函数a,而原有的变量a依然在栈内存中。

  2. a = 1是在栈内存中进行赋值操作。而访问a时,JavaScript 引擎会优先访问栈内存,因此打印出1

  3. 函数a声明后,栈内存中的a被赋值为2,因此第二次打印出2

  4. if 语句执行完毕后,栈内存中的a值为1,因此最后一次打印出1

如何避免这个问题

  1. 避免在 if 语句中声明函数,这样可以避免存在两个不同的a

  2. 声明变量时始终使用 let 或 const 关键字,避免使用 var。

  3. 如果确实需要在 if 中定义函数,可以使用函数表达式而不是函数声明:

if (true) {
var a = function () {};
}