声明变量的问题
使用var声明变量
- 允许重复的变量声明:导致数据被覆盖
var a = 1;
function print(){
console.log(a)
}
//假设这里有一千行代码
var a = 2;
print();
- 变量提升:怪异的数据访问、闭包问题
怪异数据访问
if (Math.random() < 0.5) {
var a = "abc";
console.log(a);
}
else {
console.log(a);
}
console.log(a);
闭包问题
var div = document.getElementById("divButtons")
// var i,i 被提升到这个位置。
for (var i = 1; i <= 10; i++) {
var btn = document.createElement("button");
btn.innerHTML = "按钮" + i;
div.appendChild(btn);
btn.onclick = function () {
console.log(i); //输出11
}
}
上面不论点击哪个按钮都会输出11,是因为var i = 1,循环这里把i变量提升了。在点击前局部变量i已经被改成11了,所以无论点击哪个都会输出11.
上面的一种解决办法
for (var i = 1; i <= 10; i++) {
var btn = document.createElement("button");
btn.innerHTML = "按钮" + i;
div.appendChild(btn);
(function(i){
btn.onclick = function () {
console.log(i); //这样输出才正常
}
})(i);
}
- 全局变量挂载到全局对象:全局对象成员污染问题
var abc = "123";
console.log(window.abc);
还有一种写法可能是b也会全局污染。
function test(){
b = "abd";
}
作用域种类
- 全局作用域
- 函数作用域
- 块级作用域
使用let声明变量
ES6不仅引入let关键字用于解决变量声明的问题,同时引入了块级作用域的概念
块级作用域:代码执行时遇到花括号,会创建一个块级作用域,花括号结束,销毁块级作用域
声明变量的问题
- 全局变量挂载到全局对象:全局对象成员污染问题
let声明的变量不会挂载到全局对象
- 允许重复的变量声明:导致数据被覆盖
let声明的变量,不允许当前作用域范围内重复声明
在块级作用域中用let定义的变量,在作用域外不能访问
- 变量提升:怪异的数据访问、闭包问题
console.log(a)
let a = 123;
使用let不会有变量提升,因此,不能在定义let变量之前使用它
底层实现上,let声明的变量实际上也会有提升,但是,提升后会将其放入到“暂时性死区”,如果访问的变量位于暂时性死区,则会报错:“Cannot access 'a' before initialization”。当代码运行到该变量的声明语句时,会将其从暂时性死区中移除。
let div = document.getElementById("divButtons");
for (let i = 1; i <= 10; i++) {
let button = document.createElement("button")
button.innerHTML = "按钮" + i;
button.onclick = function () {
console.log(i) //使用的是当前作用域中的i
}
div.appendChild(button)
}
上面循环10次,开启10个作用域,并且每次循环用的自己当前作用域的i,不是同一个变量i,只是名字一样而已。 在循环中,用let声明的循环变量,会特殊处理,每次进入循环体,都会开启一个新的作用域,并且将循环变量绑定到该作用域(每次循环,使用的是一个全新的循环变量)
在循环中使用let声明的循环变量,在循环结束后会销毁
使用const声明常量
const和let完全相同,仅在于用const声明的变量,必须在声明时赋值,而且不可以重新赋值。
实际上,在开发中,应该尽量使用const来声明变量,以保证变量的值不会随意篡改,原因如下:
- 根据经验,开发中的很多变量,都是不会更改,也不应该更改的。
- 后续的很多框架或者是第三方JS库,都要求数据不可变,使用常量可以一定程度上保证这一点。
注意的细节:
- 常量不可变,是指声明的常量的内存空间不可变,并不保证内存空间中的地址指向的其他空间不可变。
const a = {
name: "kevin",
age: 123
};
a.name = "abc";
console.log(a)
const 声明的对象,对象里面的内容是可以改的。
- 常量的命名
- 特殊的常量:该常量从字面意义上,一定是不可变的,比如圆周率、月地距地或其他一些绝不可能变化的配置。通常,该常量的名称全部使用大写,多个单词之间用下划线分割
- 普通的常量:使用和之前一样的命名即可
- 在for循环中,循环变量不可以使用常量 在普通for循环里面有i++,所以不能使用const,而for in循环是可以的,里面的属性又没有修改。
for (const prop in obj) {
console.log(prop)
}