JavaScript中的作用域、闭包、定时器 由浅入深

1. JavaScript中的作用域是什么?

作用域(Scope)是程序中定义变量的区域,它决定了变量的可访问性(可见性)。在JavaScript中,作用域主要分为三种:全局作用域、函数作用域和块级作用域(ES6引入)。

  1. 全局作用域:在代码任何地方都能访问的变量,它们定义在函数外部。在浏览器中,全局作用域是window对象。
  2. 函数作用域:在函数内部定义的变量,只能在函数内部访问。
  3. 块级作用域:由一对花括号{}限定,使用let和const声明的变量只能在块内访问(例如:if、for循环等)。
    作用域链:当访问一个变量时,JavaScript引擎会从当前作用域开始查找,如果当前作用域没有,则向上一级作用域查找,直到全局作用域。如果全局作用域也没有,则报错(ReferenceError)。

案例分析1:作用域

var globalVar = "global"; // 全局作用域
function outer() {var outerVar = "outer"; // outer函数作用域function inner() {var innerVar = "inner"; // inner函数作用域console.log(globalVar); // 可以访问全局变量console.log(outerVar);  // 可以访问外部函数变量console.log(innerVar);  // 可以访问自身变量}inner();console.log(globalVar); // 可以访问全局变量console.log(outerVar);  // 可以访问自身变量// console.log(innerVar); // 报错,innerVar在inner函数内部
}
outer();
// console.log(outerVar); // 报错,outerVar在outer函数内部

2. 闭包会在哪些场景中使用?

闭包(Closure)是指一个函数可以访问并记住其词法作用域,即使该函数在其词法作用域之外执行。简单来说,闭包就是函数内部定义的函数,并且这个内部函数可以访问外部函数的变量。
闭包的使用场景:

  1. 封装私有变量:通过闭包创建私有变量,避免全局污染。
  2. 模块模式:创建模块,暴露公共接口,隐藏私有实现。
  3. 回调函数:在异步编程中,回调函数常常形成闭包,以保留某些状态。
  4. 函数工厂:创建多个相似但配置不同的函数。
  5. 在定时器、事件监听器等需要保留状态的场景。

案例分析2:闭包

