二、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。