likes
comments
collection
share

JavaScript变量声明let、const、var

作者站长头像
站长
· 阅读数 16

前言

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}`)

浏览器输出如图

JavaScript变量声明let、const、var

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}`)

浏览器输出报错了

JavaScript变量声明let、const、var

这是因为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}`)

JavaScript变量声明let、const、var

接下来把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}`)

JavaScript变量声明let、const、var

不允许重复声明

var声明来做比较,可以看到var重复声明了变量a多次,但是变量a的值只取最后一次声明。

var a = 100
var a = 101
console.log(`a is ${a}`)

JavaScript变量声明let、const、var

换成let,则直接报错,提示a不可以重复声明

let a = 100
let a = 101
console.log(`a is ${a}`)

JavaScript变量声明let、const、var

不存在变量提升

var声明作比较,可以看到变量a其实可以被正常访问的,只是因为尚未赋值,所以为undefined

console.log(`a is ${a}`)
var a = 100

JavaScript变量声明let、const、var

以上代码其实等同于

var a
console.log(`a is ${a}`)
a = 100

换成let

console.log(`a is ${a}`)
let a = 100

这里其实应该是要报错的,但是不知道为什么并没有,也许是我创建的node项目什么地方没配置好 JavaScript变量声明let、const、var

换到浏览器中试一试,可以看到提前访问let声明的变量,会抛出异常

JavaScript变量声明let、const、var

暂时性死区

暂时性死区相当于一个封闭的作用域,在这个作用域内,变量必须先声明才能使用。

比如如下代码,在if (true) {...}中形成了一个函数作用域,在这个作用域内,let声明的变量a,是不可以在声明前使用的。

var  a= 5
if (true) {
    a = 7
    let a 
}

JavaScript变量声明let、const、var

块级作用域

ES6之前,只有全局作用域函数作用域,比如如下代码其实是不合理的,因为是在循环体中声明的,它不应该能在循环体外被访问到

for (var i = 0; i < 5; i++) {
    i ++
}
console.log(i)

JavaScript变量声明let、const、var

换成let的话,变量i则仅限于在循环体中被访问,也就是形成了一个块级作用域

for (let i = 0; i < 5; i++) {
    i ++
}
console.log(i)

JavaScript变量声明let、const、var

const

使用const声明的变量将被表示为不可改变的常量。并且let有的几种特性,const也都有。

比如如下代码,常量b的值在声明时就已经做了初始化,并不可更改,否则会抛出异常。

const b = 100
b = 100
console.log(`b is ${b}`)

JavaScript变量声明let、const、var

const修饰的常量的值不是基本类型,而是引用类型。比如Object的话,那么实际上不可更改的只是指向该Object的指针,也就是内存地址

以下代码可以看到,b指向了一个Object,然后对该Object的a属性进行改值操作,代码还是能够正常执行。

const b = {a: 100, c: 100}
b.a = 200
console.log(`b is ${b.a}`)

JavaScript变量声明let、const、var

但如果直接将b重新赋值,那么也还是会抛出异常

const b = {a: 100, c: 100}
b = {a: 100}

JavaScript变量声明let、const、var

defineProperty

使用Object.defineProperty()也能达到同样的效果。代码如下

Object.defineProperty(window, 'b', {
    value: 100,
    writable: false // 禁止修改
})
b = 120
console.log(`b is ${b}`)

JavaScript变量声明let、const、var

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}`)

JavaScript变量声明let、const、var

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}`)

JavaScript变量声明let、const、var

还需要冻结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}`)

JavaScript变量声明let、const、var