二、let进阶、const、全部变量与顶层对象
一、let进阶
let创建了块级作用域,每次循环时内部的块级作用域都会去访问外层块级作用域中的变量i,而外层块级作用域中的变量i都不同,所以打印0-9;类似于闭包:内部函数返回到外部,保持其作用域链不释放。
var arr = [];
for(let i = 0; i < 10; i++){
    arr[i] = function(){
        console.log(i);
    }
}
for(var j = 0; j < 10; j++){
    arr[j]();
}
 
函数声明提升只在当前块级作用域内,不像var声明提升是作用域中逐级提升的。
但是不推荐在块级作用域中声明函数——>使用函数表达式。
{
    let a = 1;
    {
        // 函数声明提升只会在当前块级作用域内
        a(); // 10
        function a(){
            console.log(10);
        }
    }
    console.log(a); // 1
}
 
二、const
结论先行:const同样可以创建(产生)块级作用域。
2.1 ※const声明时必须赋值,且值不可改变(常量)
const声明了一个引用类型的值,只能保证栈中的引用值的地址不改变,而堆中的引用值本身是可以被修改的。
------>解决方案:Object.freeze()方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性。
const obj = {
    name: 'white'
}
// 对象冻结后,无法修改/增加对象的属性值
Object.freeze(obj);
obj.name = 'he';
obj.age = 22;
console.log(obj); // {name: 'white'}
 
------>但是如果引用值的属性仍然为引用值,仍可以改变引用值内部引用值的值
// 若对象的属性值为一个引用类型的值呢?
const obj1 = {
    friends: {
        name: 'white',
        fav: 'basketball'
    }
}
Object.freeze(obj1);
obj1.friends.name = 'jake';
console.log(obj1);
 

------->解决方案:循环冻结
const obj1 = {
    friends: {
        name: 'white',
        fav: 'basketball'
    }
}
myFreeze(obj1);
obj1.friends.name = 'jake';
console.log(obj1);
function myFreeze(obj){
    Object.freeze(obj);
    // 遍历对象属性
    for(var key in obj){
        if(typeof(obj[key]) === 'object' && obj[key] != null){
            myFreeze(obj[key]);
        }
    }
}
 

但很少用,源头上解决这个问题:
const http = require('http');
 
定义模块时,最后一步return实例化对象,不会(不允许)对父级构造器进行修改。
2.2 const不会声明提升,会产生一个暂时性死区
2.3 const只能在当前作用域下生效
2.4 const在同一作用域下不可重复声明
三、全局变量与顶层对象
浏览器环境中顶层对象是window。
var function定义的全局变量的值与顶层对象window的同名属性值相同。
var a = 2;
//当我修改变量时,名字写错了,但 window 的属性值仍能访问到,不合理
b = 1;
console.log(window.b); // 1
 
而let、const定义的全局变量与在顶层对象window上找不动同名属性。
注:不同环境中顶层对象不同;
浏览器:window;
node:global。



















