Node(7),程序员必学

exports指向module.exports对象,用于挂载模块对外导出的数据。
require本身是一个函数,用于导入其他模块的module.exports对象
module模块对象,包含模块的一些信息。
__filename模块对应的文件所在绝对路径,包含文件名
__dirname模块所在文件夹得绝对路径,不包含文件名

我们来看下module形参传入的对象是啥?

CommonJS - Module对象是个啥

可以发现module形参传入了一个Module类型对象,该Module对象有如下属性

id模块的唯一标识
path模块所在文件夹的绝对路径,不包含文件名,等价于模块包装函数的形参__dirname
exports模块的对外暴露对象,等价于模块包装函数的形参exports
filename模块文件所在的绝对路径,包含文件名,等价于模块包装的形参__filename
loaded模块是否被加载了,默认false,表示未被加载
children当前模块引入的其他模块的集合
paths其他模块引入该模块,未指定查找路径时,提供的默认查找路径集合

这里我们发现,包装函数的module形参值 和 包装函数的其他形参存在对应关系,我们不妨验证:

那么模块包装函数为什么不只提供一个module形参,反而多余的搞了exports,__dirname,__filename 这些形参呢?

我思考了一下,应该是这几个参数比较常用,单独拎出来后,使用起来比较方便。而且语义化设计更好。

以上就是关于模块包装函数的介绍,Node通过模块包装函数实现了模块独立作用域。

CommonJS - 如何实现模块的导出和导入

我们可以在模块中使用模块包装函数的exports形参或者module.exports来挂载模块需要对外暴露的数据。

我们可以在模块中使用模块包装函数的require形参来导入其他模块的module.exports对象

通过以上示意图,我们可以知道:

每一个模块都对应一个Module对象,Module对象的exports,path,filename会作为实参传给模块包装函数的形参 exports,__dirname,__filename。require函数是唯一的,会被作为每一个模块包装函数的形参require的值。

小结一下:

模块内,使用module.exports导出数据,使用require引入其他模块的module.exports

CommonJS - module.exports和exports两种导出的区别

这里需要注意的是:模块require引入的最终是其他模块的module.exports对象。但是我们在模块内既可以使用module.exports导出数据,也可以使用exports导出数据,那么二者使用是否有差别呢?

答:有差别,module.exports 可以改变指向,但是exports不可以改变指向,举个例子

 

画图解释

CommonJS - 关于require函数必知的二三事

1、require函数的入参是个啥?

我们见过 require(‘./a.js’)这种的,也见过require(‘./a’)这种的,还有require(‘a’),当然也只有这三种。

是否指定了模块路径是否指定了模块后缀名
require('./a.js')
require('./a')×
require('a')××

2、require函数查找模块的规则是啥?

我们已经知道require函数参数传入就是要被加载的模块路径或名字。

当require(‘./a.js’)时,没有什么规则,根据指定路径和文件名查找即可

当require(‘./a’)时,按照如下流程查找

当require(‘a’)时,按照如下流程

注意,这里require(‘a’)查找模块所在的node_modules路径集合,就是Module对象的paths属性的值

CommonJS - 模块代码的执行时机

Node模块中代码只会在首次被require时执行,首次require之后对应Module对象的loaded属性被置为true,二次被require不会再次触发模块代码执行。

即require会触发loaded=false的模块中的代码执行,而而不会触发loaded=true的模块的代码执行。

CommonJS模块化规范和ES6模块化规范的区别

模块定义的区别

CommonJS一般只将JS文件和JSON文件视为有效模块(require只支持导入js或json文件),而ES6将任何文件都视为模块(import path语法支持导入任何类型文件)

模块三要素的区别

1、CommonJS将模块包装函数的函数作用域 作为 模块独立作用域,是运行时确定的。

由于ES6模块是在编译时(静态分析时)确定的,此时没有全局变量或模块变量之分,变量是运行时产生的,所以也不会产生变量污染

2、导出导入

CommonJS模块的导出module.exports的是对象,

ES6模块的导出接口export是静态分析阶段的符号链接

访问CommonJS模块的导出对象module.exports,其实访问的是对应模块导出数据的浅拷贝,

对于简单值来说,和原模块无关,对于引用值来说,和原模块有关。

访问ES6模块的导出接口export,其实是访问的对应模块的实时数据,无论是简单值还是引用值都和原模块有关

