let-const块级作用域浅析

var的块级作用域
  • 在我们前面的学习中,JavaScript只会形成两个作用域:全局作用域和函数作用域
    在这里插入图片描述
  • ES5中放到一个代码中定义的变量,外面是可以访问的:

通过var声明的变量或者非严格模式下(non-strict mode)创建的函数声明没有块级作用域。在语句块里声明的变量的作用域不仅是其所在的函数或者script标签内,所设置变量的影响会在超出语句块本身之外持续存在。换句话说,这种语句块不会引入一个作用域。尽管单独的语句块是合法的语句,但在JavaScript中你不会想使用单独的语句

//var没有块级作用域
{
    var foo = "小余"
}

console.log(foo);//foo,正常打印,代码块中并没有阻挡住var声明出来的foo
let/const的块级作用域
  • 在ES6中新增了块级作用域,并且通过let、const、function、class声明的标识符是具备块级作用域的限制的:
{//形成作用域了
    let age = 18
    const name = "小余"
}

console.log(name,age);//报错,无法访问,说明{}形成的作用域块将let跟class限制住了
  • 但是我们会发现函数拥有块级作用域,但是外面依然是可以访问的:
    • 这是因为V8引擎会对函数的声明进行特殊的处理,允许像var那样在外界直接访问
    • 提升的原因:理论来说不应该允许像var那样在外界直接访问的,因为这个也属于糟粕,不符合规范,但是早期的代码有不少的function是写在{}的里面的,如果不允许在外界访问,那会造成以前的代码库大量的报错,而现在依旧有很多的代码会去引用那些以前的代码库,如果报错就需要去修改别人库的源码,这是一件成本很高的事情,因为很多旧的库都没有人维护了。所以允许像var那样在外界直接访问的原因是为了兼容以前的代码
    • 但是function跟var依旧有区别的,那就是var可以在声明地方的上方进行调用(输出undefined),但是function不行(会报错)

(掌握)let-const块级作用域的应用场景

块级作用域的应用

代码块的词法环境执行中:

在这里插入图片描述
代码块的词法环境执行结束:
在这里插入图片描述

  1. 全局上下文会指向(创建)一个词法环境,这个词法环境里面包含了环境记录outer:null

  2. 环境记录会指向全局环境记录全局环境记录里面有声明式环境记录对象式环境记录window

  3. 那我们就开始对这个进行了分门别类

    • 声明式环境记录:address
    • 对象式环境记录:message、age、foo、height( height是var声明,不受{}限制 )
  4. 提示:{}代码块不会形成新的执行上下文的,而是会创建对应的词法环境

    所以代码块的词法环境就创建了出来,他也有属于自己的环境记录outer

  5. 代码块的词法环境:outer:指向全局的词法环境环境记录:代码块的环境记录

    其中代码块的环境记录:title、info

  6. 我们在执行代码块的过程时候看似好像没有人指向它({}代码块的词法环境),但其实这个时候是由执行上下文进行指向的,所以这个代码块的内容是不会被销毁的

  7. 当我们{}代码块的内容执行完的时候,就不再被全局执行上下文指向了,然后就被销毁掉了,然后{}里面的height属性依旧可以访问,那是因为我们这个变量是使用var声明的,是存储到了全局的对象式环境记录(window)里面了

//形成的词法环境
var message = "Hello 小余"
var age = 20
function foo(){}
let address = "福建"

{
    var height = 1.75

    let title = "老师"
    let info = "了解真相"
}
按钮的案例
//HTML文件
    <button>按钮1</button>
    <button>按钮2</button>
    <button>按钮3</button>
    <button>按钮4</button>
//JS文件
const btnEls = document.querySelectorAll('button')
for(var i = 0;i < btnEls.length;i++){
    var btnEl = btnEls[i]
    btnEl.onclick = function(){
        //console.log("点击了按钮");
        console.log(`点击了按钮${i}`);
    }
}

console.log(i);
//永远是4,我们通过了var声明,那for内部声明的i会直接作用在全局上下文的环境记录里的对象式环境记录window里面,所以每一次for循环i的刷新,都会覆盖掉上一次的结果,最终留下的只是最后循环次数的结果。所以如果我们想用i来测试我们按下去的是哪个按钮,使用var声明是行不通的,因为不管按哪个按钮,最终i的结果都是最好循环的结果,是没办法知道我们具体是按下哪个按钮的
//通过let将i限制在for循环带的{}代码块的环境记录里面,i无法传递到外面,每次刷新得到的都是目前的i
//那此时最外层的console.log(i);就需要去掉了,因为id
//使用this的取巧方式
const btnEls = document.querySelectorAll('button')
for(var i = 0;i < btnEls.length;i++){
    var btnEl = btnEls[i]
    btnEl.index = i+1
    btnEl.onclick = function(){
        console.log(`点击了${this.index}按钮`);
    }
}

console.log(i);
//立即执行函数:(XXX)(i)
const btnEls = document.querySelectorAll('button')
for(var i = 0;i < btnEls.length;i++){
    var btnEl = btnEls[i];//这里要加分号,不然会出现语法错误,V8引擎误认为上面跟下面的代码是一起的
    // console.log(btnEl);
    (function(m){//通过函数多加一层作用域跟闭包,这里最后产生的是4个对象,就每次收集到是i都是不会互相干扰的(因为都在不同的对象里,收到了一个i就立马执行,然后创建出下一个新的对象准备接受下一个i)
        btnEl.onclick = function(){
            console.log(`点击了${m}按钮`);
        }
    })(i)
}

console.log(i);

在这里插入图片描述

//最终简单的let写法
//[[scope]]会将闭包的结果保留下来,造成词法环境不被销毁
const btnEls = document.querySelectorAll('button')
for(let i = 0;i < btnEls.length;i++){//形成新的词法环境了
    //debugger测试使用,能看到block的块级作用域,每次的块内容都不一样的,通过let将i限制在for循环带的{}代码块的环境记录里面,i无法传递到外面,每次刷新得到的都是目前的i
    const btnEl = btnEls[i]
    btnEl.onclick = function(){
        console.log(`点击了${i}按钮`);
    }
}

(掌握)var-let-const开发中的选择

  • 那么在开发中,我们到底应该选择使用哪一种方式来定义我们的变量呢?
  • 对于var的使用:
    • 我们需要明白一个事实,var所表现出来的特殊性:比如作用域提升、window全局对象、没有块级作用域等都是一些历史遗留问题
    • 其实是JavaScript在设计之初的一种语言缺陷
    • 当然目前市场上也在利用这种缺陷出一系列的面试题,来考察大家对JavaScript语言本身以及底层的理解
    • 但是在实际工作中,我们可以使用最新的规范来编写,也就是不再使用var来定义变量
  • 对于let、const:
    • 对于let和const来说,是目前开发中推荐使用的
    • 我们会优先推荐使用const,这样可以保证数据的安全性不会被随意的篡改
    • 只有当我们明确知道一个变量后续会需要被重新赋值时,这个时候再使用let
    • 这种在很多其他语言里面也都是一种约定俗成的规范,尽量我们也遵守这种规范
  • 19
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值