有 3 个关键字可以声明变量: var 、 const 和 let。
变量可以用于保存任何类型的数据。
1 var 关键字
var message; //不初始化的情况下,变量会保存一个特殊值 undefined
var message = "hi"; // 声明并且初始化变量
1.1 var 声明作用域
使用 var 操作符定义的变量会成为包含它的函数的局部变量
function test() {
var message = "hi"; // 局部变量
}
test();
console.log(message); // 出错!
在函数内定义变量时省略 var 操作符,可以创建一个全局变量
function test() {
message = "hi"; // 全局变量
}
test();
console.log(message); // "hi"
虽然可以通过省略 var 操作符定义全局变量,但不推荐这么做。在局部作用域中定
义的全局变量很难维护,在严格模式下,如果像这样给未声明的变量赋值,则会导致抛出 ReferenceError 。
一次定义多个变量
var message = "hi",
found = false,
age = 29;
1.2 声明提升(hoist)
function foo() {
console.log(age);
var age = 26;
}
foo(); // undefined
上一段不会报错,是因为 ECMAScript 运行时把它看成等价于如下代码:
即,把所有变量声明提升到作用域顶部
function foo() {
var age;
console.log(age);
age = 26;
}
foo(); // undefined
因此多次声明同一个变量也没有问题
function foo() {
var age = 16;
var age = 26;
var age = 36;
console.log(age);
}
foo(); // 36
2 let 关键字
2.1 let 声明的范围是块作用域
var 声明的范围是函数作用域。
块作用域是函数作用域的子集.
if (true) {
var name = 'Matt';
console.log(name); // Matt
}
console.log(name); // Matt
if (true) {
let age = 26;
console.log(age); // 26
}
console.log(age); // ReferenceError: age 没有定义
2.2 let 不允许同一个块作用域中出现冗余声明
var name;
var name; // 不会报错
let age;
let age; // SyntaxError;标识符 age 已经声明过了
嵌套使用相同的标识符不会报错
let age = 30; // 作用域是整个块
console.log(age); // 30
if (true) {
let age = 26; // 作用域是if块
console.log(age); // 26
}
这两个关键字声明的并不是不同类型的变量,它们只是指出变量在相关作用域如何存在
var name;
let name; // SyntaxError
let age;
var age; // SyntaxError
2.3 let 不会提升(hoist)
console.log(age); // ReferenceError:age 没有定义
let age = 26;
2.4 let 全局作用域声明不会成为 window 对象
var 声明的变量会成为 window 对象的属性
var name = 'Matt';
console.log(window.name); // 'Matt'
let age = 26;
console.log(window.age); // undefined
2.5 let 不能条件声明
<script>
var name = 'Nicholas';
let age = 26;
</script>
<script>
// 假设脚本不确定页面中是否已经声明了同名变量
// 那它可以假设还没有声明过
var name = 'Matt';
// 这里没问题,因为可以被作为一个提升声明来处理
// 不需要检查之前是否声明过同名变量
let age = 36;
// 如果 age 之前声明过,这里会报错
</script>
对于 let 这个新的 ES6 声明关键字,不能依赖条件声明模式
<script>
let name = 'Nicholas';
let age = 36;
</script>
<script>
// 假设脚本不确定页面中是否已经声明了同名变量
// 那它可以假设还没有声明过
if (typeof name === 'undefined') {
let name;
}
// name 被限制在 if {} 块的作用域内
// 因此这个赋值形同全局赋值
name = 'Matt';
try {
console.log(age); // 如果 age 没有声明过,则会报错
}
catch(error) {
let age;
}
// age 被限制在 catch {}块的作用域内
// 因此这个赋值形同全局赋值
age = 26;
</script>
2.6 for 循环中的 let 声明不会渗透到循环体外部
for (var i = 0; i < 5; ++i) {
// 循环逻辑
}
console.log(i); // 5
for (let i = 0; i < 5; ++i) {
// 循环逻辑
}
console.log(i); // ReferenceError: i 没有定义
3 const 声明
-
const 的行为与 let 基本相同,唯一一个重要的区别是用它声明变量时必须同时初始化变量
-
尝试修改 const 声明的变量会导致运行时错误
const age = 26;
age = 36; // TypeError: 给常量赋值
- const 声明的限制只适用于它指向的变量的引用。如果 const 变量引用的是一个对象,那么修改这个对象内部的属性并不违反 const 的限制
const person = {};
person.name = 'Matt'; // ok
- 不能用 const 来声明迭代变量(因为迭代变量会自增)
for (const i = 0; i < 10; ++i) {} // TypeError:给常量赋值
- 如果你只想用 const 声明一个不会被修改的 for 循环变量,那也是可以的。也就是说,每次迭代只是创建一个新变量
let i = 0;
for (const j = 7; i < 5; ++i) {
console.log(j);
}
// 7, 7, 7, 7, 7
for (const key in {a: 1, b: 2}) {
console.log(key);
}
// a, b
for (const value of [1,2,3,4,5]) {
console.log(value);
}
// 1, 2, 3, 4, 5
4 关键字使用原则
- 不使用 var
- const 优先, let 次之
使用 const 声明可以让浏览器运行时强制保持变量不变,也可以让静态代码分析工具提前发现不合法的赋值操作。因此,很多开发者认为应该优先使用 const 来声明变量,只在提前知道未来会有修改时,再使用 let。
本文是JavaScript高级程序设计(第四版)学习笔记