| 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前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。






既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)

最后
==
就答题情况而言,第一问100%都可以回答正确,第二问大概只有50%正确率,第三问能回答正确的就不多了,第四问再正确就非常非常少了。其实此题并没有太多刁钻匪夷所思的用法,都是一些可能会遇到的场景,而大多数人但凡有1年到2年的工作经验都应该完全正确才对。
只能说有一些人太急躁太轻视了,希望大家通过此文了解js一些特性。
并祝愿大家在新的一年找工作面试中胆大心细,发挥出最好的水平,找到一份理想的工作。
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中…(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)]
467

被折叠的 条评论
为什么被折叠?



