想品客老师的第七天:闭包和作用域

闭包之前的内容写在这里

环境、作用域、回收

首先还是数据的回收问题,全局变量一般都是通过关闭页面回收的;而局部变量的值不用了,会被自动回收掉

像这种写在全局里的就不会被主动回收捏:

      let title = '荷叶饭'function fn() {alert(title)}document.querySelector('button').addEventListener('click', fn)

求后盾人老师别举他那听不懂的栗子了。。。

在计算机里环境可以理解为一块内存的数据,就是块空间

  let title='荷叶饭'function show(){let url='#'}show()console.log(url)

外面用不了里面的变量

里面用得了外面的变量:

      let title = '荷叶饭'function show() {let url = '#孩子们我是个链接'function hd() {console.log(url)//里面可以访问外面}hd()}show()

函数环境生命周期

函数在调用之前的声明相对于建造城市的计划,调用函数相当于城市动工了,调用多次函数相当于建造多个一样的城市(开辟多块空间),但是不是同一个城市,彼此独立

function buildCity() {let city = {};return city;
}let city1 = buildCity(); // 创建第一个城市
let city2 = buildCity(); // 创建第二个城市
console.log(city1 === city2); // false,两个不同的城市

只有在显式修改共享状态时,才可能出现覆盖的情况。

let sharedCity = null;function buildCity() {sharedCity = {}; // 覆盖之前的 sharedCityreturn sharedCity;
}let city1 = buildCity(); // 创建并覆盖 sharedCity
let city2 = buildCity(); // 再次覆盖 sharedCity
console.log(city1 === city2); // true,因为 sharedCity 被覆盖了

如果一个函数不return,相当于没有被外部引用,每次调用都是单独创建新的一块:

      function hd() {let n = 1;return function sum() {// console.log(++n);let m = 1;function show() {console.log("m:" + ++m);console.log("n:" + ++n);};show()};}let a = hd();a();a();a();

n被return了,所以n++,但是m是独立的没有被外部调用,所以不++

输入被return,相当于被外部引用,所以多次调用m会在原来的基础上++:

     function hd() {let n = 1;return function sum() {// console.log(++n);let m = 1;return function show() {console.log("m:" + ++m);console.log("n:" + ++n);};};}let a = hd()();a();a();a();

延长了函数的生命周期

构造函数里的环境是什么

每执行一次构造函数就会创造出一个新的对象:

function Hd() {let n = 1;this.sum = function () {console.log(++n);};}let a = new Hd();a.sum();a.sum();let b = new Hd();b.sum();b.sum();

这方面和普通函数没什么区别:

块级作用域

没报错,这就说明这是两个块

        {let a = 1}{let a = 1}

var没有块作用域的原因居然是块作用域出的比var晚,推出let和const可以适用块作用域

let-const-var在for循环

var没有块的特性:

 for (var i = 1; i <= 3; i++) {console.log(i);}console.log(i)//可以访问

在for里写个定时器

如果是var的话:

 for (var i = 1; i <= 3; i++) {setTimeout(function () {console.log(i);}, 1000);}console.log(i);

当 setTimeout 的回调函数执行时,for 循环已经执行完毕,此时 i 的值已经变成了 4,所以所有回调函数都会输出 4

换成let结果就不一样了:

let是有块级作用域的概念,for里的定时器函数在i等于不同值的时候,会先向上一级找i,还没找到全局,就在父级找到i了,找到的i就等于此时i的值

  for (let i = 1; i <= 3; i++) {setTimeout(function () {console.log(i);}, 1000);}

var模拟块作用域

用定时器+立即执行函数模拟块作用域,每次循环创建一个新的作用域,从而捕获当前的 i 值,确保 setTimeout 回调函数输出正确的值。

for (var i = 1; i <= 3; i++) {(function (i) {setTimeout(function () {console.log(i);//1,2,3}, 1000);})(i);}

多级作用域嵌套

不想被清掉的函数可以放在数组里:

   let arr = [];for (var i = 1; i <= 3; i++) {arr.push(function () {return i;})}console.log(arr[2]());//4

但是i打印出来都是4,因为定时器比for执行的慢

可以保留作用域:

    let arr = [];for (var i = 1; i <= 3; i++) {(function (i) {arr.push(function () {return i;});})(i)//把i传过去// console.log(i);}console.log(arr[2]())//3

闭包

哪个傻逼弹幕说的闭包和函数没有必然关系的

闭包(Closure)是 JavaScript 中一个非常重要的概念,也是函数式编程的核心特性之一。闭包是指一个函数能够记住并访问它的作用域,即使这个函数在其作用域之外执行

求后盾人老师不要再温故而知新了

在做一个筛数组元素的函数的时候,可以发现筛的这个方法可以单独提出来:

    let arr = [1, 23, 4, 5, 6, 7, 8, 9, 21, 10];function between(a, b) {return function(v) {return v >= a && v <= b;};}console.log(arr.filter(between(3, 9)))// [4, 5, 6, 7, 8, 9]

本来筛不同区间的数是这么做的:

let hd = arr.filter(function (v) {return v >= 2 && v <= 9;});console.log(hd);// [4, 5, 6, 7, 8, 9]let a = arr.filter(function (v) {return v >= 6 && v <= 10;});console.log(a)//[6, 7, 8, 9, 10]

现在使用闭包结构,让匿名函数访问上层函数的参数,然后再返回当作filter函数的回调函数传入

也可以实现筛选商品的效果:

 let lessons = [{title: "媒体查询响应式布局",click: 89,price: 12},{title: "FLEX 弹性盒模型",click: 45,price: 120},{title: "GRID 栅格系统",click: 19,price: 67},{title: "盒子模型详解",click: 29,price: 300}];function between(a, b) {return function(v) {return v.price >= a && v.price <= b;};}console.table(lessons.filter(between(10, 100)));

应用

加深对闭包的印象:移动动画

按钮的 position 属性需要设置为 relative 或 absoluteleft 样式才能生效。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>button {position: absolute;}</style>
</head>
<button>点我 </button>
<button>溜了溜了。。。</button><body><script>let btns = document.querySelectorAll('button')btns.forEach(function (item) {item.addEventListener('click', function () {let left = 1setInterval(function () {item.style.left = left++ + 'px'//这里使用了闭包}, 10)})})</script>
</body></html>

如果多点几次,会触发频闪效果。。。会抖动

为什么呢?因为把left的定义写在了事件监听的函数里,所以每次点击left就会重新等于1

解决方案1就是把对left的定义放在外面一层:

     let btns = document.querySelectorAll('button')btns.forEach(function (item) {let left = 1item.addEventListener('click', function () {setInterval(function () {item.style.left = left++ + 'px'}, 10)})})

当你连点两次的时候,会发现按钮不抖动了,但是移动速度加快了!

为什么移动速度变快了?因为外面没有清除定时器,每点一下,就是新增了一个定时器,到最后越来越多的定时器再给按钮做left++的运算,所以越走越快!!

解决方法可以设置一个flag量,来判定是否创建过计时器:

 let btns = document.querySelectorAll('button')btns.forEach(function (item) {let left = 1let flag = falseitem.addEventListener('click', function () {if (!flag) {flag = truesetInterval(function () {item.style.left = left++ + 'px'}, 10)}})})

反过来把left放在里面,也不会出现频闪的情况(因为定时器只能开一个):

    let btns = document.querySelectorAll('button')btns.forEach(function (item) {let flag = falseitem.addEventListener('click', function () {if (!flag) {let left = 1flag = true//写在里面setInterval(function () {item.style.left = left++ + 'px'}, 10)}})})

利用闭包特性做购物排序:

let lessons = [{title: "媒体查询响应式布局",click: 89,price: 12},{title: "FLEX 弹性盒模型",click: 45,price: 120},{title: "GRID 栅格系统",click: 19,price: 67},{title: "盒子模型详解",click: 29,price: 300}];function order(field, type = "asc") {return function(a, b) {if (type == "asc") return a[field] > b[field] ? 1 : -1;//可以自由更改升序降序return a[field] > b[field] ? -1 : 1;//并且改变根据click/price的排序};}let hd = lessons.sort(order("price"));console.table(hd);

闭包导致的内存泄漏

获取一整个对象会很臃肿,当你只想获取一个对象里的属性,可以使用一种过河拆桥的方法:获取整个对象->使用属性->销毁对象,还可解决闭包导致的内存泄漏

不是foreach是同步,先执行foreach里的同步,item是空也是同步,元素的事件绑定是异步的:

let divs = document.querySelectorAll("div");divs.forEach(function (item) {let desc = item.getAttribute("desc");item.addEventListener("click", function () {// console.log(item.getAttribute("desc"));console.log(desc);//获取属性console.log(item);});item = null;//销毁对象});

this在闭包中的历史遗留问题

this在闭包里指向混乱:

 let hd = {user: "后盾人",get: function () {return function(){return this.user;}}};let a = hd.get();console.log(a())//undefined

这是个闭包结构,a是get方法,a()就是执行get方法,返回get方法里面的匿名函数的this.user,但是这个匿名函数是个函数啊,他的this是window,window里没有user这个属性,所以返回undefined

怎么让内部的匿名函数的this指向hd?声明一个this

let hd = {user: "后盾人",get: function () {let This=thisreturn function(){return This.user;}}};let a = hd.get();console.log(a())//后盾人

也可以用箭头函数,箭头函数的this指向get方法的this,也就是对象hd,这样就可以访问user了:

 let hd = {user: "后盾人",get: function() {return () => {return this.user;};}};let a = hd.get();console.log(a())//后盾人

后盾人老师我恨你。。。

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

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

相关文章

Qt调用FFmpeg库实时播放UDP组播视频流

基于以下参考链接&#xff0c;通过改进实现实时播放UDP组播视频流 https://blog.csdn.net/u012532263/article/details/102736700 源码在windows&#xff08;qt-opensource-windows-x86-5.12.9.exe&#xff09;、ubuntu20.04.6(x64)(qt-opensource-linux-x64-5.12.12.run)、以…

Ubuntu 20.04 x64下 编译安装ffmpeg

试验的ffmpeg版本 4.1.3 本文使用的config命令 ./configure --prefixhost --enable-shared --disable-static --disable-doc --enable-postproc --enable-gpl --enable-swscale --enable-nonfree --enable-libfdk-aac --enable-decoderh264 --enable-libx265 --enable-libx…

如何在IDEA社区版Service面板中管理springboot项目

1、开启service仪表盘 2、在service仪表盘中&#xff0c;添加启动类配置项&#xff0c;专业版是SpringBoot 、社区版是application。 3、控制台彩色日志输出 右键启动类配置项&#xff0c;添加虚拟机参数 -Dspring.output.ansi.enabledALWAYS

Vue.js组件开发-如何实现带有搜索功能的下拉框

创建 Vue 项目&#xff1a; 如果还没有创建一个 Vue 项目&#xff0c;可以使用 Vue CLI 来创建一个新的项目。 vue create my-project cd my-project安装依赖&#xff1a; 如果需要使用第三方组件库&#xff0c;比如 Element UI 或 Ant Design Vue&#xff0c;可以安装相应的…

网盘资源查找工具---AI功能

01 软件介绍 这是一款融入了ai技术的网盘搜索神器&#xff0c;可以让你更快&#xff0c;更精准的找到自己需要的文件&#xff0c;不管你是找影视&#xff0c;音乐&#xff0c;还是找软件或者学习资料都可以&#xff0c;欢迎前来使用。 02 功能展示 该软件非常简洁&#xff…

【2025年数学建模美赛E题】(农业生态系统)完整解析+模型代码+论文

生态共生与数值模拟&#xff1a;生态系统模型的物种种群动态研究 摘要1Introduction1.1Problem Background1.2Restatement of the Problem1.3Our Work 2 Assumptions and Justifications3 Notations4 模型的建立与求解4.1 农业生态系统模型的建立与求解4.1.1 模型建立4.1.2求解…

【Elasticsearch】index:false

在 Elasticsearch 中&#xff0c;index 参数用于控制是否对某个字段建立索引。当设置 index: false 时&#xff0c;意味着该字段不会被编入倒排索引中&#xff0c;因此不能直接用于搜索查询。然而&#xff0c;这并不意味着该字段完全不可访问或没有其他用途。以下是关于 index:…

FPGA 使用 CLOCK_LOW_FANOUT 约束

使用 CLOCK_LOW_FANOUT 约束 您可以使用 CLOCK_LOW_FANOUT 约束在单个时钟区域中包含时钟缓存负载。在由全局时钟缓存直接驱动的时钟网段 上对 CLOCK_LOW_FANOUT 进行设置&#xff0c;而且全局时钟缓存扇出必须低于 2000 个负载。 注释&#xff1a; 当与其他时钟约束配合…

蓝桥杯3518 三国游戏 | 排序

题目传送门 这题的思路很巧妙&#xff0c;需要算出每个事件给三国带来的净贡献&#xff08;即本国士兵量减其他两国士兵量&#xff09;并对其排序&#xff0c;根据贪心的原理累加贡献量直到累加结果不大于0。最后对三国的胜利的最大事件数排序取最值即可。 n int(input()) a …

【redis初阶】redis客户端

目录 一、基本介绍 二、认识RESP&#xff08;redis自定的应用层协议名称&#xff09; 三、访问github的技巧 四、安装redisplusplus 4.1 安装 hiredis** 4.2 下载 redis-plus-plus 源码 4.3 编译/安装 redis-plus-plus 五、编写运行helloworld 六、redis命令演示 6.1 通用命令的…

LLM - 大模型 ScallingLaws 的设计 100B 预训练方案(PLM) 教程(5)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/145356022 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 Scalin…

HttpClient学习

目录 一、概述 二、HttpClient依赖介绍 1.导入HttpClient4依赖 2.或者导入HttpClient5依赖 3.二者区别 三、HttpClient发送Get请求和Post请求测试 (一)通过HttpClient发送Get请求 (二)通过HttpClient发送Post请求 一、概述 HttpClient是 Apache 软件基金会提供的一…

FAST-DDS and ROS2 RQT connect

reference: FAST-DDS与ROS2通信_ros2 收fastdds的数据-CSDN博客 software version: repositories: foonathan_memory_vendor: type: git url: https://github.com/eProsima/foonathan_memory_vendor.git version: v1.1.0 fastcdr: …

Kafka后台启动命令

#保存日志 nohup ./kafka-server-start.sh ../config/server.properties > /path/to/logfile.log 2>&1 &#不保存日志 nohup ./kafka-server-start.sh ../config/server.properties >/dev/null 2>&1 & nohup: 是一个Unix/Linux命令&#xff0c;用于…

蓝桥杯算法赛第25场月赛

前言 这些题对于我的难度有点大&#xff0c;大家感兴趣的可以来做一下&#xff0c;看一下&#xff0c;下面给大家展示一下题目 1. 桃花运走向【算法赛】 问题描述 2025 年春节&#xff0c;小明和小红兴致勃勃地去庙会玩耍。庙会上&#xff0c;一个算命先生摆摊算命&#xf…

开源先锋DeepSeek-V3 LLM 大语言模型本地调用,打造自己专属 AI 助手

DeepSeek-V3是一个强大的混合专家 (MoE) 语言模型&#xff0c;总共有 671B 个参数。为了实现高效的推理和经济高效的训练&#xff0c;DeepSeek-V3 采用了多头潜在注意力机制 (MLA) 和 DeepSeekMoE 架构&#xff0c;这些架构在 DeepSeek-V2 中得到了彻底的验证。此外&#xff0c…

喜报丨迪捷软件入选2025年浙江省“重点省专”

根据《浙江省经济和信息化厅 浙江省财政厅关于进一步支持专精特新中小企业高质量发展的通知》&#xff08;浙经信企业〔2024〕232号&#xff09;有关要求&#xff0c;经企业自主申报、地方推荐、材料初审以及专家评审等程序&#xff0c;浙江省经济和信息化厅发布了2025年浙江省…

简识JVM中并发垃圾回收器和多线程并行垃圾回收器的区别

在JVM中&#xff0c;多线程并行垃圾回收器和并发垃圾回收器是两种不同类型的垃圾回收机制&#xff0c;它们的主要区别在于垃圾收集线程与用户线程之间的运行关系&#xff0c;以及这种关系对应用程序性能的影响。以下是对这两种垃圾回收器的详细比较&#xff1a; 一、多线程并行…

Golang Gin系列-8:单元测试与调试技术

在本章中&#xff0c;我们将探讨如何为Gin应用程序编写单元测试&#xff0c;使用有效的调试技术&#xff0c;以及优化性能。这包括设置测试环境、为处理程序和中间件编写测试、使用日志记录、使用调试工具以及分析应用程序以提高性能。 为Gin应用程序编写单元测试 设置测试环境…

通过 NAudio 控制电脑操作系统音量

根据您的需求&#xff0c;以下是通过 NAudio 获取和控制电脑操作系统音量的方法&#xff1a; 一、获取和控制系统音量 &#xff08;一&#xff09;获取系统音量和静音状态 您可以使用 NAudio.CoreAudioApi.MMDeviceEnumerator 来获取系统默认音频设备的音量和静音状态&#…