自从ES6新增后给前端的js代码书写带来很大的变化或者便利性。其中对于变量的定义var,let,const我们就来说说之间的区别吧。
let声明变量有以下几个特点:
1.没有预解析过程(即没有变量提升,var有变量提升)
2.不可以重复声明。
3.{}代表块级作用域,let有块级作用域,var只有函数作用域。
下面我们就一一举例来说明这些问题。
预解析问题:
function sayname() {
console.log(name)
console.log(age)
var name = "lili"
let age = "20"
}
sayname()
// undefined
// Uncaught ReferenceError
上面打印也很好理解:var 有变量提升,有个预解析的过程,所以在打印之前相当于先var name,然后再name = “lili”,所以第一个打印为undefined。第二个打印因为let没有预解析过程,所以就直接报错了。
下面我们来讲讲js中的变量提升与作用域的问题:
我们来先看一段代码,看看会打印什么:
console.log(a)
var a = 1
console.log(a)
function a(){}
console.log(a)
结果是:
f a(){}
1
1
为什么呢?
这代码执行的情况是这样的:
先预编译var a = undefined,再预编译function a(){},函数a覆盖了a的默认值undefined,再a = 1
所以JS的预解析大致可以概括为:
1.从代码开始搜索直到结尾,只去查var function和参数等内容
2.遇到函数与变量重名的,只留函数
3.遇到函数与函数重名的只留最后一个。
4.所有函数,在正式运行代码之前,都是整个函数块。
重复声明问题:
var a = 1
var a = 3
let b = 2
let b = 3
很简单,当你在一个块级作用域声明了一个b,你不能再声明b,否则会报错,当然在不同的块级作用域是可以重复声明的。然而var就可以
块级作用域与函数作用域问题:
function tes(){
var a = 'var ok'
let b = 'let ok'
for (var i = 0; i < 1; i++) {
var a = 'var change'
let b = 'let change'
}
console.log(a)
console.log(b)
}
tes()
打印:var change let ok
因为var只有函数作用域的概念,所以命名相同参数在同一作用域下会覆盖的,而let有块级作用域,function和for循环是两个不同的作用域,所以打印b不会取不同作用域的值。
类似的,在for循环中的定时器打印数据的问题上两者写法也有很大不同:
var 传统的写法,需要用闭包:
for (var i = 0;i < 5;i++){
(function(a){
setTimeout(function(){
console.log(a)
},1000)
})(i)
}
运用闭包参数被引用的缓存机制,使得不会被销毁。
let的写法:
for (let i=0; i<5;i++){
setTimeout(function(){
console.log(i)
},1000)
}
相当于let定义的i有两个作用域,let i一直被引用,所以不会被销毁
const讲解:
const声明常量,只可以声明一次,并且必须赋值,可以防止命名冲突,防止之后发生错误的修改。
const g = 1
const g=2 // 报错
g=2 // 报错
但是下面的修改却成功修改成功了,这是为什么呢?
const d = {
name:'allen'
}
d.name = 'zhaosi'
console.log(d)
因为const只是修饰栈空间的地址不可更改,并没有修饰堆空间的值不可修改。