JavaScript 变量是松散类型的,而且变量不过就是特定时间点一个特定值的名称而已。由于没有规则定义变量必须包含什么数据类型,变量的值和数据类型在脚本生命期内可以改变。
有 3 个关键字可以声明变量:var、const 和 let。其中,var 在 ES 的所有版本中都可以使用,而 const 和 let 只能在 ES6 及更晚的版本中使用。
1、var 关键字
要定义变量,可以使用 var 操作符(注意 var 是一个关键字),后跟变量名(即标识符,如前所述):
var message;
不初始化的情况下,变量会保存一个特殊值 undefined。
- var 声明作用域
使用 var 操作符定义的变量会成为包含它的函数的局部变量。省略 var 操作符,可以创建一个全局变量。
function test() {
var message = "hi"; // 局部变量
name = "xiaoming"; // 全局变量
}
test();
console.log(message); // 出错!
console.log(name); // "xiaoming"
使用关键字声明的称为显式声明,未使用任何关键字声明的称为隐式声明
- var 声明提升
var 声明的变量会自动提升到函数作用域顶部:
function foo() {
console.log(age);
var age = 26;
}
foo(); // undefined
2、let 声明
let 跟 var 的作用差不多,但有着非常重要的区别。最明显的区别是,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 没有定义
let 也不允许同一个块作用域中出现冗余声明。
- 暂时性死区
let 与 var 的另一个重要的区别,就是 let 声明的变量不会在作用域中被提升。
// name 会被提升
console.log(name); // undefined
var name = 'Matt';
// age 不会被提升
console.log(age); // ReferenceError:age 没有定义
let age = 26;
在解析代码时,JavaScript 引擎也会注意出现在块后面的 let 声明,只不过在此之前不能以任何方式来引用未声明的变量。在 let 声明之前的执行瞬间被称为“暂时性死区”(temporal dead zone),在此阶段引用任何后面才声明的变量都会抛出 ReferenceError
- 全局声明
与 var 关键字不同,使用 let 在全局作用域中声明的变量不会成为 window 对象的属性(var 声明的变量则会)
var name = 'Matt';
console.log(window.name); // 'Matt'
let age = 26;
console.log(window.age); // undefined
- 条件声明
<script>
var name = 'Nicholas';
let age = 26;
</script>
<script>
// 假设脚本不确定页面中是否已经声明了同名变量
// 那它可以假设还没有声明过
var name = 'Matt';
// 这里没问题,因为可以被作为一个提升声明来处理
// 不需要检查之前是否声明过同名变量
let age = 36;
// 如果 age 之前声明过,这里会报错
</script>
- for 循环中的 let 声明
在 let 出现之前,for 循环定义的迭代变量会渗透到循环体外部:
for (var i = 0; i < 5; ++i) {
// 循环逻辑
}
console.log(i); // 5
改成使用 let 之后,这个问题就消失了,因为迭代变量的作用域仅限于 for 循环块内部:
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 name = 'Matt';
const name = 'Nicholas'; // SyntaxError
// const 声明的作用域也是块
const name = 'Matt';
if (true) {
const name = 'Nicholas';
}
console.log(name); // Matt
const 声明的限制只适用于它指向的变量的引用。换句话说,如果 const 变量引用的是一个对象,那么修改这个对象内部的属性并不违反 const 的限制。
const person = {};
person.name = 'Matt'; // ok
const 在 for 循环中的示例:
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-of 和 for-in 循环特别有意义:
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、声明风格及最佳实践
ES6 增加 let 和 const 从客观上为这门语言更精确地声明作用域和语义提供了更好的支持。
-
不使用 var
有了 let 和 const,大多数开发者会发现自己不再需要 var 了。限制自己只使用 let 和 const 有助于提升代码质量,因为变量有了明确的作用域、声明位置,以及不变的值。 -
const 优先,let 次之使用 const 声明可以让浏览器运行时强制保持变量不变,也可以让静态代码分析工具提前发现不合法的赋值操作。因此,很多开发者认为应该优先使用 const 来声明变量,只在提前知道未来会有修改时,再使用 let。这样可以让开发者更有信心地推断某些变量的值永远不会变,同时也能迅速发现因意外赋值导致的非预期行为
5、总结
-
3 个关键字可以声明变量:var、const 和 let。
-
定义变量:操作符+变量名(标识符)
-
var 声明的特点:
(1)可以省略关键字自动变为全局变量
(2)可以重复声明
(3)可以先访问后声明(声明提升)
(4)声明范围是函数作用域
- let 声明的特点:
(1)未声明不可使用,会报错
(2)不可以重复声明
(3)声明范围是块作用域
- const 声明的特点:
(1)包含 let 所以特点
(2)声明变量时必须同时初始化变量
(3)值类型不可修改,引用类型不可修改引用地址
- var、let、const 共同点:都可以声明为全局作用域
网友评论