好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

ES6+ let

ES6+ let

1. 前言

本节我们一起学习下 ES6 中的 let ,在 ES5 中变量的 方法 只有 一个 var ,但是使用 var 来定义的变量存在很多缺陷和弊端,ES6 引入了 let 语句来声明变量,同时引入了很多概念,比如块级作用域、暂存死区等等。限制了任意声明变量,提升了程序的健壮性。

2. 基本 用法

let 的使用 方法 类似于 var ,并可以代替 var 来声明变量。

  { 
   let  name  =   'imooc'  ; 
 } 
 @H_ 502 _50@

let 允许你声明 一个 作用域被限制在块级中的变量、语句或者表达式。与 var 关键字不同的是, var 声明的变量只能在全局或者整个 函数 块中。 var 和 let 的不同之处在于 let 是在编译时才初始化,也就是在同 一个 块级下不能重复地声明 一个 变量,否则会报错。

let 不会在全局声明时创建 window 对象的 属性 ,但是 var 会。

  { 
   var  name  =   'imooc'    // imooc 
   var  name  =   'iimooc'   // iimooc  
 } 
console .  log  ( window . name ) 	 // iimooc 
 { 
   let  age  =           // 10 
   let  age  =           // Uncaught  Syntax Error: Identifier 'age' has already been declared  
 } 
console .  log  ( window . age ) 		 // undefined 
 @H_ 502 _50@

上面的 代码 中,在 一个 块中分别使用 var 和 let 来声明变量对比他们之 间的 差异,从上面的 代码 操作可以看出,我们可以使用 var 多次对 name 声明,但是使用 let 声明的 age,后面再使用 let 对其声明是会报错的。
var 是没有块的概念的,声明的变量会是 window 对象上的 属性 ,在最外层的 window 上可以取到。而 let 存在块的概念,不会 添加 到 window 对象上,这些是 let 和 var 之 间的 区别。从这里我们可以了解到为什么使用 let 。

3. 块级作用域

在深入了解 let 前,我们需要了解一下,在 JavaScript 中有哪些作用域:

全局作用域 函数 作用域 / 局部作用域 块级作用域

上面是 JavaScript 中的三种作用域,那什么是作用域呢?首先要明白的是:几乎所有的编程语言都存在在变量中储值的能力,存储完就需要使用这些值。所以,作用域就是一套规则,按照这套规则可以方便地去存储和访问变量。

在 ES5 中的作用域有全局作用域和 函数 作用域,而块级作用域是 ES6 的概念。

3.1 全局作用域

全局作用域顾名思义,就是在任何地方都能访问到它,在浏览器中能通过 window 对象拿到的变量就是全局作用域下声明的变量。

  var  name  =   'imooc'  ; 
console .  log  ( window . name )     // imooc 
 @H_ 502 _50@

使用 var 定义的变量,可以在 window 对象上拿到此变量。这里的 name 就是全局作用域下的变量。

3.2 函数 作用域

函数 作用域就是在 函数 内部定义的变量,也就是局部作用域,在 函数 的外部是不能使用这个变量的,也就是对外是封闭的,从外层是无法直接访问 函数 内部的作用域的。

  function   bar  (  )   { 
   var  name  =   'imooc'  ; 
 } 
console .  log  ( name )  ;    // undefined 
 @H_ 502 _50@

在 函数 内部定义的 name 变量,在 函数 外部是访问不了的。要想在 函数 外部访问 函数 内部的变量可以通过 return 的方式返回出来。

  function   bar  ( value )   { 
   var  name  =   ' imooc'  ; 
   return  value  +  name ; 
 } 
console .  log  (  bar  (  'hello'  )  )  ;    // hello imooc 
 @H_ 502 _50@

借助 return 执行 函数 bar 可以取到 函数 内部的变量 name 的值进行使用。

3.3 块级作用域

块级作用域是 ES6 的概念,它的产生是要有一定的条件的,在大括号( {} )中,使用 let 或 const 声明的变量,才会产生块级作用域。
这里需要注意的是,块级作用域的产生是 let 或 const 带来的,而不是大括号,大括号的作用是限制 let 或 const 的作用域范围。当不在大括号中声明时, let 或 const 的作用域范围是全局。

  let  name  =   ; 
console .  log  ( window . name )     // undefined 
 @H_ 502 _50@

