顶层对象
    在js中,顶层对象是window对象,其属性与全局变量挂钩,被认为js中的败笔之一。
    a.但是window又指浏览器窗口对象,是具有实体意义的,这样不合适。
    b.引擎预编译的时候不能对未声明变量进行报错,因为不知道是不是window上的属性。
    c.顶层变量属性任意地方都可以读写,不利于模块化开发。
    (1)let
    let在全局作用域中声明的变量不会放在window(目前js中的顶层对象)上,将全局变量与顶层对象脱钩。

    1. let a = 123;
    2. console.log(window.a) // undefined
    1. 块级作用域与函数声明。es5浏览器中,不允许在会计作用域中进行函数声明,es6中允许,为了兼顾老代码,所以推荐函数表达式方式。块级作用域中, 函数声明会提升到当前块级作用域头部。
    1. let a = 123;
    2. {
    3. function a (){}
    4. }
    5. //不会报错

    (2)const 定义常量
    a.声明时,需要进行赋值,定义后不能重新赋值。只要栈内存中的值不变就行,引用的对象可以修改(指针没变)。
    b.也会产生块级作用域,不会有变量提升,存在暂时死区。

    (3)冻结对象
    Object.freeze,可以冻结对象的属性值。但是对象和数组的值依然可以改变(因为修改引用值,所存的指针没有改变)

    1. function freezingTool(obj){
    2. //先冻结原始值
    3. Object.freeze(obj)
    4. //遍历对象,将对象和数组值冻结
    5. //for in 对象,数组都可以遍历
    6. for(var key in obj){
    7. let val = obj[key],
    8. isReference = typeof( val ) === 'object' && val !== null;
    9. if(isReference){
    10. freezingTool(val)
    11. }
    12. }
    13. }

    (4)块级作用域的作用
    a.避免访问外部变量时,被内部变量覆盖(变量提升)

    1. let a = 123;
    2. (function(){
    3. console.log(a) //本来想要引用外部变量,结果变量提升,打印出undedined
    4. if(1){
    5. var a = 321
    6. }
    7. })()
    1. b.避免循环体产生全局变量,通过var声明的变量会渗透到循环体外部,形成全局变量
    1. for(var a = 0; a < 10; a++){
    2. console.log(a)
    3. }