Let 声明方式
不属于顶层对象 window
let b = 50
console.log(window.b)
console.log(b)
var a = 5
console.log(window.a)
window.b 为 undefined, 说明 let 声明的 变量 不会挂载到 顶层 对象 window 下,同样 不会 导致 全局变量 遭到污染的 情况出现
不允许重复声明变量
var a = 1
console.log(a)
var a = 10
console.log(a)
使用 var 重复声明同一个变量 ,不会报错。对前一个声明的变量进行值的覆盖
let b = 9
console.log(b)
let b = 90
console.log(b)
使用 let 不可以重复声明同一个变量
let 使我们的项目中的代码跟家的规范,不会导致 同一个变量名被重复声明,并且前一个 声明的 变量的值 也被覆盖,导致项目出现bug
不存在变量提升问题
console.log(a)
var a = 555
这种写法其实等价于
var a
console.log(a)
a = 555
这就是变量的提升,这样的做法并不好,会导致 项目出现 意想不到的 bug
而使用 let 则可以避免这个问题
console.log(b)
let b = 555
暂时性死区
var a = 5
if (true) {
a = 6
var a
console.log(a)
}
大括号会形成一个封闭的作用域,大括号外的变量无法影响到 括号内的 数据,如果没有声明直接使用就会报错
var a = 5
if (true) {
a = 6
let a
console.log(a)
}
类似的 场景还会出现在 函数的 参数中
function foo(a = b, b = 2) {
console.log(a,b)
}
foo()
暂时性死区的本质就是防止 变量在 未声明之前就去使用它,提高我们代码的安全度+
块级作用域
for (var i = 0; i < 3; i++) {
console.log('括号内: ',i)
}
console.log('括号外:',i)
使用 let 声明
for (let i = 0; i < 3; i++) {
console.log('括号内: ',i)
}
console.log('括号外:',i)
es6中 大括号 内就是一个 块级作用域,括号外是无法拿到 括号内声明的变量
var 是没有块级作用域的,let 是有的
if (false) {
var a = 5;
}
console.log(a); // undefined
这里 如果 var 存在 块级作用域的话 就会直接报错,而不是输出 undefined。
因为 es6 中 块级作用域的存在,我们以后使用 if的 时候必须使用 大括号,es5 中 if 后如果只是 一行代码,大括号可以省去,但是因为 es6 中 存在块级作用域,这个 大括号就不可以省去了, 否则会报错
if (true) let a = 5
练习题
for (var i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i)
})
}
因为 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)
}
闭包的特点
闭包 有一个外部函数,有一个内部函数, 内部函数 调用 外部函数 传进来的 变量,保持对 变量的 持续引用。
外部函数参数i, 传入内部函数定时器内的参数 j,然后打印输出
同样的效果
可以使用 let 进行变量的 声明
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i)
});
}
使用 新的 声明方式 会让我们的 代码更加的健壮
const 声明方式
他的特点和 let 基本一致,但是 const 是专门用来处理 声明常量, 声明完的常量 如果被修改则会报错
let a = 5
a = 6
console.log('a',a)
const b = 5
b = 6 // 会报错
console.log('b',b)
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)
这里没有报错的 原因 是 和 js存储 变量的方式 有关
基本类型变量 使用 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)
const 定义 数组
const arr = [1,2,3]
console.log(arr)
arr.push(4)
console.log(arr)
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 属性 就没有添加进去
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)
从这里可以看出 ,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)
const 与 let 的使用场景
很简单,当我们进行变量定义时,只要考虑该变量,在后面的代码中是否会发生变化。
如果,该变量我们只会使用初始值,或者他是引用类型,在后面的代码中不会发生内存地址的改变,那就可以使用 const 去定义
如果,这个变量在后面的业务代码中,值发生了变化,那就使用let就可以了
根据本人的工作经验与习惯,能用 const 定义变量,优先考虑 const, 尽可能的减少数据被污染的可能性。