关于作用域的那些事(进阶)
一、作用域
原理:
- 作用域 => 房子 => 除了对象的{}都构成一个作用域
- 作用域 => 为了区别变量.不同作用域内声明的变量是各不相同的.(就算名字相同).
作用域语法:
- let x = 10; (全局变量).
- if () {块级作用域 let y = 20; (局部变量)}
- for () {块级作用域 let y = 20; (局部变量)}
- switch () {块级作用域 let y = 20; (局部变量)}
- function () {函数作用域 let y = 20; (局部变量)}
- let obj = {不是作用域}
作用域分类
- 全局作用域 => script内部的区域 (局部作用域外面的区域)
- 局部作用 => {}内的区域.
a: 块级作用域 => 函数{}之外的其他大括号.
b: 函数作用域 => 函数{}内部.
- 全局变量: 声明在全局作用域内的变量 => 全局变量不安全, 能不用尽量不用.
- 局部变量: 声明在局部作用域中的变量
二、变量可见性
全局变量任何地方都可以访问.
- 局部变量只能在本局部作用域中访问.
- 全局作用域中无法访问局部变量
- 局部作用域A不能访问局部作用域B中的变量
- 子作用域可以访问父作用域中的变量。
// 全局变量任何地方都可以访问.let z = 10000;// 第一个局部作用域{let x = 10;// 不能访问其他局部作用域内的变量// console.log(y);// 可以访问全局变量// console.log(z);// 可以访问本作用域内的变量console.log(x);}// 全局作用域中不能访问局部作用域中的变量// console.log(x);// 第二个局部作用域{let y = 20;// 不能访问其他局部作用域内的变量// console.log(x);// 可以访问全局变量// console.log(z);// 可以访问本作用域内的变量console.log(y);}// 全局作用域中不能访问局部作用域中的变量// console.log(y);{// 祖父作用域let n = 200;{// 父作用域let m = 100;{// 子作用域内可以访问父作用域内的变量.console.log(m);// 子作用可以访问所有祖先作用域内的变量.console.log(n);}}}
三、作用域链
- 作用域 => 房子 => 地点
- 作用域链 => 作用域套作用域 => 地点链
- 作用域链 => 保证变量的有序访问.(是一种机制)
列如:
广东省广州市海珠区。。。
// let x = 300;{// let x = 200;{let x = 100;{console.log(x);}}}
四、变量查找
我们也可以利用作用域链进行变量查找:
1: 写出作用域链.(使用变量所在代码行的作用域链)
2: 沿着这个作用域链查找变量声明(形参也算声明),找到就停止查找.(就近原则),
如果找到全局作用域就没有变量声明,就报错
- 全局作用域是所有作用域链的顶级作用域.(任何一个作用域链的最后一个作用域都 是全局作用域)
- 函数的形参相当于是声明在函数内部的.
- function fn(x) {}
1: let x = 10; (全局的x)2: fn();let x = 20; (fn内的x)add();x += 10; (把fn内的x变成30)console.log(x); (打印fn内的变量x,30);console.log(x); (打印fn内的变量x,30);
let x = 10;function fn () {let x = 20;function add() {x += 10; // add => fn => 全局console.log(x);// add => fn => 全局}add();console.log(x);// fn => 全局}fn();
let x = 10;function fn () {let x = 20;function add(x) {// 这里的x在进行变量查找时,找到的是add内的形参x,而不是fn内的x.(就近原则)x += 10; console.log(x); // 30}add(x);// 这里打印的x,是fn内的x.fn内的x没有被修过过,因此还是20.console.log(x);// 20}fn();
五、变量提升
预解析 => (*)
程序运行之前,浏览器会把函数声明和var声明的变量提升到本作用域的最前面. (变量提升)
要点:
- 1:只会提升函数声明和var声明.只提升声明部分,赋值部分不提升.
- 2:如果var变量和函数同名,则提升后,函数覆盖变量.
- 函数声明可以写在函数调用之后,因为预解析时,浏览器会把函数声明放到最前面.
console.log(x); // undefinedvar x = 10;// 以上代码预解析之后,变成下面的代码:var x;console.log(x);x = 10;
console.log(x); // 函数function x() {};var x = 10;var x;function x() {};console.log(x);x = 10
// var a是在fn内声明的,因此只能提升到fn的最前面.function fn() {console.log(a); // undefinedvar a = 10;}fn();console.log(a);// 报错.因为a不会提升到全局作用域,这里变量查找找不到a的声明,会报错.
六、块级作用域
- 块级作用域内用var声明的变量,变量提升时,会提升到块级作用域的外面。
- var 不能识别块级作用域.
- 块级作用域是ES6提出的新特性,var不是ES6的语法,因此识别不了块级作用域。
- 函数声明也不识别块级作用域.
- 只有在函数作用域内的var变量和函数才是局部变量.
块级作用域和函数作用域的区别
- let 和 const 能识别块级作用域。var不行。
- 块级作用域是ES6新增的特性。
- 用var 在块级作用域中声明变量,不构成局部变量。
{console.log(x);// undefined// 块级作用域内用var声明的变量会变成全局变量.var x = 10;// 块级作用域内的函数声明,也会提升到块级作用域的外面.function fn() {}}console.log(x);console.log(fn);
七、循环中的let和var
<body><ul> <li>1111</li><li>2222</li></ul>
</body>
循环中var和let:
- 循环用let声明i,则这个i是一个局部变量.
- 循环多少次,就有多少个块级作用域,每个作用域内有一个不同的i.
- 第一个i是0,第二个i是1,以此类推...
- 循环用var声明i,则这个i是一个全局变量.
- 不管循环多少次,修改的都是全局变量的i.
- 循环时操作元素,但凡在事件中使用了i,如果这个i是var声明的,则事件中访问到的i必定 是循环结束之后的i.
- 循环结束之后的i,必定超出了数组的最大下标.
let aLi = document.querySelectorAll('li');// 循环中有事件,事件中如果使用了循环的i,则不能用var声明。for (var i = 0; i < aLi.length; i++) {aLi[i].onclick = function() {alert(aLi[i].innerText)}}{let i = 0;// 第一个li的点击事件aLi[i].onclick = function() {alert(aLi[i].innerText); // 第一个匿名函数 => 第一个块级作用域 => 全局作用域}}{let i = 1;// 第二个li的点击事件aLi[i].onclick = function() {alert(aLi[i].innerText); // 第二个匿名函数 => 第二个块级作用域 => 全局作用域}}
let aLi = document.querySelectorAll('li');
var i = 0;{// 第一个li的点击事件aLi[i].onclick = function() {alert(aLi[i].innerText); // 第一个匿名函数 => 第一个块级作用域 => 全局作用域}}var i = 1;{// 第二个li的点击事件aLi[i].onclick = function() {alert(aLi[i].innerText); // 第二个匿名函数 => 第二个块级作用域 => 全局作用域}}
- 循环结束之后, i的值变成2. aLi[2]是不存在的.是undefined
- 当执行aLi[2].innerText时,就会报错.
let aLi = document.querySelectorAll('li');
for (var i = 0; i < 2; i++) {}{// 第一个li的点击事件aLi[0].onclick = function() {alert(aLi[i].innerText); // 第一个匿名函数 => 第一个块级作用域 => 全局作用域}}{// 第二个li的点击事件aLi[1].onclick = function() {alert(aLi[i].innerText); // 第二个匿名函数 => 第二个块级作用域 => 全局作用域}}
八、this
<ul> <li>1111</li><li>2222</li></ul>
let aLi = document.querySelectorAll('li');// 循环结束,i变成2,导致aLi[i]是undefinedfor (var i = 0; i < aLi.length; i++) {aLi[i].onclick = function() {alert(aLi[i].innerText)}}
let aLi = document.querySelectorAll('li');// 事件句柄内的this => 表示触发事件的标签.(点谁它就是谁)for (var i = 0; i < aLi.length; i++) {aLi[i].onclick = function() {// console.log(this.innerText);alert(this.innerText);}}
let aLi = document.querySelectorAll('li');for (var i = 0; i < aLi.length; i++) {// 通过自定义属性存储下标。(第一个li存0,第二个li存1)aLi[i].index = i;aLi[i].onclick = function() {// 通过当前被点击的li,获取对应的下标let i = this.index;alert(aLi[i].innerText);}}
九、自执行函数
- 自执行函数 => 匿名函数的调用
- 声明的同时,马上调用.
语法:
1:(function(形参){})(实参)
2:(function(形参){}(实参))
// 自执行函数 => 模块化开发时,需要通过自执行函数来进行模块化 => 快速创建一个函数作用域(function () {console.log(100);})();(function(){console.log(1000)}());
十、闭包
- 循环用let => 为了用let把下标0和1分别存储到两个块级作用域中.
- 循环用var => 没办法把0和1分半存储到两个块级作用域中.(因为有变量提升).
- var没办法把变量存储到块级作用域中,但是可以存储到函数作用域中.
- 闭包 => 为了把变量存储到函数作用域中.
- 闭包的两个作用 => 存储,保护. => 存储变量,保护变量
- 作用:为了把一个变量存储在函数作用域中.可以在任意时间访问.(存储,保护)
- 表现:函数套函数.子函数使用了复函数内声明的变量.子函数还可以在任何时间调用. (最重 要的条件).
- 原理:为了保证子函数调用时,沿着作用域链,能访问到父函数内的变量.只能不销毁父 函数内的变量.以便子函数在任何时间访问.
- 缺点:滥用多了会有内存泄漏.
- 工作中实际使用:工作里很少用.模块化项目中,每个模块都是一个闭包.
- 闭 => 保护
- 包 => 数据
10.0
<ul> <li>1111</li><li>2222</li></ul>
let aLi = document.querySelectorAll('li');for (var i = 0; i < aLi.length; i++) {(function(){var j = i;aLi[j].onclick = function() {alert(aLi[j].innerText)}})();}
(function(){var j = 0;// 第一个li事件aLi[j].onclick = function() {alert(aLi[j].innerText); // 第一个匿名函数 => 第一个自执行函数作用域 => 全局作用域}})();(function(){var j = 1;// 第二个li事件aLi[j].onclick = function() {alert(aLi[j].innerText); // 第二个匿名函数 => 第二个自执行函数作用域 => 全局作用域}})();
10.1
<ul> <li>1111</li><li>2222</li></ul>
<script>let aLi = document.querySelectorAll('li');// 循环多少次,就有多少个块级作用域,每个块级作用域内的i都是各不一样的.// for (var i = 0; i < aLi.length; i++) {// show(i);// }// 同一个函数调用多少次,就创建多少个函数作用域,每个函数作用域内的变量都是各不相同的.show(0);show(1);function show(j) {aLi[j].onclick = function() {alert(aLi[j].innerText)}}
</script>
十一、闭包的构成
闭包的表现形式 => 函数套函数.
闭包的构成条件:
- 1: 一定是函数套函数.
- 2: 子函数使用父函数内声明的变量.
- 3: 子函数可以在任意时间调用.(最重要).
闭包内的变量特性:
- 1:永不销毁 (全局变量的特性).
- 2:只能通过子函数来访问.(局部变量的特征).
面试特别喜欢问闭包:
- 你怎么理解闭包的? => 除了想看你基础好不好,你的表达能力如何.
- 1: 闭包的作用 => 为了把变量存储到函数作用域中.(存储,保护).
- 2: 闭包的原理 => 自行发挥.
- 3: 工作中闭包的实际应用. => 项目中的每个模块都是一个闭包.
这里不是闭包.
- j存储在了fn中了吗? => 并没有
- 函数内的局部变量会在函数调用结束之后,就会被销毁. => 节约内存.
- 全局变量在程序关闭后才会被销毁,全局变量在程序运行期间,不会被销毁.
把fn的返回值赋值给show.则show和fn就同一个函数.
- show是全局变量.show不会被销毁.
- show不会被销毁,则show可以在任意事件调用.
- show每次调用时,都会访问一个变量j.
- 为了保证show每次都能正常的访问j,浏览器就将j一直保存在内存中,不销毁它.
- 只能通过show的调用来访问j.其他方式不行.
- let show = fn();
- j不会被销毁,但是不能直接访问.
- console.log(j);
// 如何让这个j不会被销毁呢?function fn() {var j = 100;}fn();
// fn返回一个匿名函数function fn() {let j = 10;return function() {console.log(j);}}相关文章:
关于作用域的那些事(进阶)
一、作用域 原理: 作用域 > 房子 > 除了对象的{}都构成一个作用域 作用域 > 为了区别变量.不同作用域内声明的变量是各不相同的.(就算名字相同). 作用域语法: let x 10; (全局变量). if () {块级作用域 let y 20; (局部变量)} for () {块级作用…...
小技巧notebook
小技巧notebook 1、MybatisPlus 批量保存 从BaseMapper接口方法可知,mybatis plus mapper只有根据id批量删除和查询,没有批量保存(insert 、update),要实现也很简单,需要定义一个Service Service Slf4j …...
【2451. 差值数组不同的字符串】
来源:力扣(LeetCode) 描述: 给你一个字符串数组 words ,每一个字符串长度都相同,令所有字符串的长度都为 n 。 每个字符串 words[i] 可以被转化为一个长度为 n - 1 的 差值整数数组 difference[i] &…...
Java面试-每日十题
目录 1.try-catch-finally中的finally的执行机制 2.什么是Exception和Error 3.Throw和Throws的区别 4.Error与Exception区别 5.Java中的I/O流是什么,分为几类 6.I/O与NI/O 7.常用的I/O的类有哪些 8.字符流与字节流的区别 9.Java反射创建对象 10.什么是类的…...
java.awt.datatransfer.Clipboard剪切板获取String字符串文本
java.awt.datatransfer.Clipboard剪切板获取String字符串文本 有两种方法获取 直接从Clipboard获得 (String) systemClipboard.getData(DataFlavor.stringFlavor);从Clipboard获得Transable再获得String (String) systemClipboard.getContents(null).getTransferData(DataFlav…...
HCIA——VLAN
目录 1,什么是VLAN: 2,如何实现VLAN: 3,VLAN的划分方式: 4,交换机接口类型: 1,Access接口: 2,Trunk接口:允许将一个接口划分给多…...
测试分析流程及输出项
测试分析 一、确认测试范围 根据测试项目的不同需求,有大致几类测试项目类型:商户平台功能测试、支付方式接入测试、架构调整类测试、后台优化测试、性能测试、基本功能自动化测试。 测试项目需要按照文档要求进行测试需求分析,并给出对应…...
OO设计原则
OO设计原则:SOLID SOLID SRP(The Single Responsibility Principle,单一责任原则) 不应有多于1个的原因使得一个类发生变化一个类,一个责任 OCP(The Open-Closes Principle,开放-封闭原则&…...
《深入理解计算机系统(CSAPP)》第5章 优化程序性能 - 学习笔记
写在前面的话:此系列文章为笔者学习CSAPP时的个人笔记,分享出来与大家学习交流,目录大体与《深入理解计算机系统》书本一致。因是初次预习时写的笔记,在复习回看时发现部分内容存在一些小问题,因时间紧张来不及再次整理…...
【Spring Boot】033-使用 `@ResponseBody` 注解代替`ServletResponse`?
【Spring Boot】033-使用 ResponseBody 注解代替ServletResponse? 文章目录 【Spring Boot】033-使用 ResponseBody 注解代替ServletResponse?0、全局总结一、ResponseBody 注解与 ServletResponse 比较1、ResponseBody 注解2、ServletResponse3、总结 二…...
【openGauss实战13】闪回技术
📢📢📢📣📣📣 哈喽!大家好,我是【IT邦德】,江湖人称jeames007,10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】!😜&am…...
Top大学教授:青年学者,请避免这些写作问题→
在科研初期,很多作者由于缺乏经验和指导,糊里糊涂地发了一些质量较低的论文。 为了帮助青年科学家提高写作能力,比利时鲁汶大学的Blocken教授(同时也是Building & Environment、Journal of Wind Engineering & Industrial…...
使用midjourney搞出一套三国人物画像!
当下已进入如火如荼的全民AI时代,最近体验了下midjourney,使用它的以图生图功能生成出来一套三国人物画像,和大家分享下使用心得。 使用midjourney的准备工作 下载工具 使用midjourney生产图片依赖的工具和流程,大致如下&#x…...
ELK日志分析系统
ELK日志分析系统 日志主要包括系统日志/var/log 应用日志 安全日志secure, rsyslog远程传输日志进行汇总集中化管理,日志统计和检索又成为一件比较麻烦的事情,、 1、完整日志系统基本特征 收集:能够采集多种来源的日志数据 …...
整型在内存中的存储
目录 一、为什么内存中存储补码? 二、大小端概念 百度笔试试题: 几道小题: 一、为什么内存中存储补码? 上一节我们了解了原码,反码,补码的概念(http://t.csdn.cn/N0grg)ÿ…...
子集-回溯算法
1题目 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例 1: 输入:nums [1,2,3] 输出:[[],[1],[2],[1…...
公司study three
ctrlwind:新建桌面 ctrlwin 箭头 切换桌面 WIN CTRL F4 删除桌面 stream foreach遍历 instFormModifytracesList.stream().forEach(s->{ s.setModifyUser(sysUserTemplate.getNameById(s.getModifyUser()));});拼接 String collect2 peopleList.stream()…...
【运维】speedtest测试
目录 docker 布署 布署云端 docker布署 云端放置于已有容器里 librespeed/speedtest: Self-hosted Speedtest for HTML5 and more. Easy setup, examples, configurable, mobile friendly. Supports PHP, Node, Multiple servers, and more (github.com) docker 布署 获取…...
CycloneDDS开源代码在Linux系统上编译生成可执行文件的详细步骤
cyclonedds开源代码在Linux系统上编译生成可执行文件的详细步骤 1 远程仓库CycloneDDS源码下载2 创建build目录3 进入build目录4 指定安装路径前缀5 编译 cmake --build6 编译完成后进行安装7 版本构建并编译7.1 虚拟机网络桥接7.2 镜像源添加7.3 CUnit单元测试工具安装7.4 编译…...
PLL锁相环的一部分--鉴频鉴相器
鉴频鉴相器作为锁相环的一部分也是有相对应的独立芯片. 鉴频鉴相器芯片主要有以下几种: LM565/LM565C 鉴频鉴相器芯片XR2211CP 鉴频鉴相器芯片NE567 比较器、鉴频、鉴相 ICMC1496/LM1496 综合运算放大器与调制/解调器 ICLM567 比较器、鉴频、鉴相 ICMC100EP2100 高…...
uniAPP 所有章节知识体系概述和网站播放器落地一体方案
uniAPP 前十章知识体系 由于 uni-app 并没有官方统一的"前十章"教程划分,不同教材的章节结构有所不同。以下基于多本主流教材内容,整合出一套通用的 uni-app 学习路线,涵盖从入门基础到后端通信的核心知识。 第1章 初识 uni-app 本章是学习的起点,帮助建立对 …...
高斯过程回归与离散变分原理:数据驱动的物理结构发现
1. 项目概述:当高斯过程回归遇见离散变分原理在物理信息机器学习这个交叉领域,我们常常面临一个核心挑战:如何从有限的、可能带有噪声的观测数据中,不仅还原出物理系统的动态,还能揭示其背后深刻的数学结构?…...
【ChatGPT脑筋急转弯生成实战指南】:20年AI工程师亲授5大提示工程心法,3步产出高智商、零冷场的原创谜题
更多请点击: https://intelliparadigm.com 第一章:ChatGPT脑筋急转弯生成实战导论 脑筋急转弯作为语言智能的微型压力测试场,天然契合大语言模型的语义推理、歧义识别与幽默生成能力。本章聚焦于利用 ChatGPT(以 OpenAI API v1 接…...
终极Win11系统优化指南:Win11Debloat深度清理教程
终极Win11系统优化指南:Win11Debloat深度清理教程 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to declutter and custom…...
Armv8-R内存一致性模型解析与Cortex-R82实践
1. Cortex-R82/R82AE内存一致性解析:架构师视角的深度指南 在实时计算领域,内存一致性模型直接影响着多核系统的确定性和性能表现。作为Armv8-R架构的旗舰处理器,Cortex-R82/R82AE集群通过精细的内存属性控制机制,为汽车电子、工业…...
JHenTai:5大核心功能打造你的全平台漫画阅读体验
JHenTai:5大核心功能打造你的全平台漫画阅读体验 【免费下载链接】JHenTai A cross-platform manga app made for e-hentai & exhentai by Flutter 项目地址: https://gitcode.com/gh_mirrors/jh/JHenTai 在数字阅读时代,寻找一款既能在手机上…...
PXE启动Ubuntu时,你的initrd.img配置对了吗?一个参数让无盘启动快3倍
PXE启动Ubuntu时initrd.img的深度调优指南当你在凌晨三点盯着PXE启动进度条缓慢爬升时,是否想过那个看似简单的initrd.img文件里藏着多少性能玄机?作为运维老兵的我在经历了数十次无盘系统部署后,发现90%的PXE启动性能问题都源于initrd配置不…...
告别虚拟机卡顿:在VMware里给CentOS 7最小化安装分配多少内存和CPU才够用?
虚拟机性能优化指南:CentOS 7最小化安装的资源分配策略在个人电脑上运行虚拟机时,最令人头疼的问题莫过于性能瓶颈。许多初学者在创建CentOS 7虚拟机时,常常陷入两难境地:分配过多资源会影响宿主机运行,分配过少又会导…...
5分钟解锁全皮肤:R3nzSkin国服特供版完全指南
5分钟解锁全皮肤:R3nzSkin国服特供版完全指南 【免费下载链接】R3nzSkin-For-China-Server Skin changer for League of Legends (LOL) 项目地址: https://gitcode.com/gh_mirrors/r3/R3nzSkin-For-China-Server 你是否曾为心仪的限定皮肤望而却步࿱…...
Kubernetes容器运行时对比分析:选择最适合你的容器运行时
Kubernetes容器运行时对比分析:选择最适合你的容器运行时 一、容器运行时概述 容器运行时(Container Runtime) 是Kubernetes集群中负责运行容器的底层软件。它负责从镜像仓库拉取镜像、创建和管理容器进程、提供隔离环境等核心功能。 1.1 …...
