“ 以下内容基于《JavaScript高级程序设计》这本书创作 ”
公众号后台回复 js 可获得本书电子版
文章大纲
变量可以保存任何类型的数据
有三个关键字可以声明变量:var、let、const,其中var在ECMAScirpt的所有版本中均可使用,而let和const只能在ES6及更晚的版本中可以使用
以下对var、let和const关键字进行对比:
先说结论:var和let不会初始化,而const会初始化
证明:
1)var
var uname = 'dapan';
uname = 32;
console.log(uname);
这里,uname被定义为一个保存字符串值'dapan'的变量,var的初始化并不会将uname标识为字符串类型,只是一个简单的赋值而已,随后不仅可以改变保存的值,还可以改变值的类型。
2)let
let uname = 'dapan';
uname = 32;
console.log(uname);
这里,let声明的变量和var一样,可以改变值,也可以改变值的类型
3)const
const uname = 'dapan';
uname = 'Dapan';
console.log(uname);
报错:
这里,const声明变量时必须同时初始化变量,尝试修改const声明的变量会导致运行时报错
先说结论:var声明的作用域是函数作用域,let和const声明的作用域是块级作用域
证明:
1)var
function demo() {
var city = 'guangzhou';
}
demo();
console.log(city);
报错:
这里,city变量是在函数demo()内部用var定义的,调用它会创建city变量,调用之后变量随即被销毁,故会报错
修改后:
function demo() {
var city = 'guangzhou';
console.log(city);
}
demo();
函数内部打印则不报错;
去掉var关键字:
function demo() {
city = 'guangzhou';
}
demo();
console.log(city);
同样可以打印出来city的值,因为如果忽略var操作符,可以创建一个全局变量,那么只要调用一次demo()函数,就会定义city这个全局变量,并且可以在函数外部访问到(但是并不推荐省略var操作符定义全局变量)
2)let
先看函数作用域:
function demo() {
let city = 'guangzhou';
}
demo();
console.log(city);
再看块级作用域:
if (true) {
let city = 'guangzhou';
}
console.log(city);
报错:
修改为在块作用域里面打印:
if (true) {
let city = 'guangzhou';
console.log(city);
}
因为块作用域是函数作用域的子集,所以let在声明变量时,函数外边和块作用域外边都访问不到变量
3)const
const和let的作用域一样,简单看一下不再做解释:
function demo() {
const city = 'guangzhou';
}
demo();
console.log(city);
和
if (true) {
const city = 'guangzhou';
}
console.log(city);
均这个报错:
if (true) {
const city = 'guangzhou';
console.log(city);
}
正常打印:
先说结论:var不会报错,let和const会报错:
1)var
var sex = 'boy'
var sex = 'girl'
console.log(sex);
2)let
let sex = 'boy'
let sex = 'girl'
console.log(sex);
但是在不同块作用域是可以重复声明的:
let sex = 'boy';
if (true) {
let sex = 'girl';
console.log(sex);
}
console.log(sex);
且对声明冗余报错不会因为混用var和let而受影响:
var sex
let sex
3)const
const sex
const sex
和let一样,const也允许在不同块作用域内重复声明变量,且混用var和const声明同样会报错
先说结论:var声明的变量会在作用域内提升变量,而let和const不会
1)var
console.log(age)
var age = 100
这里,并没有报错是因为var声明变量时把变量提升到了作用域的顶部,上边代码其实等价于下边这段代码:
var age;//提升了声明变量
console.log(age);
age = 100;
2)let
console.log(age);
let age = 100;
报错:
let和var的一个重要区别就是let声明的变量不会在作用域中被提升
3)const
和let关键字一样,不能被提升,不再赘述
与var关键字不同,使用let和const声明的变量不会成为window对象的属性(var变量则会)
证明 :
var x = 99;
console.log(window.x);
let x = 99;
console.log(window.x);
和
const x = 99;
console.log(window.x);
打印结果:
在let出现之前,for循环定义的迭代变量会渗透到循环体外部:
for (var i = 0; i < 5; i++) {
//循环逻辑
}
console.log(i);
打印结果:
改成let之后这个问题就消失了:因为迭代变量的作用仅限于for循环块内部:
for (let i = 0; i < 5; i++) {
//循环逻辑
}
console.log(i);
报错:
使用var时,最常见的问题就是对迭代变量的奇特声明和修改。判断下边代码的输出:
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, 0);
}
你可能以为输出的是0、1、2、3、4,但其实输出的是5、5、5、5、5,之所以会这样是因为在退出循环时,迭代变量保存的时导致循环退出的值:5。在之后执行超时逻辑时,所有的i都是同一个变量,因而输出的都是5。
而在使用let声明迭代变量时,JavaScript引擎在后台会为每个迭代循环声明一个新的迭代变量。每个setTimeout引用的都是不同的变量实例,所以console.log输出的是我们期望的值。
for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, 0);
}
打印:
思考:上边这段代码能不能把let替换为const,为什么?
摘自《JavaScript高级程序设计》
可以的话,希望能够转发分享,点个在看并且点个赞~~也欢迎规范转载~