JavaScript模块化历程(一)

文章目录

      • 1. 模块化概述
      • 2. 模块化演变[^2]
        • 2.1.文件划分模式(`了解`)
        • 2.2.命名空间模式(`了解`)
        • 2.3.IIFE(立即执行函数表达式)和参数依赖声明(`了解`)
      • 3.模块化规范
        • 3.1 CommonJS
          • `Require的基本实现逻辑(重点看)`
        • 3.2 AMD
          • `AMD的基本实现逻辑(重点看)`
        • 3.3 CMD
      • 4.模块化标准规范

1. 模块化概述

  • 提高代码的复用性:模块化可以将代码划分成可重用的部分,降低代码的冗余和重复,提高代码的复用性。

  • 简化代码的维护和调试:当一个软件系统变得越来越复杂时,进行模块化开发可以使得每个模块都相对独立,这样就可以方便地维护和调试每个模块,而不必考虑整个系统的复杂性。

  • 提高代码的可读性:模块化可以使得代码更加结构化,清晰明了,从而提高代码的可读性和可维护性。

  • 提高开发效率:模块化开发可以使得团队成员在不同模块上并行开发,从而提高开发效率。

  • 降低项目的风险:模块化开发可以使得开发人员更加关注模块之间的接口和依赖关系,从而降低项目的风险。
    总之,模块化开发是一种有效的软件开发模式,可以提高软件开发的质量、效率和可维护性,特别是在大型软件系统的开发中,模块化更是必不可少的1

2. 模块化演变2

2.1.文件划分模式(了解)

将每个功能以及它相关的一些状态数据,单独存放到不同的文件当中,我们去约定每一个文件就是一个独立的模块。
我们去使用这个模块,就是将这个模块引入到页面当中,然后直接调用模块中的成员(变量 / 函数)。
一个script标签就对应一个模块,所有模块都在全局范围内工作

缺点:

  • 污染全局作用域
  • 命名冲突问题
  • 无法管理模块依赖关系

示例代码:

// Student.js
var maths = 80;
var chinese = 90;
function score() {return maths + chinese;
}
// Cook.js
var name = "煮饭";
function make_food() {return new Promise(function (resolve, reject) {setTimeout(() => {resolve("煮好饭了")}, 1000);})
}
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>文件划分模式</title>
</head>
<body><script src="Cook.js"></script><script src="Student.js"></script><script>var studentScore = score();console.log(studentScore );make_food().then((message) => console.log(message));</script></body>
</html>

2.2.命名空间模式(了解)

我们约定每个模块只暴露一个全局的对象,我们所有的模块成员都挂载到这个全局对象下面。
在第一阶段的基础上,通过将每个模块「包裹」为一个全局对象的形式实现,
有点类似于为模块内的成员添加了「命名空间」的感觉。

  • 没有私有空间
  • 模块成员仍然可以在外部被访问/修改
  • 无法管理模块依赖关系

迪米特法则:又叫作最少知识原则(The Least Knowledge Principle),一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和陌生人说话。英文简写为: LOD

// Student.js
var Student = {maths: 80,chinese: 90,score: function () {return this.maths + this.chinese;},skills: [Cook]
}
// Cook.js
var Cook = {name: "煮饭",make_food: function () {return new Promise(function (resolve, reject) {setTimeout(() => {resolve("煮好饭了")}, 1000);})}
}
<!-- index.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>命名空间划分模式</title>
</head><body><!-- 在这里因为有引用,顺序不能乱 --><script src="Cook.js"></script><script src="Student.js"></script><script>// 我们可以用命名空间来访问属性,成员var studentScore = Student.score();console.log(studentScore);Student.skills[0].make_food().then((message) => console.log(message));</script></body></html>

2.3.IIFE(立即执行函数表达式)和参数依赖声明(了解)

使用立即执行函数的方式,去为我们的模块提供私有空间。将模块中每个成员都放在一个函数提供的私有作用域当中,
对于需要暴露给外部的成员,我们可以通过return的方式实现。确保了私有成员的安全。
有了私有成员的概念,私有成员只能在模块成员内通过闭包的形式访问。
利用立即执行函数的参数传递模块依赖项

立即执行函数 : (function() {})()

