ES6学习笔记——let和const
let
和 const
是es6新增的用于声明变量的命令 。let
用于可以代替 var
,用于声明作用域为该代码块的变量。const
用于声明作用域为该代码块的常量。
es5和es6声明变量的几种方式
es5
var
function
es6
var
function
let
const
import
class
let
作用域
ES6新增了 let
命令用于声明变量,只在所在代码块内有效。
实例:
1 | { |
实例:
1 | // 这里的i的作用域仅仅在循环体内 |
实例:
1 | var a = []; |
1 | var a = []; |
for
循环中: ()
内和 {}
内不是同一个作用域, ()
位于 {}
的父作用域,孤儿 {}
内部可以再次声明已经在 ()
中声明过的变量。
1 | for (let i = 0; i < 3; i++) { |
另外:在同一个作用域内不可以使用 let
重复声明同一个变量。
没有变量提升
var
有变量提升。let
没有变量提升。
故而, let
需要先声明后使用。
实例:
1 | // var,这里使用var声明的foo会被提升到顶部,但是对于foo的赋值没有被提升到顶部,故而可以获取到foo但是为undefined |
暂时性死区(TDZ)
只要块级作用域内存在 let
,那么它所声明的变量就绑定了这个作用域且不受外部影响。
实例:
1 | var tmp = 123; |
实例:
1 | if (true) { |
正因为 let
的TDZ特性,使得很多在 let
声明一个变量之前对于这个变量做的操作失效。
故而,在一个代码块内,对于一个变量,一定要先使用 let
声明后使用。
实例:隐蔽的死区
1 | function bar(x = y, y = 2) { |
这里y还没声明就赋值给了x,也就是y还没被声明就被使用了,此时为TDZ故而报错。
实例:
1 | // 不报错 |
软神的这句话可能有误:
ES6 规定暂时性死区和let、const语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。
总而言之:需要先let声明后使用,否则就报错。
不允许重复声明
不允许使用 let
在同一作用域内多次声明一个变量,否则报错。
块作用域
es5作用域
es5的作用域:只有全局作用域和函数内作用域。
实例:函数体内部的声明覆盖了全局声明
1 | var tmp = new Date(); |
这里编写代码的本意是在 console.log
这行使用外部 tmp
,在 if
循环体内部重新声明变量 tmp
,但是结果却是 undefined
。
原因是循环体内部对于 tmp
的声明被提升到了函数的顶部,故而 console.log
打印的是已经被在函数内声明的 tmp
。
实例:计数变量泄露为全局变量
1 | var s = 'hello'; |
这里的 i
最终被计算成了5,并且是一个全局变量,造成了变量泄露。
es6作用域
es6的作用域: let
为js提供了块作用域, let
声明的变量作用域仅在其所处的代码块内,并且外层代码块不受内层代码块的影响。
实例:
1 | function f1() { |
实例:es6允许块作用域任意层嵌套, 内层可以访问外层,外层无法访问内层。
1 | { |
块作用域可以替代IIFE:
1 | // IIFE写法 |
块作用域和函数声明
那么函数可以在块作用域内声明吗?
es5:只能在全局声明或者函数内作用域声明,但是浏览器不会报错。
es6:允许在块作用域内声明函数。
实例:下面的代码在es5中运行会得到 I am inside!
,因为if内部的重新声明会被提升到函数顶部从而覆盖了外部的声明。
而如果在es6中运行理论上会得到 I am outside!
但是实际上在浏览器中运行都会报错,原因是这段代码在es5和在es6中的运行结果截然相反,故而会导致严重的问题,为了保证对es5的兼容会在es6中报错。
1 | function f() { |
故而不要在块作用域内部声明函数或者如果要声明也要携程函数表达式。
1 | // 不建议 |
es6的块作用域必须有大括号 {}
,否则js不认为其是块作用域。
实例:
1 | // 没有写大括号,报错 |
1 | ; |
const
基本用法
const
声明一个只读的常量,其指向一个内存空间,内存空间内容不可改变。
故而 const
声明时就要赋值,因为一旦声明之后就无法改变。
const
的作用域和 let
一样:只在其所声明的块作用域内有效。const
也存在TDZ,也需要先声明并赋值后使用。const
也不可以重复声明。
const的本质
const
实质上是保证 const
变量指向的内存空间的内容不变。
对于基本数据类型而言:指向的内存空间就是保存了基本类型的数据(布尔,字符串,数字),故而一旦使用 const
声明了基本数据类型就无法改变。
对于符合数据类型而言:指向的内存空间保存了这个复杂类型的指针,而指针又指向了另外一个或者多个内存空间,这里的内存空间才是真正保存了复杂类型下的基本类型的值。
实例:
1 | const person = {}; |
实例:
1 | const arr = []; |
顶层对象的属性
浏览器中的顶层对象是 window
对象。
node环境中的顶层对象是 global
对象。
在es5中:顶层对象的属性和全局变量等价。
1 | window.person = 'mason'; |
在es6中: var
和 function
声明的仍然是顶层对象的属性(或者说全局变量),而 let
, const
, class
声明的全局变量不属于顶层对象的属性。
1 | // 这两种写法等价 |
globalThis对象
上面说了js在浏览器环境和在node环境中的顶层对象不同,或者说“在各个js的实现里面,顶层对象不统一。”
为了能让代码能适应多个实现的环境,在es2020里面引入了 globalThis
对象,该对象存在于所有环境,可以通过其拿到顶层对象。
1 | // 全局作用域下,以下均为true |
1 | // 全局作用域下,以下均为true |