JS作用域:全局作用域,函数作用域,块级作用域

JS作用域:全局作用域,函数作用域,块级作用域

  • 背景
  • 作用域
    • 全局作用域
    • 函数作用域
    • 块级作用域
      • 通过调用栈分析块级作用域
      • 开发者工具查看作用域
      • 选项卡示例

背景

由于 JavaScript 存在变量提升这种特性,从而导致很多与直觉不符的代码,这也是 JavaScript 的一个重要设计缺陷,这种设计缺陷带来的问题可以去看看JS变量和函数提升。

所以 ES6 通过引入块级作用域并配合 letconst 关键字来避开了这种设计缺陷,但是由于 JavaScript 需要向下兼容,所以变量提升在相当长一段时间内还会继续存在。

作用域

作用域是指在程序中定义变量的区域,该位置决定了变量的生命周期。即,作用域是变量和函数的可访问范围,控制着变量和函数的可见性和生命周期。

ES6(2015年6月) 之前,ES 的作用域只有两种:全局作用域函数作用域。相较而言,其他语言则都普遍支持块级作用域

全局作用域

全局作用域中的对象在代码中的任何地方都能访问,其生命周期伴随着页面的生命周期。

函数作用域

函数作用域就是在函数内部定义的变量或函数,并且定义的变量或函数只能在函数内部被访问,函数执行结束后,函数内部定义的变量就会被销毁。

块级作用域

ES6 中给 JavaScript 新增了块级作用域

  • 块级作用域由{}包括,if语句和for语句里面的{}都属于块级作用域
  • var定义的变量没有块级作用域概念,可以跨块级作用域访问
  • letconst定义的变量只能在块级作用域里访问

下面几段示例代码可以帮助你对块级作用域的理解:

{var a = 1let b = 2const c = 3
}
console.log(a) // 1
console.log(b) // 报错(let和const只能在块级作用域中访问)
console.log(c) // 报错
for(var a = 0; a < 3; a++) {var d = 5
}
console.log(a) // 3
console.log(d) // 5for(let a = 0; a < 3; a++) {var d = 5
}
console.log(a) // 报错(for语句属于块级作用域)
console.log(d) // 5
if(true) {var a = 5
}
console.log(a) // 5if(true) {let a = 5
}
console.log(a) // 报错(if语句属于块级作用域)

通过调用栈分析块级作用域

不了解调用栈的可以先去看JS调用栈

function foo() {var a = 1let b = 2{let b = 3var c = 4let d = 5console.log(a)console.log(b)}console.log(b)console.log(c)console.log(d)
}
foo()
  1. 第一步编译并创建执行上下文。函数内部通过 var 声明的变量在编译阶段全都被存到变量环境里
    在这里插入图片描述

  2. 第二步继续执行代码。执行到函数内部代码块,在块级作用域之前,变量环境中的 a 已经被设置成了 1,词法环境中的 b 已经被设置成了 2
    在这里插入图片描述

  3. 进入函数内的块级作用域,块级作用域中通过 let 声明的变量会被存放在词法环境一个单独的区域中,这个区域中的变量并不影响块级作用域块外面的变量,比如它们都有一个变量 b其实在词法环境中维护了一个小型的栈结构,栈底时函数最外层的变量,进入一个块级作用域后,就会把该作用域内的变量压入栈中,当该作用域执行完成后就从栈中弹出。当然,这里的变量是指通过 letconst 声明的变量。
    在这里插入图片描述

  4. 当执行到块级作用域中的 console.log(a) 这行代码时,需要在词法环境和变量环境中查找 a 的值,具体查找方向是:沿着词法环境的栈顶向下查询,如果有直接返回给 JavaScript 引擎,如果都没有就去变量环境中查找。

  5. 块级作用域执行完后返回内容,弹出栈中,执行结果为
    在这里插入图片描述
    在这里插入图片描述

所以,块级作用域是通过词法环境的栈结构来实现的,而变量提升是通过变量环境来实现的,通过两者的结合,JavaScript 引擎也就同时支持了变量提升和块级作用域了。

开发者工具查看作用域

var a = 'hello'
let b = 18
const c = 'female'
{var aa = 'hi'let bb = 188const cc = 'male'debugger
}
function foo() {var aaa = 'go'let bbb = 10const ccc = 'man'{var aaaa = 'ok'let bbbb = 8const dddd = 'girl'debugger}debugger
}
foo()

打开Chrome开发者工具查看这段代断点处作用域下变量值

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

选项卡示例

