本文具体内容:
- let的相关用法以及所需要注意的问题
- const的相关用法
- 声明变量的6中方式
- 加深对块级作用域的理解
- 顶层对象(如何在不同环境中获取顶层对象)
let 和 const 命令
let 的使用
- let 用来声明变量,只在let命令所在的代码块中有效。【代码块:使用花括号包住的内容称为代码块{}】
利用let只在当前代码块中有效的特性 解决 for 循环中的问题:
总结:
- var声明的变量在全局范围内都有效,全局变量只有一个i,所以每一次的循环,变量i的值度会发生改变。在数组a的函数内部的变量i指向的就是全局的i。所以无论执行数组a的哪一个成员最终所打印出的i均为10
- let 声明的变量在块级作用域内有效,即在循环中,当前的i只在本轮循环有效。所以每一轮循环都是全新的变量i。
问题:
每一轮的i重新声明,那么怎么知道上一轮循环的值,从而计算出本轮循环的值?
解答:JavaScript引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮的循环基础上进行计算
注意:
for循环有一个特别之处:设置循环变量的部分是一个父作用域,而循环体内部是一个单独的子作用域
- let不存在变量提升
变量提升: 即变量在声明之前可以使用,值为undefined
若使用let来声明,在声明之前使用变量将会报错
- let =>暂时性死区
即只要在这个{块级作用域}中使用let来定义变量,则所声明的变量将“绑定”这个区域,不会受外部(全局变量)的影响。
【全局变量的名称与块级作用域的名称一致时,则全局变量将不会在块级作用域中起作用。】
let变量声明之前的部分均属于let变量的死区,只要在之前使用了let变量均会报错
隐蔽的死区
- 下图代码中,调用bar函数之所以报错(某些实现可能不报错),是因为参数x默认值等于另一个参数y,而此时y还没有声明,属于”死区“。如果y的默认值是x,就不会报错,因为此时x已经声明了。
- let声明x变量时,还没有声明完成之前,使用变量将会报错
- 不允许重复声明
不允许在相同作用域内重复声明同一个变量【不能在函数内部重新声明参数】
const的使用
1.基本规则
- 声明只读常量
- 声明后不可修改
- 一旦声明,必须马上初始化
- 若只声明不初始化将报错
- 只在声明所在的块级作用域内有效
- 声明的常量不提升,存在暂时性死区,只能在声明的位置后面使用
- const声明的常量,与let一样不可重复声明(不管使用var、let、还是const声明均不可)
2.本质
- 并不是值不可变动,而是变量指向的内存地址所保存的数据不得改动
- 对于简单类型的数据(数值、字符串、布尔值),值保存在变量所指向的内存地址,因此等同于变量。
- 对于复合类型的数据(对象与数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证指针是固定的,即总指向另一个固定的地址,并不能控制其地址中所存的数据。所以将对象声明为常量时需要小心。
- 将对象冻结,使得添加新属性将不起作用,在严格模式下将会报错
- 将对象属性也冻结,即将对象彻底冻结
声明变量的六种方式
es5:
- var
- function
es6:
- let
- const
- import
- class
为什么出现块级作用域
- 内层变量可能覆盖外层变量
调用f()函数之后打印出的结果为undefined。这是由于var声明的变量存在变量提升,导致内层的tmp覆盖了外层的tmp变量。打印结果时内层变量并没有赋值
- 用来计数的循环变量泄露为全局变量
变量i只用来控制循环,但是循环结束之后,i变量并没有消失,而是泄露成了全局变量
ES6的块级作用域
- 两个代码块中的同名变量不相互影响
- 外层作用域无法读取内层作用域的变量
- 内层作用域可以定义外层作用域的同名变量,但二者不相互影响
块级作用域与函数声明
- 在ES6中,允许在块级作用域之中声明函数
- 块级作用域中,函数声明语句在块级作用域之外不可引用
- 在浏览器ES6的环境中,块级作用域内声明的函数,行为类似于var声明的变量,会提升到所在的块级作用域的头部。但是在其他环境中还是将块级作用域的函数声明当做let来处理
- ES6允许在块级作用域中声明函数,条件是使用{}
代码示例:
上图代码在浏览器ES6环境中,会报错。其实际运行的是下面的代码:
注意:避免在块级作用域内声明函数,若需要,可以写成函数表达式的形式
顶层对象模型
- 顶层对象概念:
浏览器中指window对象
node环境中指global对象
在ES5,顶层对象的属性与全局变量是等价的
*浏览器中,顶层对象是window,但Node和Web Worker是没有window的
*浏览器和Web Worker里面,self也指向顶层对象,但是Node没有self
*Node里面,顶层对象是global,但其他环境都不支持
同一段代码为了可以在各种环境都能取到顶层对象,目前一般使用this变量,但是具有局限性
*全局环境中,this会返回顶层对象,但是在Node模块和ES6模块中,this返回的是当前模块
*函数里面的this,如果函数不是作为对象的方法运行,而是单纯作为函数运行,this会指向顶层对象,但是严格模式下,this此时返回的是undefined
*无论是严格模式还是普通模式,new Function(‘return this’)(),总是会返回全局对象。但是如果浏览器使用CSP(Content Security Policy,内容安全策略),那么eval,new Function这些方法都可能无法使用
勉强可以使用下图中的方式,在所有情况下,都取到顶层对象:
11.
全局变量与顶层对象等价引发的问题
* 没有办法在编译时报出未声明的错误,只有运行时才知道是否未声明。
(全局变量可能是由顶层对象的属性创造的,而属性的创造是动态的)
* 在不知觉中我们可能就创建了全局变量
* 顶层对象的属性可以到处读写,不利于模块化编程
window对象有实体含义,指的是浏览器的窗口对象,顶层对象是一个有实体含义的对象,是不合适的
12.
ES6针对这一现象所做的改变
*保持兼容性:var和function命令声明的全局变量依旧是顶层对象的属性
*let、const、class所声明的全局变量,不属于顶层对象的属性。也就是说,ES6开始,全局变量将逐步与顶层对象的属性脱钩
例如:
global对象
- 目前有一个提案,在语言标准的层面,引入global作为顶层对象。也就是说,在所有环境下,global都是存在的,都可以从它拿到顶层对象。
- 垫片库system.global模拟了这个提案,可以在所有环境拿到global