ES6学习之-let、const

182 阅读4分钟

第一部分 let

1、基本用法

  1. ES6 新增了let命令,用来生命变量。let声明的变量只在let命令所在的代码块内生效。常用于for循环,使得计数器只在循环体内生效,在循环体外就会报错。
let a;
let b,c,d
//只能再块级作用域生效
{
  let a = 10;
  var b = 1;
}
a   //ReferenceError: a is not defined  原因:let命令所在的代码块内生效
b   //1

2、不存在变量提升

"变量提升":代码再执行之前回去收集变量,相当于在声明之前可以使用。 var命令存在“变量提升”现象。即变量可以在声明之前使用,值为undefined。 let命令声明的变量一定要在声明后使用,否则报错。

//var的情况
console.log(a); // 输出undefined
var a = 2;

// let 的情况
console.log(b); // 报错ReferenceError
let b = 2;

分析:a是用var命令声明的变量,会发生变量提升。脚本开始运行时,变量a就已经存在,但是还没有赋值,所以是undefined
     b是let命令声明的变量,不会发生变量提升。

3、暂时性死区

console.log(a)  //Cannot access 'a' before initialization
let a=1
console.log(a) 

ES6明确规定了,如果区块中存在let、const命令,这个区块会对这些命令声明的变量,从一开始就形成封闭作用域。但是在声明这个变量之前使用这个变量都会报错。在语法上成为“暂时性死区“,Temporal Dead Zone。简称TDZ。

function bar(x = y, y = 2) {
  return [x, y];
}
bar(); // 报错
分析:这个“TDZ”就不是很明显。参数x默认等于参数y,但是此时参数y还没有声明,属于“TDZ”
function bar(x=2,y=x){
    return[x,y];
}
bar();//2,2  y=x不会报错是因为,因为x在使用的时候已经声明了

3.1 暂时性死区中的typeof行为慎用

typeof x; // ReferenceError ,在TDZ中使用这个变量都会报错
let x;
作为比较:一个根本就没有声明的变量使用typeof反而不会报错。

3.2 不允许重复声明

function func() {
  var a = 10;
  var a = 1;
} //不报错

function func() {
  let a = 10;
  var a = 1;
}//Identifier 'a' has already been declared

function func() {
  var a = 10;
  let a = 1;
}//Identifier 'a' has already been declared

function func(arg) {
  let arg;
}//Identifier 'arg' has already been declared,不能重复声明参数

function func(arg) {
  {
    let arg;  
  }
}//不报错,两个atg所在作用域不同

4、块级作用域

** ES5只有全局作用域和函数作用域,没有块级作用域,会出现一些不合理场景**

  1. 内层变量覆盖外层
  2. 用来计数的循环变量泄露为全局变量
//内层变量覆盖
var tmp = new Date();
console.log("第一处的tmp"+tmp)  //Wed Aug 05 2020 19:34:28 GMT+0800 (中国标准时间)
function f() {
  console.log("第二处的tmp"+tmp)  //undefined
  if (false) {
    var tmp = 'hello world';  
  }
}
f()//调用函数的时候,进入函数内部作用域,还没有执行tmp输出的时候,函数内部定义的tmp变量提升会覆盖掉函数外面的tmp,所以tmp
值为初始化时的值。


用来计数的循环变量泄露为全局变量
var s = 'hello';
for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}
console.log(i); // 5,i泄露到全局作用域,循环消失了,i缺没有消失。

** ES6为javascript新增了块级作用域 **

function f(){
let i=8 //外层代码块不受内层影响
for(let i=0;i<5;i++){
  console.log(i)
}
console.log(i) //8
}

function f(){
var i=8  //会被内层代码块覆盖掉
for(var i=0;i<5;i++){
  console.log(i)
}
console.log(i) //5
}

有了块级作用域,匿名立即函数表达式就不再必要了!! 块级作用域与函数声明

  1. ES5规定,函数只能在顶层作用域和函数作用域中声明,不能在块级作用域声明
  2. ES6 引入了块级作用域,明确允许在块级作用域之中声明函数
  3. 在ES6环境中有三条规则有效
  • 允许在块级作用域内声明函数。
  • 函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
  • 同时,函数声明还会提升到所在的块级作用域的头部。

除了ES6环境,其他环境,块级作用域内声明的函数当做let处理

**总结: ** 考虑到环境导致的差异大,我们尽量不要在块级作用域内声明函数,如果确实需要,可以将函数声明写成函数表达式。

5、不影响作用域链

{
  let name="vina"
  function fn(){
     console.log(name) //可以获取到name,虽然是块级作用域但是不影响作用域链
  }
  fn()
}

6、案例

var定义for循环里面的i,循环完,i自增已经变成了3,这时候再去点击,items[3]获取不到,所以报错。for循环默认用let
   <!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Document</title>
   <style>
       .item {
           width: 100px;
           height: 60px;
           border: 1px solid olivedrab;
           display: inline-block;
           background: white;
       }
   </style>
</head>
<body>
   <div class="container">
       <h2 class="page-header">点击切换颜色</h2>
       <div class="item"></div>
       <div class="item"></div>
       <div class="item"></div>
   </div>
   <script>
       //获取div元素对象
       let items = document.getElementsByClassName('item')

       //遍历并绑定事件
       for (var i = 0; i < items.length; i++) {
           items[i].onclick = function () {
               // this.style.background = 'pink'
               items[i].style.background = 'pink'  //Cannot read property 'style' of undefined
           }
       }
       for (let i = 0; i < items.length; i++) {
           items[i].onclick = function () {
               // this.style.background = 'pink'
               items[i].style.background = 'pink'  //用let去定义变量i
           }
       }
   </script>
</body>

</html>

第二部分 const

1、基本用法

  1. const声明一个只读的常量。一旦声明,常量的值就不能改变。最典型的就是PI
  2. const声明的变量不可以改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到后面赋值
  3. const只在声明所在的块级作用域内生效
  4. const声明的常量也存在"TDZ"
  5. const不仅不能重复的赋值,更不能重复的声明!!!
  6. const本质不是声明变量的值不能改动,而是变量指向的那个内存地址所保存的数据不得改变
  • 数值、字符串、布尔值,值保存的就是变量指向的地址,等同于常量
  • 对象、数组这种复合型,变量指向内存地址,保存的只是一个指向实际数据的指针,const只能保证指针固定,总是指向固定的地址,地址所指向的数据结构的内容是可变的,所以对象声明为常量需要小心!!!!
PI=2  //VM3975:1 Uncaught TypeError: Assignment to constant variable.
const 2 //Missing initializer in const declaration

第三部分:其他

1、ES6声明变量六种方法

ES5的两种varfunction
ES6新增四种:letconstimportclass

2、顶层变量

  顶层对象再浏览器环境指的是window,在Node环境指的是global对象。
  ES5中,顶层对象的属性等价于全局变量
  ES6改变了,varfunction命令声明的全局变量仍然为全局变量。但是letconst命令、class声明的全局变量不属于顶层变量了
  
 let a=1
 window.a   //undefined
 var b=1
 window.b //1