当前位置: 首页 > news >正文

带你深度了解作用域和闭包

了解作用域

一共有三种,函数作用域,全局作用域和块作用域(ES6的let,const会创建)

什么是块作用域呢?

使用{}包裹,就是一个块作用域,比如if条件判断。

函数和模块两者的不同之处呢?

都有各自的作用域,一个是函数内,一个是模块内,变量只在自己内部被访问。

外部访问不同,函数作用域不可以外部访问,模块可以导出让外部访问。

运行环境不同,函数作用域适用于所有JS运行环境,模块适用于ES module环境或者<script type=’module’>

这里我们可以了解一下模块

为什么要有模块?

早期js,所有变量和函数都在全局作用域里面共享,很容易造成变量名冲突,代码混乱。

所以ES6引入了模块系统ES Module,每个文件都是独立的模块作用域,可以导入导出内容,浏览器和nodejs环境都支持,在浏览器使用js注意,script添加type=‘module’,浏览器就知道这是一个模块。

不同的模块系统
1.ES Module现在最常用的方式

使用import和export 自动严格模式
最高级this是undefined 只会执行一次,缓存结果。
静态加载,编译的时候就确定依赖关系

拓展
有默认导出export default只能导出一个和具名导出export导出多个
重命名模块,可以在导入的时候使用as重命名模块
可以创建模块对象,通过exports * as module from ‘index.js’ 作为module对象成员使用。这样可以不用导出很多个。
可以合并模块,就是如果有很多不同的模块,都导出一个模块,那么我们可以创建一个文件夹用来进行所有模块的导入。

export { Square } from "./shapes/square.js";
export { Triangle } from "./shapes/triangle.js";
export { Circle } from "./shapes/circle.js";

导入声明有提升,也就是导入的值在模块声明前可以使用,但最好还是写在最上面,更容易分析依赖。
循环导入可能存在问题,比如A,导入B,B依赖A
编写同构模块,因为编写的模块代码不一定在每个环境中都可以运行(window在nodejs中没有),为了提高模块的可重用性,可以使用重构,即每个运行中表现相同。
比如在使用之前检测特定的全局变量是否存在。浏览器环境是全局对象是window,nodejs环境全局对象是global,process也是全局对象。process.env获取当前的环境变量,比如 process.env.NODE_ENV

// myModule.js
let password;
if (typeof process !== "undefined") {// 我们在 Node.js 中运行;从 `process.env` 中读取password = process.env.PASSWORD;
} else if (typeof window !== "undefined") {// 我们在浏览器中运行;从输入框中读取password = document.getElementById("password").value;
}

常见的故障
.mjs 后缀的文件需要以 text/javascript MIME 类型来加载,否则,你会得到严格的 MIME 类型检查错误
如果你尝试用本地文件加载 HTML 文件(即,具有 file:// 的 URL),由于 JavaScript 模块的安全性要求,你会遇到 CORS 错误。你需要通过服务器来做你的测试。

2.CommonJS nodejs早期用法

使用require和module.exports
运行时加载,执行阶段读取。
同步执行,不适用于浏览器环境,只在nodejs中使用

main.js
const a=1
function b (){return 1+1
}
module.exports ={a,b}
引入使用
const {a,b}=require('./main.js')
3.AMD浏览器早期异步模块加载方式

使用define和require
为了解决浏览器中同步加载模块卡顿的问题
在前端打包工具普及之前很常见

静态加载VS运行时加载

静态加载 在代码编译阶段也就是执行前,知道需要哪些模块变量和函数,所以加载速度快,能做语法检测和优化
运行时加载 在代码运行时,加载到require时候才读取加载模块,灵活,但加载效率低,无法提前优化。

动态加载模块什么情况?

仅在需要的时候动态加载模块,而不是预先加载所有模块

import("/modules/mymodule.js").then((module) => {// 使用模块做一些事情。
});

返回一个promise,可以访问导出的模块对象。

什么是MIME?

模块引入的时候要确认文件类型,就可以通过MIME

MIME就是告诉浏览器或者服务器,一个文件的类型是什么。

text/javascript,现代标准,推荐使用 application/javascript,曾经被推荐,现在依然可用 一些旧规范或旧项目 module ,<script type="module"> 中的属性值,表明脚本是 ESM 模块,ES Module加载 text/plain 表示纯文本

了解闭包

闭包的理解就是,函数和函数周围状态的引用组合而成。

具体一点就是,一个函数,里面嵌套了一个函数,内部函数引用外部函数的变量,外部函数在外面被调用,这就形成了一个闭包。

作用: 1.封装私有化变量。 2.维持状态(比如函数实现一个累加功能,闭包的话,每次调用这个函数变量会一直累加,而不是重新创建一个新的变量。

function add (){let count=0return function (){count ++console.log(count)}
}
let t=add()
t() //1
t() //2

缺点: 1.内存占用增加,因为被引用的变量不会销毁,我们可以把变量不再使用时置为 null 2.意外引用错误变量,for 循环中用 var 定义变量,改用 let,或创建立即执行函数

for (var i = 0; i < 3; i++) {setTimeout(() => {console.log(i);}, 1000);
}
//实际输出 2 2 2
循环嵌套定时器,为什么会出现都是2呢?

第一点是var声明的变量是函数作用域,整个for循环使用同一个i 第二点是settimeout是异步的,在for完成之后执行,所以都是2

上面问题的解决办法是什么呢?

要么把var变为let,要么创建立即执行函数

for (var i = 0; i < 3; i++) {(function(i) {setTimeout(() => {console.log(i);}, 1000);})(i);
}
i的作用是什么呢?

函数立刻执行创建独立作用域,传入当前的i值

http://www.dtcms.com/a/523599.html

相关文章:

  • 【Mac下通过Brew安装Ollama 】部署 DeepSeek 轻量模型(实测版)
  • 微信网站用什么语言开发wordpress4.9.4 安装
  • 如何在百度提交自己的网站简要列举网站常见类型
  • 机器视觉HALCON:5.图像标定
  • 【跟小嘉学习JavaWeb开发】第三章 从数据类型说起
  • CTF WEB入门 爆破篇
  • NAT网络地址转换
  • 【自然语言处理】预训练01:词嵌入(word2vec)
  • 利用inscode帮我用前端页面展示分析博客数据
  • 「赤兔」Chitu 框架深度解读(十):任务调度与并发控制策略
  • Java CompletableFuture 详解与实战:让异步编程更优雅
  • 建设外贸网站要多少钱建设局办的焊工证全国通用吗
  • Linux_基础IO(2)
  • Docker 中使用Nginx 一个端口启动多个前端项目
  • S9 顺序队列
  • 函数绑定器 std::bind
  • STM32基本定时器
  • 第9部分-性能优化、调试与并发设计模式
  • 编程素养提升之EffectivePython(Builder篇)
  • Vue 3 + TypeScript 项目性能优化全链路实战:从 2.1MB 到 130KB 的蜕变
  • 网站首页图腾讯 云上做网站教程
  • Ubuntu(Linux)安装更好用的中文输入法
  • 《算法闯关指南:优选算法--二分查找》--23.寻找旋转排序数组中的最小值,24.点名
  • 【ssh密钥】--- 当密钥密码遇见 Git 服务器:一场关于 “信任” 的浪漫喜剧
  • kotlin 数据类的get和set 问题
  • 爱站网功能左旗网站建设
  • 中国企业跨境云组网指南:低延迟访问德国AWS云做数据分析的实操方案
  • 从单机阅读到云端协作:KoodoReader+cpolar构建知识管理新范式
  • 设计模式之:命令模式
  • EulerOS(NPU)安装llamafactory