第一章:let 和 const 命令

第一章:let 和 const 命令

let命令

含义

用于生命变量,所声明的变量只能在let命令所在的代码块内有效

let的变量提升

let不会发生变量提升现象,所以,变量一定要在声明之后使用,否则报错。

  • 什么是变量提升?
  • 在JavaScript中,把定义在后面的变量提升到代码的顶部去定义,变量提升只是提升变量的声明,并不会把赋值也提升上来。
    //在最新的前端大佬中也有人说let可以引起变量提升,MDN也说明let可以引起变量提升,关于这个问题,也有很多很多的解释,不知道谁对谁错,而且我发现了一个奇妙的问题,就是let x = x;之后,x既不能赋值也不能重新let,好像x变成了一个bug,所以关于let是否能引起变量提升还是持保留意见。
1
2
3
4
5
(function(){
var a = 1,
b = 2,
c = 3
})()

其实实际的效果

1
2
3
4
5
6
(function(){
var a,b,c
a = 1;
b = 2;
c = 3;
})()

函数也存在着变量提升,但是函数的提升是吧整个函数都提升到代码的顶部,只有函数声明可以提升,函数表达式并不会提升

暂时性死区(TDZ)

如果区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成了封闭的作用域,只要在声明这些变量之前使用变量,就会报错,总的来说,在使用let命令声明变量之前,改代码块的变量都是不可用的

1
2
3
4
5
var tmp = 123;
if(true){
tmp = 'abc' //出错
let tmp
}

暂时性死区中不存在变量提升,这样就可以避免一些运行错误,防止变量声明前就使用这个变量,从而导致一些意料之外的行为

不允许重复声明

let不允许在相同的作用域内重复声明同一个变量,不能再函数内部重新声明参数

1
2
let a = 2;
let a = 3;//报错
1
2
3
4
5
function hello(arg){
let arg = 0
console.log(arg)
}
hello(9)//SyntaxError: Identifier 'arg' has already been declared

块级作用域

没有块级作用域的后果

在ES6之前,之最在全局作用域和函数作用域,这带来了很多不合理的场景。由于ES5不存在块级作用域,所以会有下面的不合理场景。

  • 内层变量可能会覆盖外层变量

    1
    2
    3
    4
    5
    6
    7
    var tmp = 'hello';
    function f(){
    console.log(tmp);
    if(false){
    var tmp = 'world'
    }
    }//undefined

    出现这个原因就是因为变量提升导致的内层的tmp变量覆盖了外层的tmp变量

  • 用来计数的循环变量泄露为全局变量

    1
    2
    3
    4
    5
    var s = 'hello';
    for(var i=0;i<s.length;i++){
    console.log(s[i])
    }
    console.log(i)//5

    i虽然只是用来控制循环,但是循环结束后,i并没有消失,而是泄露为全局变量

#####块级作用域的优点

  • 外层代码不收内层代码块的影响,外层作用域无法读取内层作用域的变量
  • 内层作用域可以定义外层作用域的同名变量
  • 块级作用域的出现使得广泛应用的立即执行匿名函数不在必要了

const命令

含义:

const用来声明常量,一旦声明,其值就不能改变,所以const一旦声明常量,就必须立即初始化,不能留到以后赋值,对于const而言,只声明不赋值就会报错。
const foo;//syntaxError:Missing initializer in const declaration
const 命令声明的常量也不存在变量提升,同样存在暂时性死区,只能在声明后使用。
const 命令只是保证变量名指向的地址不变,并不能保证该地址的数据不变,所以讲一个对象声明为常量是十分小心的事情,const定义的对象不可变的只是这个地址,既不能吧foo指向另一个地址。但对象本身是可变的,所以依然可以为其添加新属性

对象冻结
  • 使用Object.freeze方法
    1
    2
    const foo = object.freeze({})
    foo.prop = 123 //不起作用

但这个并不是根本性的冻结,如果要冻结属性的话,也应该将对象的属性也冻结

  • 对象属性冻结
    1
    2
    3
    4
    5
    6
    7
    8
    var constantize = (obj) => {
    object.freeze(obj);
    object.key(obj).forEach((key,value)=>{
    if(typeof obj[key] == 'object'){
    constanize(obj[key])
    }
    })
    }
ES6的六种声明变量方式
  • var
  • function
  • let
  • const
  • class
  • import
跨模块常量

由于const声明的常量只能在当前的代码块有效,如果想要设置跨模块的常量,可以采用下面的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//constants.js模块
export const A = 1;
export const B = 2;
export const C = 3;
//test01.js模块
import * as constants from './constants'
console.log(constants.A);//1
console.log(constants.B);//2
//test02.js模块
import {A,B} from './constants'
console.log(A);//1
console.log(B);//2

全局对象的属性

#####含义:
全局对象是最顶层的对象,在浏览器环境中指的是window对象,在Node.js中指的是global对象,在ES5中,全局对象和全局变量是等价的。
这样造成的问题就是容易不知不觉就创建了全局变量

#####ES6改进

  • var 和 function 命令声明的劝捐变量依旧是全局对象的属性
  • let、const、class命令声明变量不属于全局对象的属性