导入导出语法区别

CommonJS模块导入使用require函数,导出使用module.exports对象

ES6模块导入使用import path 或者 import { somename as anyname} from path 或者 import anyname from path

导出使用export 或 export {} 或 export default

ES6的导入导出语法更复杂一点,CommonJS的简单一点

模块代码的执行时机

CommonJS模块代码运行是在模块首次被require时,

ES6模块代码运行是在模块首次被import时

深入了解请看下面关于模块化的总结

(1条消息) 随笔-深入理解ES6模块化(三)_qfc_128220的博客-优快云博客 随笔-深入理解ES6模块化(三)_qfc_128220的博客-优快云博客")

Node.js的软件包管理器(npm)


啥是软件包

基于Node.js开发的应用程序都可以看成一个Node Package,Node package中通常都有一个包描述文件package.json。

啥是软件包管理器

npm (Node package manage),即软件包管理器,我们下载Node.js时,会默认捆绑下载npm。

它是一个命令行工具。

npm 可以通过命令行管理Node应用程序依赖的第三方软件包,比如下载、删除、更新、查看第三方软件包。

npm 下载和更新第三方软件包的默认地址是 npmjs.com,

npmjs.com 是一个Node软件包管理网站,开发者可以将自己开发Node软件包共享到该网站,也可以从该网站下载Node软件包。

认识package.json

一个Node软件包的项目根目录下必须有一个package.json文件,该文件用于记录当前软件包的一些基本信息。

我们可以在Node软件项目根目录下使用npm命令,生成一个由用户自定义的package.json

npm init

或者加上 --yes 或简写 -y 参数 来自动生成一个采取默认定义的package.json

npm init --yes

npm init -y

接下来,我们看下package.json包含哪些信息

npm init 生成的package.json只包含一些基本的软件包信息:

name软件包名字,或者项目名字
version软件包版本号
description软件包描述
main软件包入口文件
script软件包的自定义命令
keywords软件包的搜索关键词
author软件包的作者信息
license软件包的许可证信息,ISC表示开源

这些信息都是比较重要的信息,而其中常手动改的是 main 和 script

main用来指定当前软件包的入口文件,作用有两个:

1、我们可以通过查看一个软件包根目录下的package.json中的main配置,快速得知当前软件包的入口,而不需要分析代码

2、package.json中main配置 是 模块查找规则 的重要一环。比如 node test 或 require(‘test’),会先去当前目录下找test.js,再找test.json,如果都没有再找test文件夹,如果有,就继续找package.json中main配置的入口文件,如果有,入口文件会被当成找到的模块被运行或被引入。

script是用于配置当前软件包一些常用命令的npm run简写的:

比如上面可以使用 npm run serve 代替npx vue-cli-service serve。

下载第三方软件包

使用 npm install packagename 或者简写 npm i packagename 即可下载指定的软件包,命令执行完后,本地Node应用程序会发生如下变化:

1、下载软件包会被保存在项目根目录的node_modules文件夹下,如果没有node_modules文件夹,则会自动生成

2、package.json会发生变化,配置项多出一个 dependencies ,其中保存着下载的软件包名字 和  软件包版本号 组成的键值对

3、项目根目录下会多出一个 package-lock.json文件,该文件会记录下载的软件包的下载地址,以及该软件包依赖的其他软件包的下载地址

项目依赖和开发依赖

我们下载的第三方软件包有两种用途:

1、用于项目中业务逻辑的开发,比如 formidable,express

2、用于辅助开发者高效开发,和项目业务无关,比如一些常用的命令行工具nodemon,eslint,

我们有必要将他们区分开来,原因是:

项目上线后,其实并不需要和项目业务无关的第三方软件包,只需要和项目业务有关的第三方软件包。

但是项目开发过程中,又需要协同开发,即至少要保证大家使用一样的开发工具。

而Node应用程序考虑到了这一点,在项目的package.json将依赖的第三方软件包分为了两类:开发依赖 和 项目依赖

开发依赖的第三方包 会被保存在 dev-dependencies中

项目依赖的第三方包 会被保存在 dependencies中

而我们只需要在下载第三方包的时候,指定一些参数即可实现分类:

npm i --save packagename  即下载时指定–save,npm就自动将下载的包添加到package.json的dependencies中,即项目依赖

简写形式是:npm i -S packagename

最简形式是:npm i packagename