<style>button.active{color: red;}p{display: none;}p.active{display: block;background-color: red;}
</style><body><button>选项一</button><button>选项二</button><button>选项三</button><p>内容一</p><p>内容二</p><p>内容三</p>
</body>
// 用var
<script>let buttons = document.querySelectorAll('button')let ps = document.querySelectorAll('p')for(var i = 0; i < buttons.length; i++) {buttons[i].index = ibuttons[i].onclick = function() {for(var j = 0; j < buttons.length; j++) {buttons[j].className = ''ps[j].className = ''}this.className = 'active'ps[this.index].className = 'active'}}
</script>
</body>
// 使用let块级作用域
let buttons = document.querySelectorAll('button')
let ps = document.querySelectorAll('p')for(let i = 0; i < buttons.length; i++) {buttons[i].onclick = function() {for(var j = 0; j < buttons.length; j++) {buttons[j].className = ''ps[j].className = ''}this.className = 'active'ps[i].className = 'active' // i的for循环每一次都会生成一个块级作用域所以每个i都在指定的作用域中工作}
}

总结:选项卡实例中,使用var没有块级作用域所以for循环结束,i就变成了最后的值,所以必须要将索引值赋值给每一个元素,然后点击的时候根据索引去判断显影性。
使用let创造了块级作用域,每次循环都会创造一个块级作用域的i,所以可以直接使用ps[i]去对对应的元素设置类名。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/309636.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c++哈希表——超实用的数据结构

文章目录 1. 概念引入1.1 整数哈希1.1.1 直接取余法。1.1.2 哈希冲突1.1.2.1 开放寻址法1.1.2.2 拉链法 1.2 字符串哈希 3.结语 1. 概念引入 哈希表是一种高效的数据结构 。 H a s h Hash Hash表又称为散列表&#xff0c;一般由 H a s h Hash Hash函数(散列函数)与链表结构共同…

CentOS7 系统安装

系统下载 官方下载 清华源下载 安装流程 1. 选择安装系统 2. 选择安装语言 3. 设置网络链接 4. 设置静态IP ![img](https://img-blog.csdnimg.cn/img_convert/53bfedd54b838f95bd8bcb2efa232e23.png)设置时区 5. 磁盘设置&#xff0c;无特殊需求默认就好 6. 安装模式选择 7…

2023年年终总结 —— 致满载荣誉或满脸惆怅的你

开篇请允许我引用歌曲《西楼儿女》的一句歌词&#xff1a; 陌生的朋友你请听我讲&#xff0c;许多年前我也曾有梦想&#xff0c;想过满载荣誉回到家乡&#xff0c;这肆意的风压弯了海棠。 2023年即将过去&#xff0c;不管你这一年经历了多少&#xff0c;或是职场的得心应手、情…

从寄存器到内存访问(程序重定位角度)逐步认识8086处理器

文章目录 一、8086的通用寄存器二、8086的内存访问和字节序三、程序的分段3.1 常见名词含义3.2 指令执行过程 四、程序的重定位难题五、段地址和偏移地址六、8086内存访问困境七、8086选择段地址的策略八、8086的内存访问过程九、逻辑地址和分段的灵活性 一、8086的通用寄存器 …

基于AT24C02的多机通信

//单片机U3对AT24C02的读取程序 #include <reg51.h> // 包含51单片机寄存器定义的头文件 #include <intrins.h> //包含_nop_()函数定义的头文件 #define OP_READ 0xa1 // 器件1地址以及读取操作,0xa1即为1010 0001B #define OP_WRI…

18 UVM Scoreboard

UVM scoreboard是一个检查DUT功能的组件。它用analysis export从monitor接受transaction事务以进行检查。 uvm_scoreboard class declaration: virtual class uvm_scoreboard extends uvm_component User-defined scoreboard class declaration: 用户定义的scoreboard是从 u…

在thingsboard中跨设备动态时间段求遥测平均值

有a,b两个传感器,a传感器是电流,b传感器是振动频率。 当a传感器的电流大于x时,表明设备开始工作。 当a传感器的电流小于x时,表明设备停止工作。 要求,在规则链里求出设备工作时间段的振动频率的平均值,并基于平均值来判断是否发送告警。 这是一个典型的 在thingsboard中…

VSCODE 修改Test模式下的的java jvm堆内存大小

在settings.json中添加如下语句 "java.test.config": {"vmArgs": ["-Xmx12G"]},

Ps:八大混合模式及其在色彩渲染上的运用

在所有的图层混合模式中&#xff0c;有八种比较特别。 特别之处在于&#xff0c;其它的混合模式在修改图层的“不透明度”或“填充”时&#xff0c;效果是一样的。 而这八种混合模式使用“填充”比使用“不透明度”可带来更好的效果&#xff0c;有时甚至可以说是惊艳。 提示&am…

【前端面经】即时设计

目录 前言一面git 常见命令跨窗口通信vue 响应式原理发布订阅模式翻转二叉树Promise.all()扁平化数组面试官建议 二面Event Loop 原理Promise 相关css 描边方式requestAnimationReact 18 新特性JSX 相关react 输出两次函数式编程React 批处理机制http请求头有哪些本地存储性能优…

oracle-存储结构

文件包括 控制文件.ctl、数据文件.dbf、日志文件.log这三类放在存储上。 参数文件&#xff1a;空间的划分&#xff0c;进程的选用&#xff08;.ora&#xff09; oracle启动的时候需要读一下&#xff0c;数据库启动后&#xff0c;参数文件并不关闭&#xff0c;但即使文件丢了&a…

基于Python的电商手机数据可视化分析和推荐系统

1. 项目简介 本项目旨在通过Python技术栈对京东平台上的手机数据进行抓取、分析并构建一个简单的手机推荐系统。主要功能包括&#xff1a; 网络爬虫&#xff1a;从京东获取手机数据&#xff1b;数据分析&#xff1a;统计各厂商手机销售分布、市场占有率、价格区间和好评率&am…