JavaScript变量声明let、const、var
前言
let、const
let和const是一种新的声明方式,弥补了var声明的很多弊端。
var
var
是最早期在js中声明变量的一种方式,用var
声明变量相当于是在当前作用域内声明,与此同时也有不使用var
关键字进行声明变量的方式,但不使用var
进行声明的变量,会直接跟当前全局顶级对象挂钩,在浏览器环境中那就是window
对象。
代码如下:
var a = 5
console.log(`a is ${a}`)
b = 6
console.log(`b is ${b}`)
console.log(`window.b === b ? ${window.b === b}`)
浏览器输出如图
而var
声明是在当前作用域内声明该怎么理解呢?把代码稍微一改。
function print() {
// a仅在print()方法内可见
var a = 5
}
console.log(`a is ${a}`)
b = 6
console.log(`b is ${b}`)
console.log(`window.b === b ? ${window.b === b}`)
浏览器输出报错了
这是因为var a = 5
是在print()
方法内声明的,也就是仅存在于print()
作用域中,成了一个局部变量,外界无法访问。如果再把var a = 5
的声明放到print()
方法外,那么它就是一个全局变量。也就是说声明它的地方决定了它在哪个作用域可见。
let
不属于顶层对象window
用let
声明变量的话,则变量不会属于顶层对象window。
用var
也不会,可以先使用var
声明然后验证一下。
var a = 5
console.log(`a is ${a}`)
console.log(`window.a === ${window.a}`)
b = 6
console.log(`b is ${b}`)
console.log(`window.b === b ? ${window.b === b}`)
接下来把var
改成let
let a = 5
console.log(`a is ${a}`)
console.log(`window.a === ${window.a}`)
b = 6
console.log(`b is ${b}`)
console.log(`window.b === b ? ${window.b === b}`)
不允许重复声明
用var
声明来做比较,可以看到var
重复声明了变量a多次,但是变量a的值只取最后一次声明。
var a = 100
var a = 101
console.log(`a is ${a}`)
换成let
,则直接报错,提示a不可以重复声明
let a = 100
let a = 101
console.log(`a is ${a}`)
不存在变量提升
用var
声明作比较,可以看到变量a其实可以被正常访问的,只是因为尚未赋值,所以为undefined
console.log(`a is ${a}`)
var a = 100
以上代码其实等同于
var a
console.log(`a is ${a}`)
a = 100
换成let
console.log(`a is ${a}`)
let a = 100
这里其实应该是要报错的,但是不知道为什么并没有,也许是我创建的node
项目什么地方没配置好
换到浏览器中试一试,可以看到提前访问let
声明的变量,会抛出异常
暂时性死区
暂时性死区
相当于一个封闭的作用域,在这个作用域内,变量必须先声明才能使用。
比如如下代码,在if (true) {...}
中形成了一个函数作用域
,在这个作用域内,let
声明的变量a,是不可以在声明前使用的。
var a= 5
if (true) {
a = 7
let a
}
块级作用域
ES6之前,只有全局作用域
和函数作用域
,比如如下代码其实是不合理的,因为是在循环体中声明的,它不应该能在循环体外被访问到
for (var i = 0; i < 5; i++) {
i ++
}
console.log(i)
换成let
的话,变量i则仅限于在循环体中被访问,也就是形成了一个块级作用域
for (let i = 0; i < 5; i++) {
i ++
}
console.log(i)
const
使用const
声明的变量将被表示为不可改变的常量。并且let
有的几种特性,const
也都有。
比如如下代码,常量b的值在声明时就已经做了初始化,并不可更改,否则会抛出异常。
const b = 100
b = 100
console.log(`b is ${b}`)
若const
修饰的常量的值不是基本类型
,而是引用类型
。比如Object
的话,那么实际上不可更改的只是指向该Object
的指针,也就是内存地址
。
以下代码可以看到,b指向了一个Object
,然后对该Object
的a属性进行改值操作,代码还是能够正常执行。
const b = {a: 100, c: 100}
b.a = 200
console.log(`b is ${b.a}`)
但如果直接将b重新赋值,那么也还是会抛出异常
const b = {a: 100, c: 100}
b = {a: 100}
defineProperty
使用Object.defineProperty()
也能达到同样的效果。代码如下
Object.defineProperty(window, 'b', {
value: 100,
writable: false // 禁止修改
})
b = 120
console.log(`b is ${b}`)
Object.freeze()
若不想Object
的属性被更改,那么则可以使用Object.freeze()
方法。
代码如下,可以看到即便修改了b的a属性值为500,其实最后输出的也还是100
let b = {a: 100, c: 200}
Object.freeze(b) // 冻结
b.a = 500
console.log(`b.a is ${b.a}`)
若Object
比较复杂,包含多重嵌套Object
作为属性值的话,简单的Object.freeze()
是不够的。
代码如下:
let b = {a: 100, c: 200, d: {e: 100}}
Object.freeze(b)
b.d.e = 200
console.log(`b.a is ${b.d.e}`)
还需要冻结b里面的d
let b = {a: 100, c: 200, d: {e: 100}}
Object.freeze(b)
Object.freeze(b.d) // 冻结内嵌的对象
b.d.e = 200
console.log(`b.a is ${b.d.e}`)
转载自:https://juejin.cn/post/7268813574250725395