上面的 代码 可以看到,使用 let 方式声明的变量在 window 下是取不到的。

  var  num  =   ; 
 { 
   var  num  =   ; 
  console .  log  ( num )    // 20 
 } 
console .  log  ( num )      // 20 
 @H_ 502 _50@

在使用 var 声明的情况下,可以看出,外层的 num 会被 {} 中的 num 覆盖,所以没有块级作用域的概念,下面看下使用 let 方式声明:

  let  num  =   ; 
 { 
  console .  log  ( num )  ;   // Uncaught ReferenceError: Cannot access 'num' before initialization 
   let  num  =   ; 
  console .  log  ( num )    // 20 
 } 
console .  log  ( num )      // 10 
 @H_ 502 _50@

这里可以看出 {} 内外是互不干涉和影响的,如果在声明 num 的前面进行打印的话,还会报错,这个时候,num 处于暂存死区,是不能被使用的,下面我们会具体说明。

在低版本浏览器中 不支持 ES6 语法,通常需要把 ES6 语法转换成 ES5,使用 babel 把上面的 代码转换 后得到如下结果:

  var  num  =   ; 
 { 
  console .  log  ( _num )  ;   // num is not defined 
   var  _num  =   ; 
  console .  log  ( _num )  ;   // 20 
 } 
console .  log  ( num )  ;      // 10 
 @H_ 502 _50@

从上面的 代码 中可以看出,虽然在 ES6 语法使用的是相同的变量名字,但是底层 JS 进行编译时会认为他们是不同的变量。也就是说即使大括号中声明的变量和外面的变量是相同的名字,但是在编译过程它们是没有关系的。

块级作用域可以任意嵌套,如下实例:

  {  { 
   let  x  =   'Hello imooc' 
   { 
    console .  log  ( x )  ;   // Hello imooc 
     let  y  =   'Hello World' 
   } 
  console .  log  ( y )  ;     // 引用 错误  ReferenceError: y is not defined. 
 }  }  ; 
 @H_ 502 _50@

上方 代码 每一层都是 一个 单独的作用域,内层作用域可以读取外层的变量,所以第 一个 会打印 Hello imooc , 而外层无法读取内层的变量,所以会报错。

4. 不能变量提升

对应 var 我们知道可以变量提升的,提升到作用域的最顶部,作用域是全局,使得声明变量前也可以使用,但值为 undefined 。

  { 
  console .  log  ( bar )  ;   //  输出 undefined,没有值但不会报错 
   var  bar  =   ; 
 } 
 @H_ 502 _50@

一般变量都应该先声明再使用,所以 let 和 const 语法规定必须声明后使用,否则报错。

  { 
  console .  log  ( name )  ;   // 引用 错误  ReferenceError: name is not defined. 
   let  name  =   'imooc'  ; 
 } 
 @H_ 502 _50@

上面 代码 中,都是在声明前的时候使用变量的,这时候由于 let 不能进行变量提升所以会报引用 错误 。

5. 暂时性死区

上面讲到在变量声明前使用这个变量,就会报错。在 代码 块内,使用 let 命令声明变量之前,该变量都是不可用的。这在语法上,称为 “暂时性死区”(temporal dead zone,简称 TDZ)。

  { 
  console .  log  ( name )  ;    // ReferenceError: name is not defined.  
   let  name  =   'imooc'  ; 
  console .  log  ( name )  ;    // imooc 
 } 
 @H_ 502 _50@

上面 代码 中,在块中使用了 let 声明了 name 变量,在使用前对 name 进行了声明,name 则会处于暂存死区,不能被使用,如果引用则会引用 错误 。

Tips :注意对于 typeof 也是会报错的。

  { 
  console .  log  (  typeof  name )  ;    // Uncaught ReferenceError: Cannot access 'name' before initialization 
   let  name  =   'imooc'  ; 
 } 
 @H_ 502 _50@

上面的 代码 中,name 引用 错误 :无法在初始化之前访问 name,因为 name 在这 个块的下面进行了声明,name 就是 一个 死区,不能被引用了。因此, typeof 运行时就会抛出 一个 ReferenceError 的参数 错误 。

6. 重复声明报错

