深入理解let和const原理

码海逐星说
• 阅读 3904

letconst命令是ES6新增的,让我们来看看它们是怎么回事吧~

在有 letconst之前,我们使用 var 来声明变量,var声明变量会有变量提升的特性,

    console.log(a)
    var a = 1 //undefined

var声明a,a变量会被提升到在当前作用域的最前面声明,其实执行的代码是这样的:

    var a;
    console.log(a)
    a = 1;

为了约束变量提升,出现了块级作用域,块级作用域搭配letconst就没有变量提升了。

 console.log(b)
 let b = 1 //ReferenceError: b is not defined

let就没有变量提升的问题,但是在声明变量之前就访问,会报错,访问b会报ReferenceError错误
let为什么没有变量提升的问题呢?块级作用域又是什么呢?

块级作用域

letconst实际上为ES6引入了"块级作用域"的概念。
在ES5中只有全局作用域和函数作用域。ES6新增了块级作用域来约束变量的生命周期。
简单说就是两个大括号{}就形成了块级作用域,外层代码块不受内层代码块的影响。

function f1(){
    let n = 5;
    if(true){
        let n = 10
    }
    console.log(n) //5
}



for(let i =0 ;i< 10 ;i++){

}
console.log(i)  //ReferenceError: i is not defined

内层的n不会影响外层的n,所以打印n=5
for循环中声明了变量i,在for循环之外访问i变量,会报错,这是因为在外层代码块中没有声明变量i

for循环中都会有块级作用域。我们来看一个经典的例子

var arr = []
for (var i = 0; i < 10; i++) {
    arr[i] = function () {
        console.log(i)
    }
}

arr[2]() //10
console.log(i) //10

在for循环外可以访问到ii的值为10,这是因为ifor循环中使用var声明了,会被变量提升,结果就是i挂载到全局对象中,
在浏览器中就是window对象,成为window的属性。每一层循环改变的都是全局的i, 传入到循环中的console.log(i)都是10,
最后外部访问的i值也变成了10

var brr = []
for (let i = 0; i < 10; i++) {
    brr[i] = function () {
        console.log(i)
    }
}

brr[2]() //2
console.log(i) //ReferenceError: i is not defined

for循环中使用let声明变量i,这样每一层循环都重新声明了i,并绑定了当前循环的代码块,所以传入内存函数的额console.log(i)
也是当时的i的值,所以brr[2]()执行时,变量i的值是2,在for循环外访问i会出错。

你可能会问,每一层的循环变量都是重新声明的,那么for循环怎么知道上一层循环的值,从而计算出本层的值?这是因为
Javascript引擎内部会记住上一层的循环的值,在上一层循环的基础上,计算得出初始化本层的变量的值。

细说for循环中的块级作用域
for循环中()是一个块级作用域,而循环体内{}是一个单独的子作用域。

for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc

上面输出三次 abc ,说明,循环内部的i变量与循环变量i不在同一个作用域,有各自单独的作用域。

我们来看看let到底是怎么由var转过来的,我用Babel试试:

var brr = []
for (let i = 0; i < 10; i++) {
    brr[i] = function () {
        console.log(i)
    }
}

brr[2]()
console.log(i)

Babel转过之后,

"use strict";

var brr = [];

var _loop = function _loop(_i) {
    brr[_i] = function () {
        console.log(_i);
    };
};

for (var _i = 0; _i < 10; _i++) {
    _loop(_i);
}

brr[2]();
console.log(i);

babel把for循环内层都抽象到一个函数中了,然后,把循环变量传入这个函数参数,这样每一层的循环都会返回一个函数声明,
而循环变量都会锁定住。

暂时性死区

只要块级作用域中有letconst命令,那么所声明的变量就"绑定"这个区域,不再受外部的影响。
在块级作用域内,使用letconst声明变量之前,该变量都是不可取的,这就叫"暂时性死区"

暂时性死区解决的问题:
解决了在声明变量之前访问该变量,没有变量提升,主要为了减少运行时错误。

暂时性死区的本质:

只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,不可访问,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

const

基本用法

const声明一个只读的常量。一旦声明,常量的值就不能改变。

const 也不会变量提升

const 也存在暂时性死区特性

  • const在声明变量时就要赋值,否则以后不能赋值了
  • const只声明不赋值会报错

本质

const 声明的变量如果是基本类型,那么不允许改变,如果是引用类型,那么只要不改变引用的地址就是可以的。

    const name = 'Friday'
    name = 'Sunday'  //直接报错

    const people = {
        name: 'Jack',
        age: 21
    }
    people.age = 22
    console.log(people) //{ name: 'Jack', age: 22 }
点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
虾米大王 虾米大王
3年前
java代码099
code099.jspInserttitlehere$pageScope.user.name
虾米大王 虾米大王
3年前
java代码073
code073.javapackagepack02;importjava.io.IOException;importjava.io.PrintWriter;importjava.util.UUID;importjavax.servlet.ServletException;importjavax.servlet.annotation.Multip
Wesley13 Wesley13
3年前
VSCode配置FiraCode和更纱黑体字体
!(https://oscimg.oschina.net/oscnet/c7bb62d935ceb01d3b7fe176322e84ae00d.png)Fira Code下载到FiraCode字体的GitHub(https://www.oschina.net/action/GoToLink?urlhttps%
Stella981 Stella981
3年前
Linux自动检测网站心跳通知shell脚本
!/bin/bashLIST("http://xxxx.com")NAME("评价系统getwindowList接口")for((i0;i<${LIST@};i))doHTTP_CODEcurlo/dev/nullsw"%{http_code}""${LIST
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
码海逐星说
码海逐星说
Lv1
其实人世间的挚爱,只有两件事情就可以表达。
文章
4
粉丝
0
获赞
0