ES6 - 新的声明方式let、const

87 阅读3分钟

let

不属于顶层对象window

使用var和不使用var声明的变量一样吗?

==> 是不一样的

使用var声明『当前作用域内』的变量(如果是在函数中声明,那它就是个局部变量)

而不使用var,相当于在顶层对象window上定义了个属性,并对这个属性进行赋值

delete只能删除对象的属性,不能删除变量

通过这点就可以验证上面的结论

var a = 5
console.log(a)
delete a
console.log(a)


b = 6
console.log(b)
delete b
console.log(b)

运行结果,说明b已经被删除

image.png

JS遗留的问题

JS在设计的时候将顶层对象的属性同全局变量进行了挂钩

var a = 5
console.log(a)
console.log(window.a)


b = 6
console.log(b)
console.log(window.b)

image.png

这就会造成一个问题:如果项目中声明了很多的全局变量,都被挂在window下面

导致window这个对象越来越大

就造成了污染全局变量

let - 解决了污染全局变量的问题

let a = 5
console.log(a)
window.log(window.a)

image.png

注意

webapck会对src下面的代码进行打包编译, 对static下面的代码不会打包编译,而是原封不动的移过去 webpack有模块机制,它将src下面的文件进行打包编译的时候,会自动处理一些存在的问题(如:可能会用es6的逻辑处理es5代码中存在的问题)

不允许重复声明

var

var a = 5
var a = 6
console.log(a)

image.png

let

let a = 5
let a = 6
console.log(a)

image.png

这样的好处就是在多人合作时,避免相同变量名的重复声明和对值的覆盖

不存在变量提升

var

console.log(a)
var a = 5

// 相当于
var a
console.log(a)
a = 5

image.png

let

console.log(a)
let a = 5

image.png

暂时性死区

只要块级作用域内存在 let 命令,它所声明的变量就绑定在了这个区域,不再受外部的影响

在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”

// 不报错
var a = 5

if(true){
    a = 6
    var a
}

// 报错
var a = 5

if(true){
    a = 6
    let a
}

块级作用域

用var定义的变量,没有块级作用域

只有全局作用域和函数作用域

let 具有块级作用域(在大括号内),作用域内声明的变量只能在块级作用域内访问

let实际上为 JavaScript 新增了块级作用域

{
    let a = 5
}
console.log(a) // undefined

a 变量是在代码块 {} 中使用 let 定义的,它的作用域是这个代码块内部,外部无法访问

没有块级作用域(一个大括号)导致的问题

var

// var中声明的变量i是全局变量i
for(var i = 0;i < 3;i++){
    console.log('循环内:' + i)
}
console.log('循环外:' + i)

// 相当于
var i
for(i = 0;i < 3;i++){
    console.log('循环内:' + i)
}
console.log('循环外:' + i)

image.png

这样其实不安全,本来是想让变量i,只能在大括号中访问,结果在大括号外面也能访问

let

for(let  i = 0;i < 3;i++){
    console.log('循环内:' + i)
}
console.log('循环外:' + i)

image.png

再看个例子: es6中的块级作用域必须有“{}”

// 不报错
if(true) var a = 5

// 报错: 词法声明不能出现在单个语句的上下文中
if(true) let a = 5
// 应改为
if(true){
  let a = 5
} 

image.png

面试题

for (var i = 0; i < 3; i++) {
    setTimeout(function() {
        console.log(i)
    })
}
// 3、3、3

setTimeout是异步的操作,而for循环是同步的、一瞬间完成的,当setTimeout执行的时候,for循环已经结束了

解决方法1: 闭包: 在函数内访问函数外的变量,那么这个变量连同这个函数就构成了闭包

for (var i = 0; i < 3; i++) {
    (function(j) {
        setTimeout(function() {
            console.log(j)
        })
    })(i)
}

解决方法2: 使用let

for (let i = 0; i < 3; i++) {
    setTimeout(function() {
        console.log(i)
    })
}

const

定义常量

ES5中定义常量

// 在window上定义属性PI
Object.defineProperty(window,'PI',{
    value: 3.14,
    writable: false
})
console.log(PI)
PI = 6
console.log(PI)

image.png

const定义的变量不允许进行重新赋值

内存在存JS的对象时,实际上是在栈内存中存的对象的地址,而堆内存中存的才是对象本身

const定义的基本数据类型不能被重新赋值,而引用数据类型可以被重新赋值

1.const定义基本数据类型

const a=5
a = 6

image.png

2.const定义引用数据类型

const obj = {
    name:'lee',
    age: 18
}
console.log(obj);
obj.名族 = '汉'
console.log(obj);

image.png

const 声明常量和赋值须同时进行

const a
a =5

image.png

数组也是一样

const 规定定义的变量的值不得改动,这个值包括基本数据类型的值,也包括引用数据类型中内存地址的值不得改变

如何让对象或者数组这种引用数据类型也不被改变呢?

Object.freeze(obj) // 只能传普通的对象,不能传数组 

注意

Object.freeze() 只是浅层冻结,只会对外层的对象进行冻结,并不会对嵌套的子对象冻结。

如果也想冻结嵌套的子对象就要手动的再写一次,如:Object.freeze(obj.child)

其他的特性和let相同