基本用法
ES6新增了let命令,用法类似于var,但是所声明的变量只在let所在的代码块有效
{
let a = 1;
var b = 2;
}
console.log(a); //error: a is not defined
console.log(b); //2
以上例子说明let声明的变量只能在所在代码块有效
for循环非常适合使用let, 以下是使用var的一个for循环
for(var i = 0;i < 10;i++){
console.log(i);// 0,1,2,3,4,5,6,7,8,9
}
console.log(i);// 10
上面代码中, 因为变量i是用var声明的,所以全局都有效, 每循环一次变量i都发生了变化, 这时候小伙伴会问为什么外部打印的不是9吗?怎么会是10呢?
因为在循环打印的时候因为是 i++ ; i++是先执行后+1的,当打印9的时候,这时候的下一步就已经+1了,所以外部变量打印的10.
如果使用let, 声明的变量仅在块级作用域内有效,最后将输出 i is not defined
for(let i = 0;i < 10;i++){
console.log(i); // 0,1,2,3,4,5,6,7,8,9
}
console.log(i); //error: i is not defined
另外,for循环有个特别之处,就是设置循环变量的那部分是一个父作用域, 而循环内部是一个单独的子作用域.
for(let i = 0;i < 3;i++){
let i = '肖某';
console.log(i);// 3个肖某
}
这说明函数内部的变量 i 与循环变量 i 不在一个作用域, 而是有各自单独的作用域, 所以在开发中一定要避免在for循环中声明与for循环一样的变量
不存在变量提升
var命令会发生变量提升的现象, 既变量可以在声明之前使用, 值为 undefined. 这种有点奇怪, 按照一般的逻辑
变量应该在声明语句之后才可以使用
console.log(a); // undefined
var a = '肖某';
console.log(a); // 报错
let a = '肖某';
以上代码中, 变量a用var命令声明会发生变量提升,既脚本开始运行时, 变量a就已经存在, 但是没有值, 所以会输出undefined . 变量a用let声明则不会发生变量提升, 表示这个变量是不存在的,如果用到他, 会抛出错误
暂时性死区
只要块级作用域内存在let命令. 它所声明的变量就一定在这个区域, 不再受外部影响
var a = '肖某';
if(true){
a = '123';
let a;
console.log(a);//报错
}
ES6明确规定, 如果区块内存在let和const命令, 则这个区块对这些命令声明的变量从一开始就是封闭作用域, 只要在声明前使用这些变量, 就会报错
不允许重复声明
let不允许在相同的作用域内重复声明同一个变量
function num(){
let a = 10;
var a = 10; //报错
}
function num(){
let a = 10;
let a = 10; //报错
}
不能在函数内容重新声明参数
function num(a){
let a; //报错
}
function num(a){
{
let a; //不报错 在一个独立的块级作用域内 ,不受外部影响
}
}
为什么需要块级作用域
如果没有块级作用域, 会导致很多场景不合理
第一种场景, 内层变量可能会覆盖外层变量
var a = new Date();
function f() {
console.log(tmp);
if(false) {
var a = '肖某';
}
}
f(); // undefined
// 本质
function f() {
var a; // 变量提升 使变量a提升到函数顶部
console.log(a);
if(false) {
var a = '肖某';
}
}
第二种场景, 用来计数的循环变量泄露与全局变量
var s = 'hello';
for (var i = 0; i < s.length;i++){
console.log(s[i]);
}
console.log(i); // 5
变量 i 只用来控制循环, 但是循环结束后, 它并没有消失, 而是泄露成了全局变量
const命令
const 声明一个只读的变量. 一旦声明, 常量的值就不能改变
const a = '肖某';
a = 3; //报错
只声明, 不赋值也会报错
const a; //报错
const 的作用域于let相同: 只在声明所在的块级作用域有效 没有变量提升, 也存在暂时性死区, 只能在声明后使用
if(true) {
const a = 3;
}
console.log(a); //报错
本质
const 实际上保证的并不是变量的值不能改动, 而是变量指向的那个内存地址不得改动. 对于简单数据类型 (数值, 字符串, 布尔值) 而言, 值就保存在变量指向的内存地址中,因此等同于常量. 但对于复合类型的数据 (主要是对象和数组) 而言, 变量指向的内存地址保存的只是一个指针, const只能保证这个指针是固定的,在至于它指向的数据结构是不是可变的,这完全不能控制, 因此将一个对象声明为常量时必须小心
const obj = new Object();
//为obj对象添加一个属性,可以改动成功
obj.name = '肖';
obj.age = 21;
//将obj指向另外一个对象, 就会报错
obj = {}; //报错
如果你想让const 声明的对象不能修改,可以用Object.freeze方法进行冻结
const obj = Object.freeze({});
obj.name = '肖'; //报错
除了将对象本身冻结,还要将对象的属性冻结
const obj = {
name: '肖',
age: 21
};
var constantize = obj => {
Object.freeze(obj);
Object.keys(obj).forEach((key, i) =>{
if(typeof obj[key] === 'object'){
constantize(obj[key]);
}
})
}
constantize(obj);
网友评论