前端学习 node 快速入门 系列 —— 模块(module)
其他章节请看:
模块(module)
模块的导入
核心模块
在 初步认识 node 这篇文章中,我们在读文件的例子中用到了 require('fs'),在写最简单的服务器的例子中用到了 require('http'),除了 fs 和 http,node 提供了很多核心模块,例如:path(路径)、os(操作系统)、events(事件)、url 等等。
如果我们需要使用核心模块的功能,就使用 require(模块名) 方法进行引入。
第三方模块
在 npm 一文中,我们知道了如何用 npm 下载包。如果我们需要使用第三方的模块,也可以像引入核心模块那样。请看示例:
// 首先得下载包。后续不再提醒
$ npm install underscore
let _ = require('underscore')
console.log(_.VERSION)
console.log(typeof _.clone)
console.log(typeof _.find)
/*
输出:
1.12.0
function
function
*/
注:由于核心模块和第三方模块(npm 下载的包)都是通过模块名加载,所以它们的名字不会相同。也就是说 node 不允许第三方模块占用 node 核心模块的名字。
require() 方法加载第三方模块有这么一套规则:
- 找到执行文件所属目录的 node_modules 文件夹
- 找到 node_modules/第三方模块名/package.json
- 找到 main 字段指向的入口文件
- 加载入口文件
- 找不到 package.json 或找不到 main 字段,又或者找不到 main 指向的文件,就加载 index.js
- 以上都失败,则去上一级找 node_modules 目录,找不到又去上一级,上一级,直到根目录都没有找到,就报错处理
我们用 underscore 这个第三方模块验证一下上面的规则。请看示例:
首先准备环境,导入 underscore。
// 进入项目(D:\实验楼\node-study\test2)
// 快速生成 package.json
$ npm init -y
// 下载 underscore
$ npm install underscore
// 创建 test2/index.js
let _ = require('underscore')
console.log(_.VERSION)
// 运行
$ node index
1.12.0
function
现在模块已经正常导入。
我们首先验证一下:加载的是否是 main 字段指向的文件。具体步骤请看:
// 修改 package.json 中的 main 字段。目录是:test2\node_modules\underscore\package.json
{
"main": "underscore.js",
// 改为
"main": "a.js",
}
// 新建 a.js 文件。目录是:test2\node_modules\underscore\a.js
exports.name = 'ph'
// 修改 index.js 内容如下
let _ = require('underscore')
console.log(_)
$ node index
{ name: 'ph' }
重置 package.json 中 main 字段,将 underscore 模块的主入口文件改为 a.js,最终我们拿到的 underscore 确实是我们返回的 { name: 'ph' }。
接着将 a.js 改为 index.js,执行 node index,输出的还是 { name: 'ph' }。说明 require() 找不到 main 指向的入口文件 a.js,于是就去找 index.js。
最后将 node_modules 文件夹剪切至 D 盘根目录中,执行 node index 仍旧输出 { name: 'ph' }。
自定义模块
在 node 中,每个文件都被视为一个单独的模块。
浏览器可以通过 script 标签执行多个 js 文件,但 node 只能执行一个文件,不过我们可以通过 require() 方法加载其他 js 文件,js 文件又加载其他 js 文件,如此循环,最终就形成了一个大大的模块。请看示例:
// index.js 内容
let m = require('./a') // {1}
console.log(m.sum(1,2))
// a.js 内容
let sum = (v1, v2) => v1 + v2
module.exports.sum = sum
// 运行
$ node index
3
index.js 中加载了 a.js。相当于运行了 2 个文件。
如果将 require('./a') 改成 require('a')(行{1}),运行则会报错:Error: Cannot find module 'a'。因为 require('a') 会直接去核心模块和第三方模块中找,结果又没有找到对应的 a 模块。只有传入 require(id) 方法中的 id 以 '/'、'./'或'../'开头,才会从自定义模块中找。
通常 '/' 很少会用到。请看示例:
// D:\1.js
console.log('hello')
// D:\实验楼\node-study\index.js
require('/1')
$ node index
hello
运行 require('/1') 会到文件的根目录(D 盘)中找 1.js。如果别人用你的项目,还得在根目录下也存一份同样的 1.js 文件吗?这是很困难的。
到现在,我们已经知道 require() 方法能导入模块,也能执行模块。其实它还有一个重要特性:优先从缓存中加载,重复导入也只会执行一次。
// 1.js
var a = 1
let two = require('./2')
let twoCopy = require('./2')
console.log(a)
console.log(`two a = ${two.a}`)
console.log(`twoCopy a = ${twoCopy.a}`)
// 2.js
var a = 2
console.log(`我是 2.js`) // {1}
exports.a = a;
// 运行
$ node 1
我是 2.js
1
two a = 2
twoCopy a = 2
在 1.js 中导入了两次 2.js,但只输出了一次 我是 2.js(行{1})。
请接着看:
// index.js
require('./a')
require('./b')
// a.js
require('./b')
// b.js
console.log('hello')
$ node index
hello
模块 b.js 同样被导入了两次,但也只执行了一次。
模块的导出
每个文件都有一个 module 的变量。就像 require() 方法,无需加载就可以直接使用。请看示例:
// index.js
console.log(typeof require)
console.log(module)
// 运行
$ node index
function
Module {
id: '.',
...
exports: {}, // {1}
...
}
我们看到 module 里面有一个 exports 的对象(行{1})。
如果我们的模块需要对外提供接口,就可以使用 module.exports。请看示例:
// index.js
let m = require('./1')
console.log(`name = ${m.name} age = ${m.age}`)
// 1.js
let name = 'ph'
let age = 'age'
module.exports.name = name // {1}
$ node index
name = ph age = undefined
模块 1.js 只导出了 name 属性,所以 index.js 只能读取到 name ,而读不到 age。
module.exports 还提供了一个快捷方式:直接使用 exports。例如将 module.exports.name = name(行{1}) 改成 exports.name = name 效果也是一样的。
注:由于 exports 是 module.exports 的引用,就像任何变量一样,如果将新值分配给 exports,则它不再绑定到 module.exports。请看示例:
// index.js
let m = require('./1')
console.log(m)
// 1.js
module.exports = 'ph' // {1}
$ node index
ph
如果将 module.exports = 'ph'(行{1}) 换成 exports = 'ph',输出结果是:{},说明导出失败。
模块的作用域
node 中没有全局作用域,只有模块作用域。内部访问不了外部,外部访问不了内部。请看示例:
// 1.js
var a = 1
var a = 2
console.log(a)
执行 $ node 1 输出 2。稍微改一下:
// 1.js
var a = 1
require('./2')
console.log(a)
// 2.js
var a = 2
再次执行 $ node 1,这次输出的是 1。说明 2.js 中的 变量 a 没能影响到 1.js 中的变量 a。继续:
// 1.js
require('./2')
console.log(a)
// 2.js
var a = 1
再次运行 $ node 1,输出报错信息 ReferenceError: a is not defined,说明 1.js 不能访问 2.js 中的变量。这就是外部访问不了内部。
其他章节请看:
前端学习 node 快速入门 系列 —— 模块(module)的更多相关文章
- 前端学习 node 快速入门 系列 —— 初步认识 node
其他章节请看: 前端学习 node 快速入门 系列 初步认识 node node 是什么 node(或者称node.js)是 javaScript(以下简称js) 运行时的一个环境.不是一门语言. 以 ...
- 前端学习 node 快速入门 系列 —— npm
其他章节请看: 前端学习 node 快速入门 系列 npm npm 是什么 npm 是 node 的包管理器,绝大多数 javascript 相关的包都放在 npm 上. 所谓包,就是别人提供出来供他 ...
- 前端学习 node 快速入门 系列 —— 报名系统 - [express]
其他章节请看: 前端学习 node 快速入门 系列 报名系统 - [express] 最简单的报名系统: 只有两个页面 人员信息列表页:展示已报名的人员信息列表.里面有一个报名按钮,点击按钮则会跳转到 ...
- 前端学习 node 快速入门 系列 —— 简易版 Apache
其他章节请看: 前端学习 node 快速入门 系列 简易版 Apache 我们用 node 来实现一个简易版的 Apache:提供静态资源访问的能力. 实现 直接上代码. - demo - stati ...
- 前端学习 node 快速入门 系列 —— 服务端渲染
其他章节请看: 前端学习 node 快速入门 系列 服务端渲染 在简易版 Apache一文中,我们用 node 做了一个简单的服务器,能提供静态资源访问的能力. 对于真正的网站,页面中的数据应该来自服 ...
- vue 快速入门 系列 —— vue loader 上
其他章节请看: vue 快速入门 系列 vue loader 上 通过前面"webpack 系列"的学习,我们知道如何用 webpack 实现一个不成熟的脚手架,比如提供开发环境和 ...
- vue 快速入门 系列 —— vue-cli 下
其他章节请看: vue 快速入门 系列 Vue CLI 4.x 下 在 vue loader 一文中我们已经学会从零搭建一个简单的,用于单文件组件开发的脚手架:本篇,我们将全面学习 vue-cli 这 ...
- webpack 快速入门 系列 - 自定义 wepack 上
其他章节请看: webpack 快速入门 系列 自定义 wepack 上 通过"初步认识webpack"和"实战一"这 2 篇文章,我们已经学习了 webpac ...
- vue 快速入门 系列 —— vue loader 下
其他章节请看: vue 快速入门 系列 vue loader 下 CSS Modules CSS Modules 是一个流行的,用于模块化和组合 CSS 的系统.vue-loader 提供了与 CSS ...
随机推荐
- ssh原理及加密传输
1.ssh??(保证过程中是加密的,即安全的)ssh 是 Secure Shell 的缩写,是一个建立在应用层上的安全远程管理协议.ssh 是目前较为可靠的传输协议,专为远程登录会话和其他网络服务提供 ...
- cdn jsdelivr + github releases 以wordpress sakura主题manifest为例
1 创建github repository 在本地创建文件,这里为文件夹 /manifest 在github创建库wordpresscdn,上传 /manifest到库中 2 github relea ...
- 使用MCSManager搭建Minecraft服务器
目录 一.准备工作 1.MCSManager Windows环境下安装 Linux安装 2.Minecraft服务端 3.Java 二.配置 1.登录面板 2.上传服务端 3.服务端的配置 三.开启服 ...
- Linux shell create file methods
Linux shell create file methods touch, cat, echo, EOF touch $ touch file.py $ touch file1.txt file2. ...
- Nestjs mongodb
nestjs 文档 mongoose 文档 使用"@meanie/mongoose-to-json"转换查询后返回的json数据 将"_id"转为"i ...
- echarts手机端,数据多时可以滚动
<div id="container" style="height: 400px"></div> <script type=&qu ...
- 新年狂欢高倍币,1万枚VAST拿到手软
新一年NGK推出了新币VAST,成为了当前最瞩目的一颗星.有人认为VAST有价无市,有人认为VAST价值巨大,潜力无限.总之,众说风云! 选择数字货币,当然是挑选有价值的货币,从内外价值入手. 一.币 ...
- 调整是为了更好的上涨,牛市下的SPC空投来了!
2021年刚过没几天,比特币就开启了牛市的旅程,BTC涨到4万美元,ETH涨到1300多美元,BGV也涨到了621.05美元,牛市已然来袭. 虽然从近两日,比特币带领着主流币进行了一波调整,但是只涨不 ...
- NGK公链大事件盘点——回顾过去,展望未来!
NGK公链构想广阔,愿景宏大,2020年10月NGK正式上线,同时NGK全球发布会正式启动,建立区块链生态体系. 早在这之前,NGK就经过了紧锣密鼓的数年缜密搭建. 2018年6月NGK底层系统技术原 ...
- PAA房产,一家有温度的房产公司
PAUL ADAMS ARCHITECT房产(以下简称PAA,公司编号:07635831)对每一个客户从心出,为他们选择优质房源,为他们缔造家的温暖.PAA房产,是一家有温度的房产公司. PAA房产( ...