第一章:let 和 const 命令
let命令
含义
用于生命变量,所声明的变量只能在let命令所在的代码块内有效
let的变量提升
let不会发生变量提升现象,所以,变量一定要在声明之后使用,否则报错。
- 什么是变量提升?
- 在JavaScript中,把定义在后面的变量提升到代码的顶部去定义,变量提升只是提升变量的声明,并不会把赋值也提升上来。
//在最新的前端大佬中也有人说let可以引起变量提升,MDN也说明let可以引起变量提升,关于这个问题,也有很多很多的解释,不知道谁对谁错,而且我发现了一个奇妙的问题,就是let x = x;之后,x既不能赋值也不能重新let,好像x变成了一个bug,所以关于let是否能引起变量提升还是持保留意见。
|
|
其实实际的效果
函数也存在着变量提升,但是函数的提升是吧整个函数都提升到代码的顶部,只有函数声明可以提升,函数表达式并不会提升
暂时性死区(TDZ)
如果区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成了封闭的作用域,只要在声明这些变量之前使用变量,就会报错,总的来说,在使用let命令声明变量之前,改代码块的变量都是不可用的
暂时性死区中不存在变量提升,这样就可以避免一些运行错误,防止变量声明前就使用这个变量,从而导致一些意料之外的行为
不允许重复声明
let不允许在相同的作用域内重复声明同一个变量,不能再函数内部重新声明参数
|
|
|
|
块级作用域
没有块级作用域的后果
在ES6之前,之最在全局作用域和函数作用域,这带来了很多不合理的场景。由于ES5不存在块级作用域,所以会有下面的不合理场景。
内层变量可能会覆盖外层变量
1234567var tmp = 'hello';function f(){console.log(tmp);if(false){var tmp = 'world'}}//undefined出现这个原因就是因为变量提升导致的内层的tmp变量覆盖了外层的tmp变量
用来计数的循环变量泄露为全局变量
12345var s = 'hello';for(var i=0;i<s.length;i++){console.log(s[i])}console.log(i)//5i虽然只是用来控制循环,但是循环结束后,i并没有消失,而是泄露为全局变量
#####块级作用域的优点
- 外层代码不收内层代码块的影响,外层作用域无法读取内层作用域的变量
- 内层作用域可以定义外层作用域的同名变量
- 块级作用域的出现使得广泛应用的立即执行匿名函数不在必要了
const命令
含义:
const用来声明常量,一旦声明,其值就不能改变,所以const一旦声明常量,就必须立即初始化,不能留到以后赋值,对于const而言,只声明不赋值就会报错。const foo;//syntaxError:Missing initializer in const declaration
const 命令声明的常量也不存在变量提升,同样存在暂时性死区,只能在声明后使用。
const 命令只是保证变量名指向的地址不变,并不能保证该地址的数据不变,所以讲一个对象声明为常量是十分小心的事情,const定义的对象不可变的只是这个地址,既不能吧foo指向另一个地址。但对象本身是可变的,所以依然可以为其添加新属性
对象冻结
- 使用Object.freeze方法12const foo = object.freeze({})foo.prop = 123 //不起作用
但这个并不是根本性的冻结,如果要冻结属性的话,也应该将对象的属性也冻结
- 对象属性冻结12345678var 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声明的常量只能在当前的代码块有效,如果想要设置跨模块的常量,可以采用下面的写法:
全局对象的属性
#####含义:
全局对象是最顶层的对象,在浏览器环境中指的是window对象,在Node.js中指的是global对象,在ES5中,全局对象和全局变量是等价的。
这样造成的问题就是容易不知不觉就创建了全局变量
#####ES6改进
- var 和 function 命令声明的劝捐变量依旧是全局对象的属性
- let、const、class命令声明变量不属于全局对象的属性