// 封装私有变量
function createCounter() {let count = 0; // 私有变量return {increment: function() {count++;return count;},decrement: function() {count--;return count;},getCount: function() {return count;}};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getCount());  // 2
// 模块模式
const Module = (function() {let privateVar = 'I am private';function privateMethod() {console.log(privateVar);}return {publicMethod: function() {privateMethod();}};
})(); //自调用,把最终函数返回的结果赋值给Module 
Module.publicMethod(); // 输出: I am private
// Module.privateMethod(); // 报错:未定义
// console.log(Module.privateVar); // 报错:未定义
// 回调函数中使用闭包
function delayedLog(value, delay) {setTimeout(function() {console.log(value);}, delay);
}
delayedLog('Hello after 1 second', 1000);

3.通过定时器循环输出自增的数字,通过js的代码如何实现?

我们经常会遇到需要每隔一段时间输出一个自增的数字,例如从0开始,每秒输出一个数字,依次递增。我们可以使用setIntervalsetTimeout来实现。
但是有一个经典的陷阱:如果直接在循环中使用setTimeout,并且不采取任何措施,由于JavaScript的事件循环机制,循环变量在循环结束后才会被更新,导致输出相同的值(循环结束后的值)或者不符合预期的值。
解决方案:

  1. 使用闭包:在循环体内为每次迭代创建一个闭包,以捕获当前的循环变量。
  2. 使用let声明循环变量:因为let具有块级作用域,每次循环都会创建一个新的变量绑定。
  3. 使用setTimeout的第三个参数(将额外的参数传递给回调函数,但这种方式在循环中并不直接解决递增问题,但可以传递当前值)。

定时器循环输出自增的数字
错误示例:

//错误示例:
for (var i = 0; i < 5; i++) {setTimeout(function() {console.log(i); // 输出5次5}, 1000);
}
// 原因:所有定时器回调共享同一个变量i,循环结束后i为5,所以输出5次5。

正确方法1:使用闭包(IIFE)

for (var i = 0; i < 5; i++) {(function(j) {setTimeout(function() {console.log(j); // 0,1,2,3,4}, 1000 * j); // 为了更明显,这里让每个定时器间隔1秒的倍数})(i);
}

正确方法2:使用let声明循环变量(推荐)

for (let i = 0; i < 5; i++) {setTimeout(function() {console.log(i); // 0,1,2,3,4}, 1000 * i);
}

正确方法3:使用setTimeout的第三个参数(传递参数)

for (var i = 0; i < 5; i++) {setTimeout(function(j) {console.log(j); // 0,1,2,3,4}, 1000 * i, i); // 第三个参数i会作为回调函数的参数
}

如果希望每隔1秒输出一个数字,并且连续输出(0,1,2,3,4),可以使用setInterval,但要注意清除定时器。
使用setInterval:

let i = 0;
const timer = setInterval(function() {console.log(i);i++;if (i >= 5) {clearInterval(timer);}
}, 1000);

或者使用递归的setTimeout(避免setInterval的连续执行可能带来的问题,如执行时间超过间隔时间):

let i = 0;
function printNumber() {console.log(i);i++;if (i < 5) {setTimeout(printNumber, 1000);}
}
setTimeout(printNumber, 1000);

总结:

  1. 作用域决定了变量的可见性,分为全局、函数和块级作用域。
  2. 闭包常用于封装私有变量、模块模式、回调函数等,可以记住并访问其词法作用域。
  3. 定时器循环输出自增数字时,要注意循环变量的作用域问题,可以使用闭包、let或setTimeout的第三个参数解决。同时,可以使用setInterval或递归setTimeout来实现连续输出。

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

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

相关文章

仓库管理系统-11-前端之头部区域Header的用户登录和退出功能

文章目录 1 登录功能 1.1 登录页面(Login.vue) 1.1.1 页面布局 1.1.2 初始化数据 1.1.3 confirm方法 1.1.4 UserController.java(登录接口) 1.1.5 Login.vue 1.2 登录页面的路由 1.2.1 创建路由文件(router/index.js) 1.2.2 注册路由器(main.js) 1.2.3 路由视图(App.vue) 2 退出…

【VLNs篇】07:NavRL—在动态环境中学习安全飞行

项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战&#xff0c;克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…

飞算科技:以自主创新引领数字科技浪潮,飞算JavaAI赋能产业智能化升级

技术创新已成为企业突破瓶颈、实现跨越式发展的核心驱动力。作为国家级高新技术企业&#xff0c;飞算数智科技&#xff08;深圳&#xff09;有限公司&#xff08;简称“飞算科技”&#xff09;凭借其深厚的互联网科技、大数据与人工智能技术积淀&#xff0c;以及在民生产业、中…

51单片机按键复位电路电压随着电容放电升高的分析

一、引言在单片机系统中&#xff0c;复位电路是一个至关重要的组成部分&#xff0c;它确保了单片机在特定情况下能够恢复到初始状态&#xff0c;从而避免程序运行错误或系统崩溃。对于51单片机而言&#xff0c;按键复位电路是一种常用的复位方式&#xff0c;它通过手动按下复位…

JVM学习日记(十五)Day15——性能监控与调优(二)

好了我们这一篇继续来说命令行监控指令&#xff0c;上一篇说了4个比较重要的指令&#xff0c;其中用的比较多的也就是jstat和jmap了。 jhat&#xff1a;堆转储分析工具 他是JDK自带的分析工具&#xff0c;分析我们上一篇说的jmap转存的内存快照&#xff0c;​​内置了一个微型…

Docker国内镜像列表

Docker 镜像源列表&#xff08;8月3日更新-长期&免费&#xff09;_docker国内镜像源-CSDN博客

Orange AI 管理平台单体版安装教程(Docker Compose 部署)

Orange AI 管理平台单体版安装教程&#xff08;Docker Compose 部署&#xff09; 本文介绍如何通过 Docker Compose 快速安装 Orange AI 管理平台单体版&#xff0c;适用于本地开发和测试环境。步骤简单&#xff0c;适合初学者和有一定运维经验的用户。 一、环境准备 已安装 …

PHP的魔术方法

一、介绍 ‌PHP魔术方法是以双下划线__开头的一组特殊方法&#xff0c;用于在对象生命周期、属性访问、方法调用等场景中实现自动化操作。‌简化面向对象编程。 二、17个现有的魔术方法 &#xff08;一&#xff09;、对象生命周期相关 1、__construct() 类的构造函数方法&a…

vue2实现类似chatgpt和deepseek的AI对话流打字机效果,实现多模型同时对话

实现多模型同时对话功能特点&#xff1a;1、抽离对话框成单独组件ChatBox.vue&#xff0c;在新增模型对比窗口时可重复利用2、通过sse与后台实时数据流&#xff0c;通过定时器实现打字效果3、适应深度思考内容输出&#xff0c;可点击展开与闭合4、可配置模型参数&#xff0c;本…

电脑上不了网怎么办?【图文详解】wifi有网络但是电脑连不上网?网络设置

一、问题背景 你有没有遇到过这种情况&#xff1a;电脑右下角的网络图标明明显示连接正常&#xff0c;可打开浏览器就是加载不出网页&#xff0c;聊天软件也刷不出新消息&#xff1f; 这种 "网络已连接但无法上网" 的问题特别常见&#xff0c;既不是没插网线&#xf…

思途Spring学习 0804

SpringBoot 核心概念与开发实践SpringBoot 是一个基于 Spring 框架的快速开发脚手架&#xff0c;通过约定大于配置的原则简化了传统 Spring 应用的初始化配置。其核心目标是整合 Spring 生态&#xff08;如 SSM&#xff09;并支持微服务架构开发。控制反转&#xff08;IoC&…

Hutool工具类:Java开发必备神器

Hutool工具类使用说明Hutool是一个Java工具类库&#xff0c;提供了丰富的功能模块&#xff0c;包括字符串处理、日期时间操作、IO流、加密解密、HTTP客户端等。以下是一些常用模块的具体使用方法。字符串工具&#xff08;StrUtil&#xff09;字符串处理是开发中的常见需求&…

Node.js中Buffer的用法

// Buffer 与字符串的转换示例 // Buffer 是 Node.js 中用于处理二进制数据的类&#xff0c;字符串与 Buffer 之间的转换是常见操作// 1. 从字节数组创建 Buffer 并转换为字符串 // Buffer.from(array) 接收一个字节数值数组&#xff0c;创建对应的 Buffer let buf_4 Buffer.f…

【Java 基础】Java 源代码加密工具有哪些?

👉博主介绍: 博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家,WEB架构师,阿里云专家博主,华为云云享专家,51CTO 专家博主 ⛪️ 个人社区:个人社区 💞 个人主页:个人主页 🙉 专栏地址: ✅ Java 中级 🙉八股文专题:剑指大厂,手撕 J…

es的histogram直方图聚合和terms分组聚合

你提到的这两个 Elasticsearch aggs 聚合语句&#xff1a;第一种&#xff1a;histogram 直方图聚合 "aggs": {"DayDiagram": {"histogram": {"field": "${FiledName}","interval": ${TimeInterval},"extende…

基于Java的AI/机器学习库(Smile、Weka、DeepLearning4J)的实用

基于Java和AI技术处理动漫视频 以下是一些基于Java和AI技术处理动漫视频(如《亚久斗》)的实用案例和实现方法,涵盖视频分析、风格转换、角色识别等方向。每个案例均提供技术思路和关键代码片段。 视频关键帧提取 使用OpenCV提取动漫视频中的关键帧,保存为图片供后续分析…

笔记本电脑联想T14重启后无法识别外置红米屏幕

【原先是可以连接重启后不行】按照以下步骤排查和解决&#xff1a;✅ 1. 基础排查确认连接方式&#xff1a;检查是否使用 USB-C转DP/HDMI线 或 HDMI/DP直连&#xff0c;尝试更换线缆或接口&#xff08;如换另一个USB-C口或HDMI口&#xff09;。测试显示器&#xff1a;将红米显示…

vue+ts 基础面试题 (一 )

目录 1.Vue3 响应式原理 一、 响应式的基本概念 二、 核心机制&#xff1a;Proxy 和依赖追踪 三、 触发更新的过程 四、 代码示例 五、 优势总结 2.如何实现组件间通信&#xff1f; 一、父子组件通信 1. 父传子&#xff1a;Props 传递 2. 子传父&#xff1a;自定义事…

Spring AI实战:SpringBoot项目结合Spring AI开发——提示词(Prompt)技术与工程实战详解

&#x1fa81;&#x1f341; 希望本文能给您带来帮助&#xff0c;如果有任何问题&#xff0c;欢迎批评指正&#xff01;&#x1f405;&#x1f43e;&#x1f341;&#x1f425; 文章目录一、前言二、提示词前置知识2.1 提示词要素2.2 设计提示词的通用技巧2.2.1 从简单开始2.2.…

【后端】Java static 关键字详解

在 Java 中&#xff0c;static 是一个修饰符&#xff0c;用于定义与类相关&#xff08;而非对象实例相关&#xff09;的成员。以下是核心知识点和用法&#xff1a;一、四大用途静态变量&#xff08;类变量&#xff09; 作用&#xff1a;属于类&#xff0c;而非实例。所有实例共…