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

news2025/7/9 5:20:35

闭包之前的内容写在这里

环境、作用域、回收

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

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

      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 = {}; // 覆盖之前的 sharedCity
    return 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 = 1
                setInterval(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 = 1
            item.addEventListener('click', function () {

                setInterval(function () {
                    item.style.left = left++ + 'px'
                }, 10)
            })
        })

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

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

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

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

            })
        })

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

    let btns = document.querySelectorAll('button')
        btns.forEach(function (item) {
            let flag = false
            item.addEventListener('click', function () {
                if (!flag) {
                    let left = 1
                    flag = 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=this
                    return 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.coloradmin.cn/o/2283912.html

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

相关文章

速通Docker === Docker 镜像分层存储机制

目录 分层存储的概念 分层存储的实现 镜像层 容器层 分层存储的优势 1. 镜像轻量化 2. 快速构建与部署 3. 高效的镜像共享 4. 版本控制 分层存储的示例 容器层的临时性与数据持久化 总结 Docker 的分层存储机制是其核心特性之一&#xff0c;它使得镜像的构建、共享和…

开源智慧园区管理系统对比五款主流产品探索智能运营新模式

内容概要 在这个数字化迅速发展的时代&#xff0c;园区管理也迎来了全新的机遇和挑战。众所周知&#xff0c;开源智慧园区管理系统作为一种创新解决方案&#xff0c;正逐步打破传统管理的局限性。它的开放性不仅使得系统可以根据具体需求进行灵活调整&#xff0c;也为用户提供…

非根目录部署 nextjs 项目,资源文件 请求404 的问题

最近在学习next项目编写的代码放到服务器上静态资源404 先分析问题 到服务器上查看是有资源目录的是不是项目配置有问题是不是nginx配置有问题 经过排查1和2是没有问题的目前来看只有3 检查一下nginx配置 尝试着把静态资源的配置禁用 问题解决 我的next项目用的是pm2管理…

mysql 学习5 mysql图形化界面DataGrip下载 安装 使用

一、下载和安装 下载&#xff1a; 其他版本 - DataGrip PS&#xff1a;安装目录最好不要有中文。 C:\Program Files\JetBrains\DataGrip 2023.3.4 二、 夸克网盘分享 当前电脑是下载到 &#xff1a;D:\Ctool\mysql\datagrip2023_3_4\datagrip2024\jetbra-datagrip\scripts …

读写和解析简单的 nc 文件

NetCDF 文件格式在气象数据工程领域占据着举足轻重的地位&#xff0c;其结构灵活、强兼容性等优势使其成为该领域的一个标准。无论是从事学术研究还是工程实践&#xff0c;掌握这种数据格式变得越发重要。其次&#xff0c;我注意到目前社区中气象编程大多数课程都聚焦于某个特定…

LLM:BERT or BART 之BERT

文章目录 前言一、BERT1. Decoder-only2. Encoder-only3. Use of Bidirectional Context4. Masked Language Model (MLM)5. Next Sentence Prediction (NSP)6. Fine-tune1、情感分析2、句对分析3、命名实体识别&#xff08;NER&#xff09; 7. BERT总结 总结 前言 NLP选手对这…

【力扣:新动计划,编程入门 —— 题解 ③】

—— 25.1.26 231. 2 的幂 给你一个整数 n&#xff0c;请你判断该整数是否是 2 的幂次方。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 如果存在一个整数 x 使得 n 2x &#xff0c;则认为 n 是 2 的幂次方。 示例 1&#xff1a; 输入&#xff1a;…

Centos7系统php8编译安装ImageMagick/Imagick扩展教程整理

Centos7系统php8编译安装ImageMagick/Imagick扩展教程整理 安装php8安装ImageMagick1、下载ImageMagick2、解压并安装3、查看是否安装成功 安装imagick扩展包 安装php8 点我安装php8 安装ImageMagick 1、下载ImageMagick wget https://www.imagemagick.org/download/ImageMa…

android的gradle

Gradle User Manual gradle官网 这里有个gradlew很有用&#xff0c;因为这个可以在窗口中运行gradlew脚本 gradlew 和 gradlew.bat 都是 Gradle Wrapper&#xff08;Gradle 包装器&#xff09; 的一部分&#xff0c;它们的作用是让项目可以使用 Gradle 而无需提前在系统中…

2025美赛MCM数学建模A题:《石头台阶的“记忆”:如何用数学揭开历史的足迹》(全网最全思路+模型)

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ 《石头台阶的“记忆”&#xff1a;如何用数学揭开历史的足迹》 目录 《石头台阶的“记忆”&#xff1a;如何用数学揭开历史的足迹》 ✨摘要✨ ✨引言✨ 1. 引言的结构 2. 撰写步骤 &#xff08;1&#xff09;研究背景 &#…

【S32K3 RTD LLD篇7】K344中心对齐PWM中心点触发ADC BCTU采样

【S32K3 RTD LLD篇7】K344中心对齐PWM中心点触发ADC BCTU采样 一&#xff0c;文档简介二&#xff0c;中心对齐PWM中心点触发ADC原理2.1 如何生成中心对齐的PWM2.2 如何生成PWM中心点触发标志 三&#xff0c; 软件配置与实现3.1 Demo CT 模块配置3.1.1 引脚配置3.1.2 时钟配置3.…

14-6-3C++STL的list

&#xff08;一&#xff09;list的插入 1.list.insert(pos,elem);//在pos位置插入一个elem元素的拷贝&#xff0c;返回新数据的位置 #include <iostream> #include <list> using namespace std; int main() { list<int> lst; lst.push_back(10); l…

unity学习20:time相关基础 Time.time 和 Time.deltaTime

目录 1 unity里的几种基本时间 1.1 time 相关测试脚本 1.2 游戏开始到现在所用的时间 Time.time 1.3 时间缩放值 Time.timeScale 1.4 固定时间间隔 Time.fixedDeltaTime 1.5 两次响应时间之间的间隔&#xff1a;Time.deltaTime 1.6 对应测试代码 1.7 需要关注的2个基本…

HarmonyOS:创建应用静态快捷方式

一、前言 静态快捷方式是一种在系统中创建的可以快速访问应用程序或特定功能的链接。它通常可以在长按应用图标&#xff0c;以图标和相应的文字出现在应用图标的上方&#xff0c;用户可以迅速启动对应应用程序的组件。使用快捷方式&#xff0c;可以提高效率&#xff0c;节省了查…

mysql 学习6 DQL语句,对数据库中的表进行 查询 操作

前期准备数据 重新create 一张表 create table emp(id int comment 编号,workno varchar(10) comment 工号,name varchar(10) comment 姓名,gender char comment 性别,ager tinyint unsigned comment 年龄,idcard char(18) comment 身份证号,workaddress varchar(10) c…

【ES实战】治理项之索引模板相关治理

索引模板治理 文章目录 索引模板治理问题现象分析思路操作步骤问题程序化方案索引与索引模板增加分片数校验管理 彩蛋如何查询Flink on Yarn 模式下的Task Manager日志相关配置查询已停止的Flink任务查询未停止的Flink任务 问题现象 在集群索引新建时&#xff0c;索引的分片比…

springboot3 集成 knife4j(接口文档)

提示&#xff1a;文章是集成 knife4j&#xff0c;而非 swagger2 或者 swagger3&#xff0c;效果如图 文章目录 前言一、添加依赖二、如何集成1.配置文件2.注解部分1.Tag2.Operation3.Parameter4.Schema 3.使用 总结 前言 提示&#xff1a;&#xff1a;大家在开发阶段&#xff…

51单片机开发:独立键盘实验

实验目的&#xff1a;按下键盘1时&#xff0c;点亮LED灯1。 键盘原理图如下图所示&#xff0c;可见&#xff0c;由于接GND&#xff0c;当键盘按下时&#xff0c;P3相应的端口为低电平。 键盘按下时会出现抖动&#xff0c;时间通常为5-10ms&#xff0c;代码中通过延时函数delay…

Flutter_学习记录_Tab的简单Demo~真的很简单

1. Tab的简单使用了解 要实现tab(选项卡或者标签视图)需要用到三个组件&#xff1a; TabBarTabBarViewTabController 这一块&#xff0c;我也不知道怎么整理了&#xff0c;直接提供代码吧&#xff1a; import package:flutter/material.dart;void main() {runApp(MyApp());…

GESP2024年3月认证C++六级( 第三部分编程题(1)游戏)

参考程序&#xff1a; #include <cstdio> using namespace std; const int N 2e5 5; const int mod 1e9 7; int n, a, b, c; int f[N << 1]; int ans; int main() {scanf("%d%d%d%d", &n, &a, &b, &c);f[N n] 1;for (int i n; i…