let 不允许在同 一个 函数 或块作用域中重复声明同 一个 变量,否则会引起语法 错误 ( Syntax Error)。

  { 
   let  x  =   ; 
   let  x  =   ; 
 } 
 // Uncaught  Syntax Error: Identifier 'x' has already been declared 
 @H_ 502 _50@

在上面的 代码 中报错,所以,同 一个 变量名不可以在同 一个 作用域内重复声明。

  { 
   let  x  =   ; 
   var  x  =   ; 
 } 
 @H_ 502 _50@

即使使用 var 去声明也是不可以的,我们知道当使用 let 声明的时候 x 已经是 一个 死区了,不可以被重复声明了。

Tips :注意在 switch 语句中只有 一个 块级作用域,所以下面这种情况也是会报错的。

  let  x  =   ; 
 switch  ( x )   { 
   case   : 
     let  num ; 
     break  ; 
    
   case   : 
     let  num ;  //重复声明了 
     break  ; 
 } 
 // 报错 
 @H_ 502 _50@

如果把 case 后面的语句放到块作用域中则不会报错。

  let  x  =   ; 
 switch  ( x )   { 
   case   :   {  //块 
     let  num ; 
     break  ; 
   }  
   case   :   {  //块 
     let  num ;  //这里就没有关系了,可以正常声明 
     break  ; 
   } 
 } 
 @H_ 502 _50@

上方 代码 , case 后面的语句 let 变量声明在放到块中,是单独的作用域,所以就不会报错。

7. 小结

本节讲解了 let 语句的使用,还有作用域的概念,需要注意以下几点:

let 只作用于块级作用域内; let 声明的变量不能进行变量提升,存在暂存死区; let 声明的变量不允许重复声明,无论重复用 var 或者其他声明都不行; 尽量使用 let 去代替 var 来声明变量。

查看更多关于ES6+ let的详细内容...

  阅读:46次

上一篇

下一篇

第1节:ES6+ 简介    第2节:ES6 环境配置    第3节:ES6+ let    第4节:ES6+ const    第5节:ES6+ 展开语法    第6节:ES6+ 剩余参数    第7节:ES6+ 解构赋值    第8节:ES6+ 模版字符串    第9节:ES6+ 箭头函数    第10节:ES6+ 数值扩展    第11节:ES6+ isFinite()&isNaN()    第12节:ES6+ Number 对象的方法    第13节:ES6+ Math 对象的扩展    第14节:ES6+ includes()    第15节:ES6+ 字符串的扩展    第16节:ES6+ startsWith()    第17节:ES6+ endsWith()    第18节:ES6+ repeat()    第19节:ES6+ padStart()    第20节:ES6+ padEnd()    第21节:ES6+ trim()    第22节:ES6+ Array.from()    第23节:ES6+ of()    第24节:ES6+ find()和findIndex()    第25节:ES6+ copyWithin()    第26节:ES6+ fill()    第27节:ES6+ isArray()    第28节:ES6+ 对象的扩展    第29节:ES6+ flat()    第30节:ES6+ 可选链操作符    第31节:ES6+ Object.is()    第32节:ES6+ Object.assign()    第33节:ES6+ Object.keys()    第34节:ES6+ Object.values()    第35节:ES6+ Object.entries()    第36节:ES6+ 数据结构扩展    第37节:ES6+ Set    第38节:ES6+ WeakSet    第39节:ES6+ Map    第40节:ES6+ WeakMap    第41节:ES6+ Symbol    第42节:ES6+ for...of    第43节:ES6+ 迭代协议    第44节:ES6+ 实现一个简版的 Promise    第45节:ES6+ Promise 基础    第46节:ES6+ Promise 进阶    第47节:ES6+ Generator 基础    第48节:ES6+ Generator 函数应用    第49节:ES6+ async/await    第50节:ES6+ Class 前置知识    第51节:ES6+ Class    第52节:ES6+ Proxy    第53节:ES6+ Reflect(一)    第54节:ES6+ Reflect(二)    第55节:ES6+ 模块化(一)    第56节:ES6+ 模块化(二)    第57节:ES6实战1-实现Vue3 reactive 源码    第58节:ES6实战2-实现 Vue3 effect 源码    第59节:ES6 实战2-封装请求    第60节:ES6+ 实战3-代码整洁之道    第61节:ES6 Map原理分析    第62节:ES6module语法加载importexport    第63节:ES6的循环与可迭代对象示例详解