一、let进阶
(1)闭包问题
var arr = [];
for(let i = 0; i< 10 i++){
arr[i] = function(){ //赋值10个函数体的同时,也生成了10个闭包
console.log(i)
}
}
for(var k = 0; k <10; k++){
arr[k]();
}
//输出0-9
//通过函数形式形成作用域
var arr = [];
var _loop = function _loop(i) { //每次循环i拿的是闭包参数的i,和全局没有关系,全局的i是10
arr[i] = function() { //这里产生了闭包,函数声明的时候捆绑着周围的环境,缓存到函数作用域链
console.log(i);
};
};
for (var i = 0; i < 10; i++) {
_loop[i]();
};
for (var i = 0; i < 10; i++) {
arr[i]();
};
{
let i = 0; // 这里生成了块级作用域,效果和函数作用域是一样的
{
arr[i] = function(){ //这里也产生了闭包,每次循环i拿的是外层作用域的i
console.log(i);
};
};
};
(2)块级作用域下声明函数
{
let a = 0;
function a(){}; //报错,重复声明了
}
{
let a = 1;
{
function a() { } //这里函数声明提升只能提升到当前作用域,不能提升到作用域之外,所以不会报错(函数表达式提升到全局)
}
console.log(a); //1
}
二、const
定义
用来定义常量,即不可变的量,与let一样会产生一个块级作用域
特征
- 一旦定义必须赋值,且值不能被更改
const a;
console.log(a); //报错
const a = 12;
a=10; //报错
特殊情况:
const obj = {}
obj.name = 'zhangsan'; //{name: 'zhangsan'}
//const定义的常量只能保证栈的地址不变,无法保证引用里数据 结构的改变
解决方法:freeze-冻结
const obj = [];
Object.freeze(obj);
obj[2] = 'zhangsan';//无效
//循环冻结
function myFreeze(obj){
Object.freeze(obj);
for(var key in obj){
if(typeof(obj[key]==='object')&& obj[key]!==null){ //排除null的情况
Object.freeze(obj[key]);
}
}
}
// 但这种方式并不好,因为它没有从源头上解决问题
const http = require('http')
// 从源头解决:require返回的是实例化的对象,无论怎么修改http,都不影响构造器,所以不用冻结
- 有块级作用域,不会变量提升,存在暂时性死区
{
const a = 12; //自带块级作用域
}
console.log(a); //报错
{
console.log(a); //报错
const a = 12;
}
- 与let一样不能重复声明
const a =12; //报错
var a =10;
三、全部变量与顶层对象
顶层对象的属性和全局变量是等效的,这样导致了全局变量会挂到window的问题
ES6为了改变了这种现状,为了兼容允许 function,var 声明全局变量,不允许let,const声明全局变量
在不同的环境下面顶层对象是不一致的,例如在浏览器环境中顶层对象就是window,node环境中的顶层对象就是global
ES6提案让所有的环境都有一个顶层对象-global,只提案没有实施