let与var
- let 声明具有块级作用域 (如果没有块: 用于计数的循环变量泄露为全局变量)
对于一段代码
1 | var a = []; |
用 var 声明的变量是全局的 这里的i 就是一个,就是全局的i,每次循环 i的值都会发生变化,赋给数组的函数内部的console.log(i) 指的就是外部的i
理解: 数组元素的[i] 与console.log(i)的i 不同,元素这里是立即会获取 增长的i 但是 console.log(i) 只是一个声明,在执行的时候,再去拿i 的值,它拿到的i 的值,是最后增长结束的 i的值,下面这样会有不同
1 | var a = []; |
立即执行的话,会不同 就是输出理想的 1-9
如果使用 let
1 | var a = []; |
这里 i 就是在本轮循环中有效,每次循环的i 都是一个新的变量
JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算
for循环还有一个特别之处,
就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
1 | for (let i = 0; i < 3; i++) { |
let
- let 声明 不存在变量提升
var 声明变量,会发生提升,变量可以在声明之前使用 值 为 undefined
- 声明之前是不能使用的(临时性死区)
- 不允许重复声明
不能在函数内部重新声明参数
1 | function func(arg) { |
没有块级作用域 易造成 用于计数的循环变量泄露为全局变量
1 | var s = 'hello'; |
上面代码中,变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。
const 声明一个只读的常量
本质:
并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动.
对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,
const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了
1 | const foo = {}; |
理解一下:
foo 指向的是一个内存地址这个地址指向了一个数组或者对象,使用const 声明后,这个地址就只能一直指向这一个 数组或者对象,不能指向其他
常量
foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。
1 | const a = []; |
常量
a是一个数组,这个数组本身是可写的,但是如果将另一个数组赋值给a,就会报错
如果需要冻结一个对象
1 | const foo = Object.freeze({}); |
ES6 中声明变量
从 ES6 开始 全局变量将逐步与顶层对象的属性脱钩。
let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。
顶层对象不统一
- 浏览器顶层是 window,Node 和web worker 没有window
- 浏览器和 web worker self 指向顶层,但Node没有self
- Node 顶层是 global,其他环境都不支持。