// Student.js
; let Student = (function (modules) {var maths = 80;var chinese = 90;var skills = [modules['cook']];score = function () {return maths + chinese;}return {score,skills}
})({ cook: Cook }); // 模块的传入
// Cook.js
// 为什么加 ; 
// 怕别的导入的库后面没有加 ";"
; let Cook = (function () {var name = "煮饭";make_food = function () {return new Promise(function (resolve, reject) {setTimeout(() => {resolve("煮好饭了")}, 1000);})}return {make_food: make_food}
})();
<!-- index.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>立即执行函数</title>
</head><body><script src="Cook.js"></script><script src="Student.js"></script><script>// 我们可以用命名空间来访问属性,成员var studentScore = Student.score();console.log(studentScore);Student.skills[0].make_food().then((message) => console.log(message));</script></body></html>

3.模块化规范

3.1 CommonJS

一个文件就是一个模块
每个模块都有单独的作用域
通过module.exports导出成员
通过require函数载入模块

缺点:

  • CommonJS的出现本来是为了解决Node.js问题,约定的是以同步模式加载模块,在浏览器端使用会导致效率低下
// Student.js
let cook = require("./Cook")var maths = 80;
var chinese = 90;
var skills = [cook];
function score() {return maths + chinese;
}module.exports = {score,skills
}
var name = "煮饭";
function make_food() {return new Promise(function (resolve, reject) {setTimeout(() => {resolve("煮好饭了")}, 1000);})
}
module.exports = { make_food }
/*** 一个文件就是一个模块* 每个模块都有单独的作用域* 通过module.exports导出成员* 通过require函数载入模块* CommonJS约定的是以同步模式加载模块,在浏览器端使用会导致效率低下。*/
let student = require('./Student')
var studentScore = student.score();
student.skills[0].make_food().then((message) => console.log(message));
console.log(studentScore)
Require的基本实现逻辑(重点看)
/*** require的基本实现原理* webpack打包原理*/(function () {var modules = {"./Cook.js": function (module, require) {var name = "煮饭";function make_food() {return new Promise(function (resolve, reject) {setTimeout(() => {resolve("煮好饭了")}, 1000);})}module.exports = { make_food }},"./Student.js": function (module, require) {let cook = require("./Cook.js")var maths = 80;var chinese = 90;var skills = [cook];function score() {return maths + chinese;}module.exports = {score,skills}},"./main.js": function (module, require) {let student = require('./Student.js')var studentScore = student.score();student.skills[0].make_food().then((message) => console.log(message));console.log(studentScore)}}// 为什么多次require只执行一次 因为有缓存// 为什么多次require只执行一次 因为有缓存var caches = {};// 基本实现逻辑// 基本实现逻辑function require(moduleId) {if (caches[moduleId]) {return caches[moduleId];}var module = { exports: {} }//需要把模块运行一下modules[moduleId](module, require);caches[moduleId] = module.exports;return module.exports;}require("./main.js")
})();

3.2 AMD

这个规范的出现主要是为了解决在浏览器里面,因为网络慢,加载JS卡顿的问题
作为一个规范,只需定义其语法API,而不关心其实现。AMD规范简单到只有一个API,即define函数:
define([module-name?], [array-of-dependencies?], [module-factory-or-object]);

  • module-name: 模块标识,可以省略。
  • array-of-dependencies: 所依赖的模块,可以省略。
  • module-factory-or-object: 模块的实现,或者一个JavaScript对象。
