在这之前先了解什么是顶层对象:在浏览器里指的是window 在Node指的是global
因此 在es5中,全局对象总是能在顶层对象的属性中找到
因为顶层对象在任何一处都能读取,这样就造成了全局对象也处处能读取到而不小心被改写,不利于模块化开发
es6 中let const 的出现正是为了解决这种问题的
window.global_a = 'a' var global_a = 'b' window.global_a // 'b' const global_b = 'const b' window.global_b = 'window b' window.global_b // 'window b'
关于let
let是es6新增的命令,用来声明变量,用法跟var类似,但是所声明的变量只在let命令所在的代码块内有效
{ let global_d = 'let d' var global_e = 'var e' console.log(global_d) // 'let d'
{
let global_d = 'let d d d'
console.log( '子作用域',global_d) // 子作用域 let d d d 子作用域是独立存在的,声明的变量不会覆盖父作用域的同名变量
}
console.log( '父作用域',global_d)
} // console.log(global_d) // 报错:global_d is not defined 且不继续往下执行 console.log(global_e) // var e
在for循环计数器
for(let i = 0; i < 3; i++){ // 此处设置循环变量的是for循环计数器的父作用域 // 此处是for循环计数器的子作用域 i = 'abc' // 此处覆盖i变量的值,i一旦被改变,循环计数器执行一次后跳出循环 console.log(i) // 这里只执行一次打印 abc } for(let i = 0; i < 3; i++){ let i = 'abc' // 子作用域再次声明同名的i变量 不会覆盖父作用域的i变量 console.log(i) // for循环执行三次 打印三次 abc } // console.log(i) // 在外面读取i变量会报错: i is no defined for(var j = 0; j < 3; j++){ j = 'efg' // 覆盖上面的j变量 console.log(j) // 打印一次 } for(var j = 0; j < 3; j++){ var j = 'efg' // 覆盖上面的j变量 console.log(j) // 打印一次 }
var for_a = [] for(var i = 0; i < 3; i++){ for_a[i] = function(){ console.log(i) } } for_a[2]() // 3
// var 声明的i是全局变量,每次循环i的值都会被更新,for循环结束后 执行for_a[2] 方法打印的i是最新的值
for(let i = 0; i < 3; i++){ for_a[i] = function(){ console.log(i) } } for_a[0]() // 0 for_a[2]() // 2 i是let声明的变量,每次循环相当于重新创建新的i变量,由于JavaScript引擎会记住上一轮循环的值,因此每次循环的值都会被单独记录下来
不会变量提升
// 不会变量提升 console.log(global_f) // undefined // var 变量会变量提升 即脚本运行时global_f 已经存在了,只是还没被赋值,所以打印undefined var global_f = 'var f' console.log(global_g) // 报错:Cannot access 'global_g' before initialization // 不会变量提升,这之前没有声明global_g 也就不存在过,打印就出错 let global_g = 'let g'
暂时性死区
var global_h = 'var h' { // let只要在块级作用域内声明变量,就会与这个作用域绑定 // global_h = 'var h h' // 所以在let声明global_h 变量前 对该变量赋值会报错 // 暂时性死区也意味着 typeof global_h 也会报错而检查不出变量类型 typeof x // undefined x未被声明过 let global_h = 'let h' // Cannot access 'global_g' before initialization }
不允许重复声明
// 在相同作用域下let不允许重复声明变量,否则报错 { var global_i = 'var i' let global_i = 'let i' // 报错:Identifier 'global_i' has already been declared } { let global_j = 'let j' let global_j = 'let j1' // 报错:Identifier 'global_j' has already been declared { let global_j = 'let j2' //不报错,这是一个单独的子作用域 } } { let global_k = 'let k' var global_k = 'var k' // 报错:Identifier 'global_k' has already been declared }
为什么需要块级作用域
es5只存在全局作用域跟函数作用域
一旦某个全局变量被函数重复声明了,那将导致这个全局变量也被覆盖
关于const
//const跟let一样有块级作用域,同样变量不提升,有暂时性死区, 同一作用域下也是不能再次被声明
//const声明一个只读的变量,一旦声明,常量的值就不可被改变 const global_l = 'const l' // global_l = 'const l1' // 报错:Identifier 'global_i' has already been declared // const global_l = 'const l2' // 报错:Identifier 'global_l' has already been declared
// const 一旦声明就必须马上赋值,只声明不赋值就会报错
const global_m; // Uncaught SyntaxError: Missing initializer in const declaration
tips:const实际上保证的不是变量的值不能被改变,对于基础数据类型(字符串,布尔值,数值)来说,值就保存在变量指向的那个内存地址
但对于复合类型数据(对象、数组),变量指向的内存地址保存的并不是值,而是一个指向实际数据的指针,const只能保证这个指针不变
并不能保证实际数据内部的格式是否发生改变
const global_obj = { a: 'const obj' }
global_obj.a = 'const obj1' // 不报错