CommonJS

CommonJS 是由 JavaScript 社区于 2oo9 年提出的包含模块、文件、IO、控制台在内的一系列标准。Node.js 的实现中采用了 CommonJS 标准的一部分,并在其基础上进行了一些调整。我们所说的 CommonJS 模块和 Node.js 中的实现并不完全一样,现在一般谈到 CommonJS 其实是 Node.js 中的版本,而非它的原始定义。

CommonJS 最初只为服务端而设计,直到有了 Browserify ——一个运行在 Node.js 环境下的模块打包工具,它可以将 CommonJS 模块打包为浏览器可以运行的单个文件。这意味着客户端的代码也可以遵循 CommonJS 标准来编写了。

不仅如此,借 Node.js 的包管理器,npm 开发者还可以获取他人的代码库,或者把自己的代码发布上去供他人使用。这种可共享的传播方式使 CommonJS 在前端开发领域逐渐流行起来。

模块

通过 script 标签插入的 JS 文件的顶层作用域是全局作用域,在进行变量及函数声明时会污染全局环境:而通过 CommonJS 引入的 JS 文件会形成一个属于模块自身的作用域,所有的变量及函数只有自己能访问,对外是不可见的。请看下面的例子:

//calculator.js
var name = 'calculator.js';
//index.js
var name = 'index.js';
require('./calculator.js');
console.log(name); // index.js

这里有两个文件,在 index.js 中我们通过 CommonJS 的 require 函数加载 calculator.js。运行之后控制台结果是 “index.js”,说明 calculator.js 中的变量声明并不会影响 index.js,可见每个模块是拥有各自的作用域的。

导出

导出是一个模块向外暴露自身的唯一方式。在 CommonJS 中,通过 module.exports 可以导出模块中的内容,如:

module.exports = {
    name: 'calculater'
    add: function(a, b){
        return a + b;
    }
}

CommonJS 模块内部会用一个 module 对象存放当前模块的信息,可以理解成在每个模块的最开始定义了以下对象:

var module = {//...};
// 模块自身逻辑
module.exports = {//...};

module.exports 用来指定该模块要对外暴露哪些内容,为了书写方便,CommonJS 也支持另种简化的导出方式——直接使用 exports:

exports.name = 'calculater';
exports.add = function(a,b){
	return a + b;
}

其内在机制是将 exports 指向 module.exports,而 module.exports 在初始化时是一个空对象。我们可以简单地理解为,CommonJS 在每个模块的首部默认添加了以下代码:

var module = {
	exports: {}
};
var exports = module.exports;

但不能直接给 exports 赋值,否则会导致其失效。还有禁止将 module.exports 和 exports 混用。

导入

在 CommonJS 中使用 require 语法进行模块导入。如:

// calculator.js
module.exports = {	
    add: function(a, b){
        return a + b;
    }
}
// index.js
const calculator = require('./calculator.js');
const sum = calculator.add(2, 3);
console.log(sum); // 5

当我们使用 require 导入一个模块时会有两种情况:

  • 该模块未曾被加载过。这时会首先执行该模块,然后获取到该模块最终导出的内容。
  • 该模块已经被加我过。这时该模块的代码不会再次执行,而是直接获取该模块上一次导出的内容。

我们前面提到,模块会有一个 module 为象用来存放其信息,这个对象中有一个属性 loaded 用于记录该模块是否被加载过。loaded 的值默认为 false ,在模块第一次被加载和执行过后会置为true,后面再次加载时检查到 module.loadedtrue ,则不会再次执行模块代码。

有时我们加载一个模块,不需要获取其导出的内容,只是想要通过执行它而产生某种作用,比如把它的接口挂在全局对象上,此时直接使用 require 即可。

require('./task.js');

模块封装器

在执行模块代码之前,Node.js 将使用如下所示的函数封装器对其进行封装:

(function(exports, require, module, __filename, __dirname) {
    // Module code actually lives in here
}); 

通过这样做,Node.js 实现了以下几点:

  • 它将顶层变量(使用 varconstlet 定义)保持在模块而不是全局对象的范围内。
  • 它有助于提供一些实际特定于模块的全局变量,例如:
    • moduleexports 对象,实现者可以用来从模块中导出值。
    • 便利变量 __filename__dirname,包含模块的绝对文件名和目录路径。

模块作用域

__dirname

当前模块的目录名。这与 __filenamepath.dirname() 相同。

__filename

当前模块的文件名。这是当前模块文件的已解析符号链接的绝对路径。对于主程序,这不一定与命令行中使用的文件名相同。

在这里插入图片描述

exports

module.exports 的引用,其输入更短。

在这里插入图片描述

直接给 module.exportsexports 赋值都会断开引用关系。

在这里插入图片描述

在这里插入图片描述

直接赋值会导致以下后果:

在这里插入图片描述

moudle

当前模块的引用,请参阅关于 module 对象的部分。特别是,module.exports 用于定义模块导出的内容,并通过 require() 使其可用。

require(id: string)

  • id —— 模块名称或路径
  • 返回值—— 模块导出的内容

用于导入模块、JSON 和本地文件。模块可以从 node_modules 导入。可以使用相对路径(例如 ././foo./bar/baz../foo)导入本地模块和 JSON 文件,该路径将根据 __dirname(如果有定义)命名的目录或当前工作目录进行解析。

require.cache

  • 第一次加载某个模块时,Node 会缓存该模块。以后再加载该模块,就直接从缓存取出该模块的 module.exports 属性(不会再次执行该模块)。
  • 如果需要多次执行模块中的代码,一般可以让模块暴露行为(函数)。
  • 模块的缓存可以通过 require.cache 拿到,也可以从此对象中删除键值,下一次 require 将重新加载模块。

在这里插入图片描述

require.main

Module 对象代表 Node.js 进程启动时加载的入口脚本,如果程序的入口点不是 CommonJS 模块,则为 undefined。参见访问主模块。

在这里插入图片描述

require.resolve(request[, options])

  • request —— 要解析的模块路径。
  • options
    • paths 从中解析模块位置的路径。如果存在,将使用这些路径而不是默认解析路径,但 GLOBAL_FOLDERS$HOME/.node_modules 除外,它们始终包含在内。这些路径中的每一个都用作模块解析算法的起点,这意味着从此位置检查 node_modules 层级。

在这里插入图片描述

require.resolve.paths(request)
  • request—— 正在检索其查找路径的模块路径。
  • 返回一个数组,其中包含在解析请求期间搜索的路径,如果请求字符串引用核心模块,例如 httpfs,则返回 null

在这里插入图片描述

moudle 对象

在每个模块中,module 自由变量是对代表当前模块的对象的引用。module 实际上不是全局的,而是每个模块本地的。

在这里插入图片描述

module.children

此模块首次需要的模块对象。

在这里插入图片描述

module.exports

从模块中导出任何你想导出的东西。
在这里插入图片描述

赋值给 module.exports 必须立即完成。不能在任何回调中完成。以下不起作用:

在这里插入图片描述

exports

参考 模块作用域->exports

module.filename

模块的完全解析文件名。

在这里插入图片描述

module.id

模块的标识符。通常这是完全解析的文件名。

在这里插入图片描述

module.isPreloading

如果模块在 Node.js 预加载阶段运行,则为 true

在这里插入图片描述

module.loaded

模块是否已完成加载,或正在加载。

在这里插入图片描述

module.parent

第一个需要此模块的模块,如果当前模块是当前进程的入口点,则为 null;如果该模块是由非 CommonJS 模块加载的模块(例如:REPL 或 import),则为 undefined

在这里插入图片描述

module.path

模块的目录名称。这通常与 module.idpath.dirname() 相同。

在这里插入图片描述

module.paths

模块的搜索路径。

在这里插入图片描述

module.require(id)

  • id —— 模块名称或路径
  • 返回值—— 模块导出的内容

module.require() 方法提供了一种加载模块的方法,就像从原始模块调用 require() 一样。为了做到这一点,有必要获取对模块对象的引用。由于 require() 返回 module.exports,并且该模块通常仅在特定模块的代码中可用,因此必须显式导出才能使用。

在这里插入图片描述

在这里插入图片描述

基本上,用 module.requrie 是极少见的,借用它家的 module 更为少见。

module.builtinModules

Node.js 提供的所有模块的名称列表。可用于验证模块是否由第三方维护。

注意:列表不包含像 node:test 这样的仅前缀模块。

此上下文中的 module 与模块封装器提供的对象不同。要访问它,需要 Module 模块:

// import { builtinModules } from 'node:module' // es模块
const builtin = require('node:module').builtinModules;

在这里插入图片描述

module.createRequire(filename)

  • filename —— 用于构造 require 函数的文件名。必须是文件网址对象、文件网址字符串、或绝对路径字符串。
  • 返回 —— require 函数。

在这里插入图片描述

module.isBuiltin(moduleName)

  • moduleName —— 模块名称。
  • 返回:—— 如果模块是内置的,则返回 true,否则返回 false

在这里插入图片描述

module.register(specifier[, parentURL] [, options])

  • specifier: <string>|<URL> —— 需要注册的定制钩子;这应该与传递给 import() 的字符串相同,但如果它是相对的,则它是相对于 parentURL 解析的。
  • parentURL: <string>|<URL> —— 如果你想要相对于基本 URL(例如 import.meta.url)解析 specifier,你可以在此处传递该 URL。默认值:'data:'
  • options: <Object>
    • parentURL: <string>|<URL> —— 如果你想要相对于基本 URL(例如 import.meta.url)解析 specifier,你可以在此处传递该 URL。如果 parentURL 作为第二个参数提供,则忽略此属性。默认值:'data:'
    • data: <any> —— 传递到 initialize 钩子的任何任意的、可克隆的 JavaScript 值。
    • transferList: <Object[]> —— 可转换对象 要传递到 initialize 钩子中。

注册一个导出钩子的模块,用于自定义 Node.js 模块解析和加载行为。参见定制钩子

module.syncBuiltinESMExports()

module.syncBuiltinESMExports() 方法,用于更新所有内置 ES 模块的实时绑定,以匹配 CommonJS 模块的exports属性。这个方法的主要目的是为了协调 CommonJS 和 ES6 模块之间的交互,确保在使用混合模块系统时,数据的同步和一致性。

此外,这个方法还涉及到对内置模块的修改和同步。虽然内置模块在运行时可以被修改,但不能新增或删除。通过module.syncBuiltinESMExports(),可以确保这些修改被正确地同步到整个模块系统中,保持数据的一致性和完整性。

在这里插入图片描述

确定模块系统

当传递给 node 作为初始输入时,或者当被 import 语句或 import() 表达式引用时,Node.js 会将以下内容视为ES 模块

  • 扩展名为 .mjs 的文件。
  • 当最近的父 package.json 文件包含值为 "module" 的顶层 "type" 字段时,扩展名为 .js 的文件。
  • 字符串作为参数传入 --eval,或通过 STDIN 管道传输到 node,带有标志 --input-type=module
  • 使用 --experimental-detect-module 时,包含语法的代码仅成功解析为ES 模块,例如 importexport 语句或 import.meta,没有明确标记应如何解释它。显式标记是 .mjs.cjs 扩展、带有 "module""commonjs" 值的 package.json "type" 字段,或者 --input-type--experimental-default-type 标志。CommonJS 或 ES 模块都支持动态 import() 表达式,并且不会导致文件被视为 ES 模块。

当传递给 node 作为初始输入时,或者当被 import 语句或 import() 表达式引用时,Node.js 会将以下内容视为CommonJS

  • 扩展名为 .cjs 的文件。
  • 当最近的父 package.json 文件包含值为 "commonjs" 的顶层字段 "type" 时,则扩展名为 .js 的文件。
  • 字符串作为参数传入 --eval--print,或通过 STDIN 管道传输到 node,带有标志 --input-type=commonjs

除了这些明确的情况之外,还有其他情况,Node.js 根据 --experimental-default-type 标志的值默认使用一个模块系统或另一个模块系统:

  • 如果同一文件夹或任何父文件夹中不存在 package.json 文件,则以 .js 结尾或没有扩展名的文件。
  • 如果最近的父 package.json 字段缺少 "type" 字段,则以 .js 结尾或没有扩展名的文件;除非该文件夹位于 node_modules 文件夹内。(当 package.json 文件缺少 "type" 字段时,无论 --experimental-default-type 如何,为了向后兼容,node_modules 下的包范围始终被视为 CommonJS。)
  • 当未指定 --input-type 时,字符串作为参数传递给 --eval 或通过 STDIN 通过管道传递给 node

该标志当前默认为 "commonjs",但将来可能会更改为默认为 "module"。因此,最好尽可能明确;特别是,包作者应始终在其 package.json 文件中包含 "type" 字段,即使在所有源都是 CommonJS 的包中也是如此。如果 Node.js 的默认类型发生变化,显式说明包的 type 将使包面向未来,它还将使构建工具和加载器更容易确定应如何解释包中的文件。

启用

Node.js 有两个模块系统:CommonJS 模块和 ECMAScript 模块

默认情况下,Node.js 会将以下内容视为 CommonJS 模块:

  • 扩展名为 .cjs 的文件;
  • 当最近的父 package.json 文件包含值为 "commonjs" 的顶层字段 "type" 时,则扩展名为 .js 的文件。
  • 当最近的父 package.json 文件不包含顶层字段 "type" 或任何父文件夹中都没有 package.json 时,具有 .js 扩展名或不带扩展名的文件;除非该文件包含错误的语法,除非它被评估为 ES 模块。包作者应该包括 "type" 字段,即使在所有源都是 CommonJS 的包中也是如此。明确包的 type 将使构建工具和加载器更容易确定包中的文件应该如何解释。
  • 扩展名不是 .mjs.cjs.json.node.js 的文件(当最近的父 package.json 文件包含值为 "module" 的顶层字段 "type" 时,这些文件将被识别为 CommonJS 模块只有当它们是通过 require() 包含,而不是用作程序的命令行入口点时)。

有关详细信息,请参阅确定模块系统

调用 require() 始终使用 CommonJS 模块加载器。调用 import() 始终使用 ECMAScript 模块加载器。

访问主模块

当文件直接从 Node.js 运行时,则 require.main 被设置为其 module。这意味着可以通过测试 require.main === module 来确定文件是否被直接运行。

对于文件 foo.js,如果通过 node foo.js 运行,则为 true,如果通过 require('./foo') 运行,则为 false

当入口点不是 CommonJS 模块时,则 require.mainundefined,且主模块不可达。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AP单类平均准确率

P_true N_true P_pred TP Fp N_pred FN TNP NTP&#xff08;真正样本&#xff0c;与真实框IoU大于阈值的框&#xff09; FP&#xff08;假正样本&#xff0c;与真实框IoU小于阈值的框&#xff09; TN&#xff08;真负样本&#xff0c;背景&#xff09;…

MQTT实战之在vue和java中使用

在VUE中使用MQTT 1、创建vue项目&#xff08;使用node版本为20.12.0&#xff09; >>npm create vitelatest Need to install the following packages: create-vite6.1.1 Ok to proceed? (y) y √ Project name: ... mqtt-vue √ Select a framework: Vue √ Select a v…

oracle: 表分区>>范围分区,列表分区,散列分区/哈希分区,间隔分区,参考分区,组合分区,子分区/复合分区/组合分区

分区表 是将一个逻辑上的大表按照特定的规则划分为多个物理上的子表&#xff0c;这些子表称为分区。 分区可以基于不同的维度&#xff0c;如时间、数值范围、字符串值等&#xff0c;将数据分散存储在不同的分区 中&#xff0c;以提高数据管理的效率和查询性能&#xff0c;同时…

数据分析系列--⑥RapidMiner构建决策树(泰坦尼克号案例含数据)

一、资源下载 二、数据处理 1.导入数据 2.数据预处理 三、构建模型 1.构建决策树 2.划分训练集和测试集 3.应用模型 4.结果分析 一、资源下载 点击下载数据集 二、数据处理 1.导入数据 2.数据预处理 三、构建模型 1.构建决策树 虽然决策树已经构建,但对于大多数初学者或…

Unity游戏(Assault空对地打击)开发(4) 碰撞体和刚体的添加

前言 飞机和世界的大小关系不太对&#xff0c;我稍微缩小了一下飞机。 详细步骤 选中所有地形对象&#xff0c;如果没有圈起的部分&#xff0c;点击Add Component搜索添加。 接着选中Player对象&#xff0c;添加这两个组件&#xff0c;最好&#xff08;仅对于本项目开发&#x…

【Windows7和Windows10下从零搭建Qt+Leaflet开发环境】

Windows7和Windows10下从零搭建QtLeaflet开发环境 本文开始编写于2025年1月27日星期一&#xff08;农历&#xff1a;腊月二十八&#xff0c;苦逼的人&#xff0c;过年了还在忙工作&#xff09;。 第一章 概述 整个开发环境搭建需要的资源&#xff1a; 操作系统 Windows7_x6…

【游戏设计原理】97 - 空间感知

一、游戏空间的类型 将游戏设计中的空间设计单独提取出来&#xff0c;可以根据其结构、功能和玩家的交互方式划分为以下几种主要类型。这些类型可以单独存在&#xff0c;也可以组合使用&#xff0c;以创造更加复杂和有趣的游戏体验。 1. 线性空间 定义&#xff1a;空间设计是…

基于云计算、大数据与YOLO设计的火灾/火焰目标检测

摘要&#xff1a;本研究针对火灾早期预警检测需求&#xff0c;采用在Kaggle平台获取数据、采用云计算部署的方式&#xff0c;以YOLO11构建模型&#xff0c;使用云计算服务器训练模型。经训练&#xff0c;box loss从约3.5降至1.0&#xff0c;cls loss从约4.0降至1.0&#xff0c;…

【大数据技术】教程03:本机PyCharm远程连接虚拟机Python

本机PyCharm远程连接虚拟机Python 注意:本文需要使用PyCharm专业版。 pycharm-professional-2024.1.4VMware Workstation Pro 16CentOS-Stream-10-latest-x86_64-dvd1.iso写在前面 本文主要介绍如何使用本地PyCharm远程连接虚拟机,运行Python脚本,提高编程效率。 注意: …

bypass hcaptcha、hcaptcha逆向

可以过steam&#xff0c;已支持并发&#xff0c;欢迎询问&#xff01; 有事危&#xff0c;ProfessorLuoMing

Mac本地部署DeekSeek-R1下载太慢怎么办?

Ubuntu 24 本地安装DeekSeek-R1 在命令行先安装ollama curl -fsSL https://ollama.com/install.sh | sh 下载太慢&#xff0c;使用讯雷&#xff0c;mac版下载链接 https://ollama.com/download/Ollama-darwin.zip 进入网站 deepseek-r1:8b&#xff0c;看内存大小4G就8B模型 …

VSCode设置内容字体大小

1、打开VSCode软件&#xff0c;点击左下角的“图标”&#xff0c;选择“Setting”。 在命令面板中的Font Size处选择适合自己的字体大小。 2、对比Font Size值为14与20下的字体大小。

嵌入式学习---蜂鸣器篇

1. 蜂鸣器分类 蜂鸣器是一种电子发声器件&#xff0c;采用直流电压供电&#xff0c;能够发出声音。广泛应用于计算机、打印机、报警器、电子玩具等电子产品中作为发声部件。一般仅从外形不易分辨蜂鸣器的种类。但是有些蜂鸣器使用广泛&#xff0c;见得多了就很容易分辨。例如常…

解析PHP文件路径相关常量

PHP文件路径相关常量包括以下几个常量&#xff1a; __FILE__&#xff1a;表示当前文件的绝对路径&#xff0c;包括文件名。 __DIR__&#xff1a;表示当前文件所在的目录的绝对路径&#xff0c;不包括文件名。 dirname(__FILE__)&#xff1a;等同于__DIR__&#xff0c;表示当前…

C++底层学习预备:模板初阶

文章目录 1.编程范式2.函数模板2.1 函数模板概念2.2 函数模板原理2.3 函数模板实例化2.3.1 隐式实例化2.3.2 显式实例化 2.4 模板参数的匹配原则 3.类模板希望读者们多多三连支持小编会继续更新你们的鼓励就是我前进的动力&#xff01; 进入STL库学习之前我们要先了解有关模板的…

【腾讯前端面试】纯css画图形

之前参加腾讯面试&#xff0c;第一轮是笔试&#xff0c;面试官发的试卷里有一题手写css画一个扇形、一个平行四边形……笔试时间还是比较充裕的&#xff0c;但是我对这题完全没有思路&#x1f62d;于是就空着了&#xff0c;最后也没过。 今天偶然翻到廖雪峰大佬的博客里提到了关…

智慧园区综合管理系统如何实现多个维度的高效管理与安全风险控制

内容概要 在当前快速发展的城市环境中&#xff0c;智慧园区综合管理系统正在成为各类园区管理的重要工具&#xff0c;无论是工业园、产业园、物流园&#xff0c;还是写字楼与公寓&#xff0c;都在积极寻求如何提升管理效率和保障安全。通过快鲸智慧园区管理系统&#xff0c;用…

自制虚拟机(C/C++)(三、做成标准GUI Windows软件,扩展指令集,直接支持img软盘)

开源地址:VMwork 要使终端不弹出&#xff0c; #pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup") 还要实现jmp near 0x01类似的 本次的main.cpp #include <graphics.h> #include <conio.h> #include <windows.h> #includ…

如何确认Linux嵌入式系统的触摸屏对应的是哪个设备文件(/dev/input/event1)?如何查看系统中所有的输入设备?输入设备的设备文件有什么特点?

Linux嵌入式系统的输入设备的设备文件有什么特点&#xff1f; 在 Linux 中&#xff0c;所有的输入设备&#xff08;如键盘、鼠标、触摸屏等&#xff09;都会被内核识别为 输入事件设备&#xff0c;并在 /dev/input/ 目录下创建相应的 设备文件&#xff0c;通常是&#xff1a; …

HTTP异步Client源码解析

我们知道Netty作为高性能通信框架&#xff0c;优点在于内部封装了管道的连接通信等操作&#xff0c;用户只需要调用封装好的接口&#xff0c;便可以很便捷的进行高并发通信。类似&#xff0c;在Http请求时&#xff0c;我们通过调用HttpClient&#xff0c;内部使用java NIO技术&…