AMD的基本实现逻辑(重点看)
// 缓存 为什么多个require只会执行一次
var modules = {};// 代码实现页面可能有需要依赖的其他js
// 所以里面也用到了loadScript
function define(name, deps, factory) {var pending = deps.length;// 创建一个数组 来看看依赖是否全部加载完成var resolvedDeps = new Array(pending);deps.forEach(function (dep, index) {if (modules[dep]) {resolvedDeps[index] = modules[dep];pending--;} else {loadScript(dep + ".js", function () {resolvedDeps[index] = modules[dep];pending--;// 这里面是异步执行了的 所以在这里// 在if外面都要做一个加载完成的判断if (pending === 0) {modules[name] = factory.apply(null, resolvedDeps);}})}if (pending === 0) {modules[name] = factory.apply(null, resolvedDeps);}});
}// 引用的时候也有可能需要依赖其他的js
// 所以在这个里面也用到了 loadScript
function require(deps, callback) {var pending = deps.length;var resolvedDeps = new Array(pending);deps.forEach(function (dep, index) {if (modules[dep]) {resolvedDeps[index] = modules[dep];pending--;} else {loadScript(dep + ".js", function () {resolvedDeps[index] = modules[dep];pending--;// 这里面是异步执行了的 所以在这里// 在if外面都要做一个加载完成的判断if (pending === 0) {callback.apply(null, resolvedDeps);}})}if (pending === 0) {callback.apply(null, resolvedDeps);}})
}// 动态加载script的话,并不会让document的渲染进行卡顿
function loadScript(url, callback) {var script = document.createElement("script");script.src = url;// 加载的过程实际就是 执行 define 函数的过程script.onload = callback || function () { };document.head.appendChild(script);
}require(["student"], function (student) {var studentScore = student.score();console.log(studentScore)
})
//student.js
// AMD的实现
define("student", [], function () {var maths = 80;var chinese = 90;function score() {return maths + chinese;}return { score }
});

3.3 CMD

实现逻辑与AMD基本类似,就不写了

4.模块化标准规范

ToDO


  1. 什么是模块化?为什么要进行模块化开发? ↩︎

  2. 细说前端模块化开发 ↩︎

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

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

相关文章

Lua之Lua源文件批量转换为luac字节码文件

准备的工具&#xff1a;luac.exe CSDNhttps://mp.csdn.net/mp_download/manage/download/UpDetailed Unity版: using System; using System.Collections; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine;public static class Bat…

RabbitMQ手动签收消息

RabbitMQ手动签收消息 这里讲解SpringBoot使用RabbitMQ进行有回调的用法和消费者端手动签收消息的用法。 1、pom依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"h…

(学习笔记-调度算法)进程调度算法

进程调度算法也称 CPU 调度算法&#xff0c;毕竟进程是由 CPU 调度的。 当 CPU 空闲时&#xff0c;操作系统就选择内存中标的某个 [就绪状态] 的进程&#xff0c;将其分配给 CPU。 什么时候会发生CPU调度呢&#xff1f;通常有以下情况&#xff1a; 当进程从运行状态转换到等待…

BATPowerShell实现本地文件自动上传FTP服务器

运维工作中&#xff0c;经常需要一些脚本来实现自动化&#xff0c;今天分享本地文件自动上传FTP的两种解决办法&#xff1a; 一、使用BAT自动上传FTP 使用批处理&#xff08;BAT&#xff09;命令文件将本地文件夹内容上传到FTP服务器需要使用Windows自带的命令行工具&#xf…

【卷积神经网络】经典网络之 LeNet-5, AlexNet 与 VGG-16

随着计算机硬件的升级与性能的提高&#xff0c;运算量已不再是阻碍深度学习发展的难题。卷积神经网络&#xff08;Convolution Neural Network&#xff0c;CNN&#xff09;是深度学习中一项代表性的工作&#xff0c;其雏形是 1998 年 LeCun 提出的 LeNet-5 模型。如今&#xff…

WPF使用依赖注入

现在依赖注入在.Net里面已经普及&#xff0c;自己常写一些简单的demo倒是无所谓&#xff0c;但偶尔写一点正式的工程&#xff0c;也免不了要使用一下&#xff0c;于是总结了一下在WPF里面使用依赖注入。 在写简单Demo时候&#xff0c;通常是在MainWindow的构造函数里面直接做初…

Python爬虫——scrapy_日志信息以及日志级别

日志级别&#xff08;由高到低&#xff09; CRITICAL&#xff1a; 严重错误 ERROR&#xff1a; 一般错误 WARNING&#xff1a; 警告 INFO&#xff1a; 一般警告 DEBUG&#xff1a; 调试信息 默认的日志等级是DEBUG 只要出现了DEBUG或者DEBUG以上等级的日志&#xff0c;那么这些…

[oneAPI] 基于BERT预训练模型的SQuAD问答任务

[oneAPI] 基于BERT预训练模型的SQuAD问答任务 Intel Optimization for PyTorch and Intel DevCloud for oneAPI基于BERT预训练模型的SQuAD问答任务语料介绍数据下载构建 模型 结果参考资料 比赛&#xff1a;https://marketing.csdn.net/p/f3e44fbfe46c465f4d9d6c23e38e0517 Int…

第2章 数据结构和算法概述

2.3线性结构和非线性结构 数据结构包括: 线性结构和非线性结构 2.3.1线性结构 线性结构作为最常用的数据结构&#xff0c;其特点是数据元素之间存在一对一的线性关系线性结构有两种不同的存储结构&#xff0c;即顺序存储结构(数组)和链式存储结构(链表)。顺序存储的线性表称…

回归预测 | MATLAB实现GAM广义加性模型多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现GAM广义加性模型多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现GAM广义加性模型多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本…

【24择校指南】华东师范大学计算机考研考情分析

华东师范大学(B) 考研难度&#xff08;☆☆☆☆&#xff09; 内容&#xff1a;23考情概况&#xff08;拟录取和复试分数人数统计&#xff09;、院校概况、23考试科目、23复试详情、各科目及专业考情分析。 正文2563字&#xff0c;预计阅读&#xff1a;3分钟。 2023考情概况…

机器学习深度学习——NLP实战(自然语言推断——注意力机制实现)

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——NLP实战&#xff08;自然语言推断——数据集&#xff09; &#x1f4da;订阅专栏&#xff1a;机器学习&…

Kotlin Channel 热流

协程&#xff1a;Channel 热流 1、Channel是什么&#xff1f; 生产者&#xff1a;多个协程消费者&#xff1a;多个协程中间&#xff1a;Channel 管道 并发安全队列发送send接收recv 协程间通信 1、Channel可以用于协程间通信 // 通道Channelval channel Channel<Int>…

C# 读取pcd、ply点云文件数据

最近研究了下用pcl读取点云数据&#xff0c;又做了个C#的dll&#xff0c;方便读取&#xff0c;同样这个dll基于pcl 最新版本1.13.1版本开发。 上次做的需要先得到点云长度&#xff0c;再获取数据。这次这个定义了一个PointCloudXYZ类来存数据。将下面的dll拷贝到可执行目录下&a…

Docker详解

文章目录 Docker详解一、Docker简介什么是容器 &#xff1f;容器技术有哪些优点 &#xff1f;什么是Docker &#xff1f;Docker的特点Docker的使用场景 二、Docker的基本组成Docker 客户端 / 守护进程Docker Image 镜像Docker Container 容器Docker Registry 仓库 三、Docker 依…

嵌入式系统中如何选择RTC电池?

RTC&#xff08;Real Time Clock&#xff09;是一种用于提供系统时间的独立定时器&#xff0c;它可以在系统断电或低功耗模式下继续运行&#xff0c;只需要一个后备电池作为供电源。在嵌入式系统中&#xff0c;选择合适的RTC电池时非常关键的&#xff0c;它会影响系统时间的准确…

二、SQL注入之联合查询

文章目录 1、SQL注入原理2、SQL注入的原因3、SQL注入的危害4、SQL注入基础4.1 MySQL相关4.2 SQL注入流程&#xff1a; 5、联合注入实例基本步骤6、总结 1、SQL注入原理 SQL注入(Sql Injection&#xff09;就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串&…

【从零学习python 】56. 异常处理在程序设计中的重要性与应用

文章目录 异常的概念读取文件异常try...except语句try...else语句try...finally语句 进阶案例 异常的概念 在程序运行过程中&#xff0c;由于编码不规范或其他客观原因&#xff0c;可能会导致程序无法继续运行&#xff0c;此时就会出现异常。如果不对异常进行处理&#xff0c;…

[C++] string类常用接口的模拟实现

文章目录 1、前言2、遍历2.1 operator[ ]下标方式2.2 迭代器2.3 范围for2.4 c_str 3、容量相关3.1 size&#xff08;大小&#xff09;3.2 capacity&#xff08;容量&#xff09;3.3 empty&#xff08;判空&#xff09;3.4 clear&#xff08;清理&#xff09;3.5 reserve3.6 res…

代码随想录算法训练营day37 | LeetCode 738. 单调递增的数字 968. 监控二叉树

738. 单调递增的数字&#xff08;题目链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台&#xff09; 思路&#xff1a;从后往前考虑&#xff0c;题目会变得很容易&#xff0c;从前往后考虑&#xff0c;结果很难处理。 int monotoneIn…