七.变量的使用需要注意哪些?
变量
ECMAScript 变量是松散类型的,意思是变量可以用于保存任何类型的数据。每个变量只不过是一 个用于保存任意值的命名占位符。有 3 个关键字可以声明变量:var、const 和 let。其中,var 在 ECMAScript 的所有版本中都可以使用,而 const 和 let 只能在 ECMAScript 6 及更晚的版本中使用。
var 声明提升
使用 var 时,下面的代码不会报错。这是因为使用这个关键字声明的变量会自动提升到函数作用域 顶部:
function foo(){
console.log(age);
var age = 26;
}
foo(); // undefined
复制代码
function foo(){
var age;
console.log(age);
age= 26;
}
foo(); // undefined
复制代码
这就是所谓的“提升”(hoist),也就是把所有变量声明都拉到函数作用域的顶部。此外,反复多次 使用 var 声明同一个变量也没有问题:
function foo(){
var age = 16;
var age = 16;
var age = 16;
console.log(age)
}
foo(); // 36
复制代码
let 声明
let 跟 var 的作用差不多,但有着非常重要的区别。最明显的区别是,let 声明的范围是块作用域, 而 var 声明的范围是函数作用域。
if(true){
var name = 'Mait';
console.log(name); // Mait
}
console.log(name); // Matt
复制代码
if(true){
let age = 26;
console.log(age); // 26
}
console.log(age); // ReferenceError: age 没有定义
复制代码
let 也不允许同一个块作用域中出现冗余声明。这样会导致报错:
var name;
var name;
let age;
let age; // SyntaxError;标识符 age 已经声明过了
复制代码
暂时性死区:
let 与 var 的另一个重要的区别,就是 let 声明的变量不会在作用域中被提升。
// name 会被提升
console.log(name); // undefined
var name = "Matt";
// age 不会被提升
console.log(age); // ReferenceError:age 没有定义
let age = 26;
复制代码
全局声明: 与 var 关键字不同,使用 let 在全局作用域中声明的变量不会成为 window 对象的属性(var 声 明的变量则会)。
var name = "Mait";
console.log(window.name); // 'Matt';
let age= 26;
console.log(window.age); // undefined
复制代码
不过,let 声明仍然是在全局作用域中发生的,相应变量会在页面的生命周期内存续。因此,为了 避免 SyntaxError,必须确保页面不会重复声明同一个变量。
条件声明 在使用 var 声明变量时,由于声明会被提升,JavaScript 引擎会自动将多余的声明在作用域顶部合 并为一个声明。因为 let 的作用域是块,所以不可能检查前面是否已经使用 let 声明过同名变量,同 时也就不可能在没有声明的情况下声明它。
<script>
var name = "Mine me";
let age= 37;
// 当前已经声明过
var name = "Mine me- to"; // 不会检查,会作为一个变量提升来声明
let age = 36;
// 如果之前声明age,这里会报错,已经重复声明了
</script>
复制代码
为此,对于 let 这个新的 ES6 声明关键字,不能依赖条件声明模式。
注意 不能使用 let 进行条件式声明是件好事,因为条件声明是一种反模式,它让程序变 得更难理解。如果你发现自己在使用这个模式,那一定有更好的替代方式。
for 循环中的 let 声明
在 let 出现之前,for 循环定义的迭代变量会渗透到循环体外部:
for (let i = 0; i<5; ++i){
// 循环逻辑
}
console.log(i) // i 没有定义
复制代码
在使用 var 的时候,最常见的问题就是对迭代变量的奇特声明和修改:
for(var i=0; i<5; ++i){
setTimeout(() =>{
console.log(i),0)
})
}
//
复制代码
const的使用
常量是块级范围的,非常类似用 [let]语句定义的变量。但常量的值是无法(通过重新赋值)改变的,也不能被重新声明。
const number = 42;
console.log(number) // 42
复制代码
const name1 = value1 [, name2 = value2 [, ... [, nameN = valueN]]];
复制代码
-
nameN
常量名称,可以是任意合法的[标识符]
valueN
-
常量值,可以是任意合法的[表达式]
此声明创建一个常量,其作用域可以是全局或本地声明的块。与([
var
])变量不同,全局常量不会变为 window 对象的属性。需要一个常数的初始化器;也就是说,您必须在声明的同一语句中指定它的值(这是有道理的,因为以后不能更改)
const
声明创建一个值的只读引用。但这并不意味着它所持有的值是不可变的,只是变量标识符不能重新分配。例如,在引用内容是对象的情况下,这意味着可以改变对象的内容(例如,其参数)。
关于“[暂存死区]”的所有讨论都适用于[let
]和const
。 一个常量不能和它所在作用域内的其他变量或函数拥有相同的名称
const 基本用法
常量在声明的时候可以使用大小写,但通常情况下全部用大写字母。
// 定义常量 MY_FAV 并赋值 7
const MY_FAV = 7;
// 报错 - 未捕捉到分配的常量 常量不可重新赋值
MY_FAV = 20;
// MY_FAV is 7
console.log('my favorite number is: ' + MY_FAV);
// 尝试重新声明会报错
// 语法错误:已声明标识符“MY_FAV”
const MY_FAV = 20;
// MY_FAV 保留给上面的常量,这个操作会失败
var MY_FAV = 20;
// 也会报错
let MY_FAV = 20;
复制代码
块作用域
const 也会涉及到块级作用域
const MY_FAV = 7;
if (MY_FAV === 7) {
// 没问题,并且创建了一个块作用域变量 MY_FAV
// (works equally well with let to declare a block scoped non const variable)
let MY_FAV = 20;
// MY_FAV 现在为 20
console.log("my favorite number is4564564564 " + MY_FAV);
// 这被提升到全局上下文并引发错误
// var MY_FAV = 20;
}
// MY_FAV 依旧为 7
console.log("my favorite number is " + MY_FAV);
复制代码
常量要求一个初始值
// 报错
// 必须有一个初始化值
declaration
const FOO;
复制代码
常量可以定义成对象和数组
const MY_OBJECT = {'key': 'value'};
// 重写对象和上面一样会失败
MY_OBJECT = {'OTHER_KEY': 'value'};
// 对象属性并不在保护的范围内
// 下面这个声明会成功执行
MY_OBJECT.key = 'otherValue';
// 也可以用来定义数组
const MY_ARRAY = [];
// 可以向数组填充数据
MY_ARRAY.push('A'); // ["A"]
// 但是,将一个新数组赋给变量会引发错误
MY_ARRAY = ['B'];