【js】JavaScript的变量提升、函数声明提升
一、变量提升
变量提升(Hoisting)是 JavaScript 中的一个特性,它指的是:
在代码执行前,变量的声明(不是赋值)会被“提升”到当前作用域的顶部。
🌟 举个例子:
console.log(x); // 输出:undefined
var x = 10;
你可能觉得这段代码会报错,但实际上它不会。
这是因为 JavaScript 在“执行前”会自动把 var x
提到代码的最上面,相当于这样:
var x; // 声明被提升了
console.log(x); // 这里访问的是 x,但还没赋值,结果是 undefined
x = 10; // 赋值发生在这一行
❗注意几点:
-
只有声明被提升,赋值不会提升!
console.log(a); // undefined,而不是 5 var a = 5;
-
函数声明也会被提升,但函数表达式不会
greet(); // OK,输出 Hello function greet() {console.log("Hello"); }greet2(); // ❌ 报错:greet2 is not a function var greet2 = function () {console.log("Hi"); };
-
let
和const
也会被“提升”,但不会初始化,访问会报错(TDZ)console.log(b); // ❌ ReferenceError: Cannot access 'b' before initialization let b = 20;
这叫做“暂时性死区(TDZ,Temporal Dead Zone)”。
✅ 建议写法:
-
避免使用
var
,使用let
或const
。 -
把变量声明放在使用之前,代码更清晰,逻辑更直观。
这是一个非常好的问题,触及了 JavaScript 的历史、兼容性以及开发习惯演变。我们来深入、清晰地解答这个问题:
🌍 为什么 var
仍然存在?
1. 历史兼容性(向后兼容)
-
var
是 JavaScript 最初就有的变量声明方式,存在于 ES5(2009年)及更早的版本。 -
let
和const
是 ES6(2015年) 才引入的新特性。 -
如果完全废弃
var
,会导致 大量旧代码无法运行,这对于浏览器厂商和开发者都是灾难性的。
➡️ 所以 var
仍被支持,是为兼容旧代码。
📌 使用 var
有哪些坑?
特性 | var | let / const |
---|---|---|
作用域 | 函数作用域 | 块级作用域 |
变量提升 | 有(声明提升 + 初始化为 undefined) | 有声明提升但存在 TDZ(初始化前不能访问) |
允许重复声明 | ✅ 是 | ❌ 不允许 |
推荐程度 | 🚫 不推荐 | ✅ 推荐 |
示例:
if (true) {var x = 10;
}
console.log(x); // ✅ 输出 10,x 逃出了 if 块if (true) {let y = 20;
}
console.log(y); // ❌ ReferenceError
✅ 总结:你该怎么做?
-
新项目中,强烈推荐只用
let
和const
-
默认用
const
,只有需要修改变量值时再用let
-
-
除非你明确在维护旧代码,或有特殊兼容性需求,否则应该避免使用
var
-
在团队开发中可使用 ESLint 等工具强制禁止使用
var
二、函数声明提升
“函数声明提升(function hoisting)”是 JavaScript 的特有行为,它并不普遍存在于其他大多数编程语言中,尤其是静态类型语言(如 Java、C++、Python)中。
✅ 回顾一下 JavaScript 中的函数提升:
在 JavaScript 中,如果使用 函数声明(function
关键字直接定义),整个函数(包括函数体)都会被提升到作用域顶部,你可以在声明之前调用它:
sayHi(); // ✅ 输出 "Hi"function sayHi() {console.log("Hi");
}
但如果使用 函数表达式,即把函数赋值给变量,那么只有变量声明被提升,函数体不会被提升:
sayHello(); // ❌ 报错:sayHello is not a functionvar sayHello = function() {console.log("Hello");
};
❓其它语言是否有函数提升?
🔸 Python:
没有函数提升,函数必须在使用之前定义:
say_hello() # ❌ NameError: name 'say_hello' is not defineddef say_hello():print("Hello")
🔸 C / C++:
函数定义前必须有 函数声明(prototype),否则编译不通过:
int add(int, int); // 函数声明int main() {int result = add(2, 3); // OK
}int add(int a, int b) { // 函数定义return a + b;
}
🔸 Java:
类方法要写在类中,没有提升,编译器需要先知道方法的签名。
💡 为什么 JavaScript 这么设计?
JavaScript 是一种解释型语言,设计之初为了方便开发者快速书写脚本,提供了“先用后声明”的容忍机制。解释器在执行之前会做一个“预扫描”(预处理阶段),把变量和函数的声明提取出来,这就造成了所谓的“提升”现象。
🔚 总结:
特性 | JavaScript | Python | C/C++ | Java |
---|---|---|---|---|
变量提升 | ✅ var 有,let/const 没有 | ❌ | ❌ | ❌ |
函数提升 | ✅ 函数声明提升 | ❌ | ❌(需声明) | ❌ |