目前 npm i packagename 默认会将下载的第三方包当成“项目依赖”

npm i --save-dev packagename 即下载时指定 --save-dev,npm就自动将下载的包添加到package.json的dev-dependencies中,即开发依赖

简写形式时:npm i -D packagename

本地下载和全局下载

我们使用 npm i packagename时,默认将第三方软件包下载到 项目根目录下的node_modules文件夹中。

此时该第三方包只在当前项目中起作用,我们称这种下载叫本地下载。

如果我们本地有多个Node项目,如果每个Node项目都依赖一些相同的第三方包,则为每一个Node项目都下载一遍,会造成内存浪费。此时就需要将多个项目都需要的公共包,下载到全局下,即所有项目都可以访问到的地方,避免内存浪费。此时下载叫做全局下载。

全局下载只需要添加一个-g参数

npm i -g packagename

如果想知道全局下载的软件包的存放路径,则可以使用命令

npm root -g

需要注意的是:

全局下载的Node软件包如果是命令行工具,则其命令会被自动被注册为系统变量,在任意目录下都可用。

而本地下载的命令行工具Node软件包,由于其命令不会被自动注册为系统变量,所以无法在任意目录下直接使用其命令,只能通过npx命令来调用

例如,只在项目中安装nodemon(保证全局下没有nodemon,使用npm list -g查看)

此时nodemon命令不会被自动注册为系统变量,所以无法在任意目录下使用nodemon命令。需要借助npx命令借调nodemon命令

为什么下载到全局的node软件包的命令会自动注册为系统变量呢?

1、安装好Node.js后,会自动将node_global目录加入系统变量Path中

2、我们下载全局软件包时,会自动存放在node_global下

3、如果下载的全局软件包是命令行工具,则会将cmd命令文件放到node_global目录下,这就保证了如nodemon.cmd这样的命令可以在任意目录下使用

那么我们是否可以将所有开发依赖的软件包都从项目本地,转移到全局呢?

一般来说不会这样搞,将项目本地的node包单分出一个开发依赖,其实就是为了团队之间的协同,如果大家都自己搞自己的,可能对于新人不太友好。

软件包的语义化版本

在package.json中有三个地方存在软件包的版本号信息

1、package.json的version配置

2、package.json的dependencies配置

3、package.json的dev-dependcies配置

其中

version 版本号,指的是当前项目的版本号,由我们控制版本的升级

而dependencies和dev-dependencies中的版本号,是当前项目依赖的第三方Node包的版本号,不由我们控制。

通过上图,我们可以发现版本号由三部分组成,即 major.minor.patch

解释升级时机
major主要版本号已有需求发生改变,并且改变了已有功能
minor次要版本号已有功能不变,开发了新需求的功能
patch补丁版本号修复了已有功能的bug,需求不变

分析一下,patch和minor升级是向低版本兼容的,而major升级不向低版本兼容。

所以,我们项目依赖一个第三方包后,一般都需要锁定major版本,防止更新包后,新版本的第三方包代码不向下兼容。

此时就需要给版本号加入语义化。如上图中 package.json的 dependencies 依赖的第三方包的版本号前面有个^,这就是语义化符号。

^major.minor.patch表示锁定major版本,只能升级minor和patch版本
~major.minor.patch表示锁定major和minor版本,只能升级patch版本
major.minor.patch表示锁定major,minor,patch版本,不让升级

下载指定版本号的第三方软件包

前面我们下载第三方软件包时,都是只指定了包名,没有指定具体版本号,此时下载就是第三方包的最新版本,即

npm i packagename

下载的时packagename包的最新版本。

但是有时候最新版本并不稳定,所以我们一般下载比较稳定的老版本,此时就需要指定版本号

npm i packagename@major.minor.patch

但是我们可能并不知道对应第三方包有哪些版本,此时需要借助命令查询第三方包历史版本列表

npm view packagename viersions

更新第三方软件包

更新项目依赖的第三方软件包时,需要注意:

1、当前依赖的第三方软件包是否过时了

2、当前依赖的第三方软件包是否被锁定

通过命令 npm outdate 就可以查看当前项目依赖的第三方包是否存在过时的,如果有会全部列出来

我们发现 eslint和nodemon都过时了,且nodemon最新版本是2.0.15,但是只能升级到1.19.4。而eslint最新版本是8.6.0,但是只能升级到1.10.3,即当前版本,即eslint无法再升级了。

