Let 声明方式

不属于顶层对象 window

  1. let b = 50
  2. console.log(window.b)
  3. console.log(b)
  4. var a = 5
  5. console.log(window.a)

image.png
window.b 为 undefined, 说明 let 声明的 变量 不会挂载到 顶层 对象 window 下,同样 不会 导致 全局变量 遭到污染的 情况出现

不允许重复声明变量

  1. var a = 1
  2. console.log(a)
  3. var a = 10
  4. console.log(a)

image.png
使用 var 重复声明同一个变量 ,不会报错。对前一个声明的变量进行值的覆盖

  1. let b = 9
  2. console.log(b)
  3. let b = 90
  4. console.log(b)

image.png
使用 let 不可以重复声明同一个变量
let 使我们的项目中的代码跟家的规范,不会导致 同一个变量名被重复声明,并且前一个 声明的 变量的值 也被覆盖,导致项目出现bug

不存在变量提升问题

  1. console.log(a)
  2. var a = 555

image.png
这种写法其实等价于

  1. var a
  2. console.log(a)
  3. a = 555

这就是变量的提升,这样的做法并不好,会导致 项目出现 意想不到的 bug
而使用 let 则可以避免这个问题

  1. console.log(b)
  2. let b = 555

这样执行代码就会报错

image.png

暂时性死区

  1. var a = 5
  2. if (true) {
  3. a = 6
  4. var a
  5. console.log(a)
  6. }

image.png
大括号会形成一个封闭的作用域,大括号外的变量无法影响到 括号内的 数据,如果没有声明直接使用就会报错

  1. var a = 5
  2. if (true) {
  3. a = 6
  4. let a
  5. console.log(a)
  6. }

image.png
类似的 场景还会出现在 函数的 参数中

  1. function foo(a = b, b = 2) {
  2. console.log(a,b)
  3. }
  4. foo()

image.png
暂时性死区的本质就是防止 变量在 未声明之前就去使用它,提高我们代码的安全度+

块级作用域

  1. for (var i = 0; i < 3; i++) {
  2. console.log('括号内: ',i)
  3. }
  4. console.log('括号外:',i)

image.png
使用 let 声明

  1. for (let i = 0; i < 3; i++) {
  2. console.log('括号内: ',i)
  3. }
  4. console.log('括号外:',i)

image.png
es6中 大括号 内就是一个 块级作用域,括号外是无法拿到 括号内声明的变量

var 是没有块级作用域的,let 是有的

  1. if (false) {
  2. var a = 5;
  3. }
  4. console.log(a); // undefined

这里 如果 var 存在 块级作用域的话 就会直接报错,而不是输出 undefined。

因为 es6 中 块级作用域的存在,我们以后使用 if的 时候必须使用 大括号,es5 中 if 后如果只是 一行代码,大括号可以省去,但是因为 es6 中 存在块级作用域,这个 大括号就不可以省去了, 否则会报错

if (true) let a = 5

image.png

练习题

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

image.png
因为 for 循环是 同步操作,而 setTimeout 是异步操作,等待 for 循环完成了,才会执行 setTimeout 中的语句
而 for 循环执行了 三次,就会执行三次 console.log, 此时
如果想要避免这种 情况的出现, 打印 1,2,3 的话,可以选择 使用 闭包 进行处理

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

image.png

闭包的特点

闭包 有一个外部函数,有一个内部函数, 内部函数 调用 外部函数 传进来的 变量,保持对 变量的 持续引用。
外部函数参数i, 传入内部函数定时器内的参数 j,然后打印输出

同样的效果
可以使用 let 进行变量的 声明

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

image.png

使用 新的 声明方式 会让我们的 代码更加的健壮

const 声明方式

他的特点和 let 基本一致,但是 const 是专门用来处理 声明常量, 声明完的常量 如果被修改则会报错

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

const b = 5
b = 6 // 会报错
console.log('b',b)

image.png

es5定义常量
原来js中定义一个window对象下的常量的方式, 是通过Object.defineProperty()方法处理,
同时 writable 表示是否可改写,false 表示不可变,这样 PI 即使被重复赋值,其值也不会发生变化

Object.defineProperty(window, 'PI', {
  value: 3.14,
  writable: false
})
console.log(PI) // 3.14

PI = 5
console.log(PI) // 3.14

const 定义 对象

const object={
    name: 'simpleSky12',
    age: '24'
}
console.log(object)

object.schoool = 'imooc'
console.log(object)

image.png
这里没有报错的 原因 是 和 js存储 变量的方式 有关
image.png
基本类型变量 使用 const 声明后,重新赋值,会报错,
基本类型包括:string,number,boolean,undefined,null;
但是 引用类型变量,如对象 和数组,引用类型是存在堆内存中,栈内存中存放的是其 引用地址
只要不是栈内存中的 引用地址被改变,就不会报错,我们只要不是将 const 定义的 对象重新赋值一个 新的 对象就不会报错

const object={
    name: 'simpleSky12',
    age: '24'
}
console.log(object)

object.schoool = 'imooc'
console.log(object)
// 这种 做法就是 对 对象引用内存的 重新赋值
object = new Object();
console.log(object)

image.png

const 定义 数组

const arr = [1,2,3]
console.log(arr)
arr.push(4)
console.log(arr)

image.png
const 对于数组的定义 与 对于 object 的 定义 是 一个道理,只要 变量 所指向的 内存地址 没有发生变化,就不会报错

对象的 冻结 Object.freeze()

Object.freeze() 方法只能适用于对象,不可以对数组使用, 对象如果被冻结了,其内部的属性就不会发生改变

const object={
    name: 'simpleSky12',
    age: '24'
}
console.log(object)
Object.freeze(object)
object.schoool = 'imooc'
console.log(object)

这里的 school 属性 就没有添加进去
image.png

Object.freeze() 是否为 浅层冻结

const object={
    name: 'simpleSky12',
    age: '24',
    skill: {
        name: 'code',
        year: 3
    }
}
console.log(object)
Object.freeze(object)
object.skill.year = 2
console.log(object)

image.png
从这里可以看出 ,Object.freeze() 只是浅层冻结, 他并没用 将object下的 skill 一起冻结
我们可以使用 Object.freeze(object.skill) 来进行深层冻结, 类似于手动递归,处理

const object={
    name: 'simpleSky12',
    age: '24',
    skill: {
        name: 'code',
        year: 3
    }
}
console.log(object)
Object.freeze(object.skill)
object.skill.year = 2
console.log(object)

image.png

const 与 let 的使用场景

很简单,当我们进行变量定义时,只要考虑该变量,在后面的代码中是否会发生变化。

如果,该变量我们只会使用初始值,或者他是引用类型,在后面的代码中不会发生内存地址的改变,那就可以使用 const 去定义

如果,这个变量在后面的业务代码中,值发生了变化,那就使用let就可以了

根据本人的工作经验与习惯,能用 const 定义变量,优先考虑 const, 尽可能的减少数据被污染的可能性。