变量声明
ECMAScript 中的变量是松散类型的,意思是变量可以用于保存任何类型的数据。每个变量只不过是一个用于保存任意值的命名占位符。
JavaScript有三个声明变量的关键字:var, const, let。其中 const, let 只能在 ECMAScript 及更晚的版本中使用。
var
var message;
这样就定义了一个名为 message 的变量,可以用它保存任何类型的值。
ECMAScript 实现变量初始化,因此可以同时定义变量并设置它的值。
var message = "hi";
定义好之后,不仅可以改变值,甚至可以改变它的类型。
var message = "hi";
message = 100;
这样虽然合法,但不推荐,也很少看到程序员会这样做。
对于多个变量可以一次性声明:
var message = "hi",
found = "false",
age = 29;
因为 ECMAScript 是松散类型的,所以使用不同数据类型初始化也可以用一条语句。
var 可以重复声明同一个变量:
var age = 16;
var age = 26;
var age = 36;
var 声明作用域
使用 var 声明的变量会成为包含它的函数的局部变量。比如,在一个函数内部 var 一个变量,这个变量将在函数退出时被销毁。
function test() {
var message = "hi";
}
test();
console.log(message);
因为 message 在函数调用完后已经被销毁,所以上面的代码在试图打印的时候就会报错。这种时候可以省略 var,将其声明为全局变量。
function test() {
message = "hi";
}
test();
console.log(message);
var 变量声明提升
console.log(age);
var age = 26;
function test() {
console.log(age);
var age = 30;
}
像这样的代码是不会报错的,并不是解析器没有按顺序。而是 var 声明会被提升到顶部,变成
var age;
console.log(age);
age = 26;
function test() {
var age;
console.log(age);
var age = 30;
}
这就是变量声明提升机制(hoist)。
let
let 声明的是块作用域,而 var 声明的是函数作用域。这是他们两个最明显的区别。
if (true) {
var name = "Matt";
console.log(name); // Matt
}
console.log(name); // Matt
if (true) {
let name = "Matt";
console.log(name); // Matt
}
console.log(name); // undefined
用 let 声明的变量只能在 if 块内使用。它的作用域范围比 var 小,所以自然也不能在函数外被使用。
并且 let 也不允许反复声明同一个变量,这个特性在有些时候被称为声明屏蔽:
let age;
let age;
会报 SyntaxError,标识符已经声明过了。
而正因为 let 声明的作用域在块内,所以下面这样嵌套使用相同的标识符是合法的。
let age = 30;
if (true) {
let age = 26;
}
let 没有变量声明提升,只有暂时性死区
不能先使用再声明,下面这样报错
console.log(age);
let age = 27;
在 let 声明之前的执行瞬间被称为暂时性死区(temporal dead zone),会抛出 ReferenceError 这个错误。
let 全局声明的变量不会成为window 对象的属性
这与 var 不同:
var name = "Matt";
console.log(window.name); // Matt
let age = 26;
console.log(window.age); // undefined
let 不能用条件声明
if (typeof name === "undefined") {
let name;
}
像这样有条件的情况下声明的 let 变量依旧只能在块内使用,不能在有条件的情况下声明出全局的 let 变量。
for 循环中的 let
这推荐使用 let 而不用 var 的一个重要原因。
使用 var 声明的迭代变量会渗透到循环体外部:
for (var i = 0; i < 5; i++) {
// 循环逻辑
}
console.log(i); // 5
但是使用 let 就不会有这个问题:
for (let i = 0; i < 5; i++) {
// 循环逻辑
}
console.log(i); // ReferenceError: i没有定义
使用 var 的时候还会有一个问题,还经常被拿来当面试题:
for (var i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 0);
}
预想的是输出:0 1 2 3 4
但实际输出的却是:5 5 5 5 5
之所以这样是因为:首先 setTimeout 是一个超时逻辑,它会在循环退出后再开始执行,而循环退出时,迭代变量保存的是导致循环退出的值:5。所有的 i 都是同一个变量,因而输出的都是同一个最终值。 对此其实有一个解决方法是使用立即执行的函数表达式(IIFE)来捕获每次迭代时 i 的值。
for (var i = 0; i < 5; i++) {
(function (i) {
setTimeout(() => console.log(i), 0);
})(i);
}
或者直接let 声明迭代变量。这样JavaScript 会在后台为每个迭代循环声明一个新的迭代变量。所以它会有 5 个 i,值分别是 0、1、2、3、4,每个 setTimeout 引用的都是不同的变量实例。
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 0);
}
这种每次迭代声明一个独立变量实例的行为适用于所有风格的 for 循环,包括 for-in 和 for-of 循环。
const
const 与 let 基本相同,唯一重要区别是
它声明变量时必须同时初始化变量,且声明后的值不能再修改。
const 也不允许重复声明。
const 声明的作用域也是块。
但是 const 只限制它指向的变量。也就是说,如果 const 声明了一个对象,那么修改对象内的属性是合法的。
const person = {};
person.name = "Matt";
const 一般不用来声明迭代变量,因为它不会被修改。但是在 for-in 和 for-of 中却是有意义的:
for (const i = 0; i < 5; i++) {} // TypeError: 给常量赋值
let i = 0;
for (const j = 0; i < 5; i++) {
console.log(j);
}
// 0 0 0 0 0
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
推荐的声明风格
ECMAScript6 增加 let 和 const 支持更精确地声明作用域和语义。
天下苦 var 久矣, 是时候抛弃它了。
1. 不使用 var
有了 let 和 const 之后,var 已经不再被需要。Eslint 代码规范,也要求开发者不要使用 var,并添加了将 var 自动转变成 let 或 const 的功能。
2. const 优先,let 次之
const 声明的变量在浏览器运行时强制保持不变,方便静态代码分析工具提前发现不合法的赋值操作。对于开发者,也可以更容易发现意外赋值导致的非预期行为。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!