再看这两个包的版本锁定情况

发现都被锁死在了major版本,即major版本不允许升级,minor、patch版本可以升级。而这也是导致eslint和nodemon无法升级到最新版本,只能升级到major为1的最高版本。

更新版本时,使用如下命令可以更新所有过时的第三方包到锁定的最高版本,而不是最新版本

npm update

而 npm outdate 和 npm update 命令加上-g参数,就可以查看和更新全局下的过时Node包

删除第三方软件包

通常使用命令

npm uninstall packagename

来删除项目本地指定的第三方软件包,

或者使用其简写命令

npm un packagename

加上 -g 参数就可以删除全局下的第三方软件包

查看第三方软件包元数据

一个第三方软件包,包含了各种元数据,比如包名,最近更新时间,历史版本列表,作者,

这里我们可以使用命令

npm view packagename

来查看指定软件包的所有元数据信息

也可以加入元数据配置项参数,来查看特定元数据信息,如命令

npm view packagename views

获取依赖的所有第三方软件包,及其依赖包列表

比如,获取项目本地下载的所有第三方软件包使用命令

npm list

加上 -g 参数表示,获取全局下载的所有第三方软件包

npm list -g

但是npm list目前只能查看被依赖的第三方软件包本身,无法查看第三方软件包依赖的包,这里提供了 --depth num,来指定查看的层级,默认npm list相当于 npm list --depth 0

npm list -g --depth 1

项目直接依赖包和项目间接依赖包

我们项目下载了一个第三方软件包,比如mongoose,那么它就成为了我们项目的一个依赖包。

但是mongoose本身也是一个项目,它也有很多依赖包,也需要被下载。

即:我们下载mongoose包,则会自动下载mongoose包的依赖包。其中mongoose就是当前项目直接依赖包,mongoose的依赖包就是当前项目间接依赖包。它们都是当前项目必须的。

上面查看,当前项目的依赖包只有一个mongoose,但是实际上,当前项目根目录node_moudules文件夹中下载的第三方包包含:

可以发现 “项目直接依赖包” 和 “项目间接依赖包” 都会被存放在 项目的node_modules文件夹下

这就会造成一个问题:

比如项目下载 A包,A包依赖于 Z@1.0.0包,项目又下载了B包,B包依赖于Z@2.0.0包

那么按照上面下载规则,A,B,Z包都会放在项目的node_modules文件夹下,那么Z包是否会发生冲突覆盖?

答案:不会,因为 项目间接依赖包 下载遵循如下规则

项目直接依赖包 会被直接下载到 项目的node_modules下,

而项目间接依赖包 下载前,会检查当前项目node_modules下是否已有该包,

没有的话,则将项目间接依赖包 下载到 项目的node_modules下,

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
img

最后

==
就答题情况而言,第一问100%都可以回答正确,第二问大概只有50%正确率,第三问能回答正确的就不多了,第四问再正确就非常非常少了。其实此题并没有太多刁钻匪夷所思的用法,都是一些可能会遇到的场景,而大多数人但凡有1年到2年的工作经验都应该完全正确才对。
只能说有一些人太急躁太轻视了,希望大家通过此文了解js一些特性。

并祝愿大家在新的一年找工作面试中胆大心细,发挥出最好的水平,找到一份理想的工作。

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img

很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中…(img-h8iMPYSK-1712689068665)]
[外链图片转存中…(img-letaz6xe-1712689068665)]
[外链图片转存中…(img-DZKbdkAy-1712689068665)]
[外链图片转存中…(img-0dKXP5Cs-1712689068666)]
[外链图片转存中…(img-Mh6DLEjv-1712689068666)]
[外链图片转存中…(img-d9NflELx-1712689068666)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-duRAQhsZ-1712689068667)]

最后

==
就答题情况而言,第一问100%都可以回答正确,第二问大概只有50%正确率,第三问能回答正确的就不多了,第四问再正确就非常非常少了。其实此题并没有太多刁钻匪夷所思的用法,都是一些可能会遇到的场景,而大多数人但凡有1年到2年的工作经验都应该完全正确才对。
只能说有一些人太急躁太轻视了,希望大家通过此文了解js一些特性。

并祝愿大家在新的一年找工作面试中胆大心细,发挥出最好的水平,找到一份理想的工作。

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-JnfcOjgK-1712689068667)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值