let真的没有变量提升吗?let暂时性死区引发的思考

let真的没有变量提升吗?let暂时性死区引发的思考
1、var声明存在变量提升
(1)var 具有变量提升的特性,来看栗子1
    function fu() {
        console.log(a);
        var a = 666;
    }
    fu();

在这里插入图片描述

解析:为什么这段代码不是"Uncaught ReferenceError: a is not defined",而是undefined呢?

js的编译过程有关,js代码有编译阶段,js不是早早把编译工作做完,而是一边编译一边执行。js代码执行前都会先编译,只是这个编译的过程非常短暂,js引擎搜集所有的变量声明,提前让这些声明生效。剩下的语句呢等待执行的时候才会生效。var声明的变量存在提升,就是变量的声明会被移动到函数或者全局代码的开头。

可以这么理解,声明了但是没有赋值,所以是undefined

var a;
console.log(a);
a = 666;
(2)var声明换成let声明,来看栗子2

学习ES6的时候,问到var let const的区别的时候,总是会答出这么一句标准答案:var存在变量提升,let const不存在变量提升。

刚开始我也是照着标准答案学的,直到最近,看来一些英文文档的介绍,我对这个标准答案存怀疑的态度。let声明真的不存在变量提升吗?一如既往,写栗子分析输出结果的为什么?

栗子2:栗子1var换成let声明,看看什么样的结果

    function fu() {
        console.log(a);
        let a = 666;
    }
    fu();

在这里插入图片描述

解析:意料之中会报错"在初始化之前无法访问"!这是为什么呢?这就涉及到let声明暂时性死区问题。

ES6明确规定,如果块作用域存在let或者const声明的变量,这个块对这些变量会形成封闭作用域,let和const不会变量提升,在let和const之前不可以访问到,这个区域就叫做暂时性死区(TDZ temporal dead zone)

意思就是块级作用域存在let声明的变量,这个变量会binding绑定这个作用域,不再受外部影响,无法在块级作用域内let之前给变量赋值。进入块级作用域的时候这个变量就存在但我们不能获取,要获取它必须等到代码执行到声明那一行代码。

(3)let真的没有变量提升吗?

上面的栗子引发我思考一个问题:既然我们进入块级作用域的时候这个变量存在,那就应该是存在变量提升,只是没有到声明那一行我们不能使用它而已。笔者是这么认为的var和let都存在hoisting变量提升,只是与var变量提升的表现形式不同。var是显式的变量提升,var的变量声明会提升到全局函数的头部,并且赋予undefined的初始值。let是隐式的,let在明显的赋值之前不会被赋予undefined的初始值,在代码没有执行到let声明赋值那一行之前试图获取这个变量就会报错。上面很常见的栗子1和栗子2都用来帮助理解let暂时性死区,结论都是var存在变量提升,但是let不存在变量提升存在暂时性死区,所以打印出不同的东西。但是笔者认为,这个结论不严谨,这两个栗子只能证明,var确实存在变量提升,但不能证明let不存在变量提升。那let有没有变量提升呢?还得继续探索,笔者我也不知道。代码不就是能用就行嘛!!!!!

2、匿名函数用var和let声明会怎么样
(1)栗子1 匿名函数用var声明

什么?你不知道什么是匿名函数?

简单点说,匿名函数就是字面意思,没有名字的函数。

什么?这谁不知道,只是说语法规范或者它究竟可以干嘛?

这个啊,感兴趣的话以后继续总结一篇详细的文章介绍普通函数、立即执行函数、匿名函数的介绍。

这里简单点说,匿名函数就是没有名字的函数,通过匿名函数可以实现闭包模拟块级函数,减少函数重名造成冲突,较少全局变量的使用,匿名函数执行完之后立即销毁内存中相应的空间。

    var a = 'hello world';
    (function () {
        console.log(a);
    }())

在这里插入图片描述

解析:匿名函数可以实现闭包,在函数作用域中没有找到a变量,会沿着作用域链,往上一级找父级作用域是否有a这个变量,往上一级就找了,所以就输出了hello world。

(2)var声明换成let声明
    var a = 'hello world';
    (function () {
        console.log(a);
        let a = 'f*** world';
    }())

在这里插入图片描述

解析:这段代码报错了!笔者上面的逻辑推理貌似错了。如果let存在变量提升,应该是 a is not defined,而不是报"Cannot access ‘a’ before initialzation"。笔者对于这段代码的逻辑是,let在匿名函数内形成封闭作用域,不受外部因素的影响,暂时性死区是从代码行数2-4,打印a的语句在暂时性死区内,所以报错。

3、总结

ES6的各种学习文档都还是赞成let、const不存在变量提升,var存在变量提升。一些英文文档描述的是"let bindings are created at the top of the (block) scope containing the declaration, commonly referred to as “hoisting”",提到的是hoisting但是是加双引号的,我的理解是let并不是真正的变量提升,就像我上面说的它是不一样的形式hoisting,严格来讲由于TDZ的原因不能在声明之前使用let变量。写代码的角度,至于let有没有存在变量提升,这倒是不必要究竟,毕竟代码真的就能用就行!!变量提升本身就是js不同于其他语言的"特点"。只是一点点思考,至于对不对,笔者也不知道,这或许也是一个学习的过程。

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

傲娇味的草莓

佛系少女只是想记录学习痕迹

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值