目录
1、为什么浏览器和Node.js都可以运行JavaScript
2、浏览器中运行JavaScript和Node.js中运行JavaScript有什么区别
3、为什么在浏览器中JavaScript不能控制系统级别的API
7、Node.js是如何实现模块的,为什么在模块文件内部定义的变量在模块文件外部访问不到?
12、node_modules文件夹中的软件包不用提交到git仓库中
20、JavaScript在Node.js中为什么是多线程的
27、process.nextTick与setImmediate()
1、为什么浏览器和Node.js都可以运行JavaScript
因为浏览器和Node.js都内置了JavaScript V8 引擎
2、浏览器中运行JavaScript和Node.js中运行JavaScript有什么区别
浏览器为了能够让JavaScript操作浏览器窗口以及HTML文档,所以在V8引擎中添加了控制他们的API,就是DOM和BOM。所以JavaScript能够在浏览器中运行时是可以控制浏览器窗口对象和DOM文档对象的。
Node.js中是没有DOM和BOM的,所以Node.js中不能执行和他们相关的代码,比如window.alert()等。DOM和BOM是浏览器环境中特有的。在Node.js中,添加了很多系统级别的API,比如对操作系统中的文件和文件夹进行操作。获取操作系统信息等
JavaScript运行在浏览器中控制的是浏览器窗口和DOM文档
JavaScript运行在Node.js中控制的是操作系统级别的内容
3、为什么在浏览器中JavaScript不能控制系统级别的API
浏览器是运行在用户操作系统中的,如果能够控制系统级别的API就会存在安全隐患【可能会影响到用户的操作系统】
Node.js是运行在远程的服务器中的,访问的是服务器系统API,不存在这方面的问题。
4、Node.js能做什么
通常使用它来构建服务器端应用和创建前端工程化工具
JavaScript运行在浏览器中我们就叫它客户端JavaScript
JavaScript运行在Node.js中我们就叫它服务器JavaScript
5、全局对象-Node.js和浏览器
浏览器中的全局对象是window,Node.js里面的全局对象是global。在global对象中存在一些和window对象中名字相同且作用相同的方法,在Node.js环境中声明的变量不会被添加到全局对象中,变量声明后是能在当前文件中使用。
6、模块系统
在Node.js环境中,默认就支持模块系统,遵循CommonJS规范:
一个JavaScript文件就是一个模块,在模块文件中定义的变量和函数默认智能在模块文件内部使用,如果需要在其他文件中使用,必须显式声明将其进行导出。
module.exports,exports----进行导出 require------进行导入
使用require导入模块文件时,该模块文件会立即执行。且导入模块时建议采用const进行声明变量,防止模块被重置
7、Node.js是如何实现模块的,为什么在模块文件内部定义的变量在模块文件外部访问不到?
在模块文件执行之前,模块文件中的代码会被包裹在模块包装函数中,这样每个模块文件中的代码都拥有自己的作用域,所以在模块外部就不能访问模块内部成员了。
(function(exports,require,module,__filename,__dirname){
});
module和require是模块内部成员,不是全局对象global。
__filename:当前模块文件名称【包含绝对路径】
__dirname:当前模块所在路径【不包含文件名】
8、Node.js内置模块
path:模块内提供一些和路径操作相关的方法
File System(fs):文件操作系统,提供和操作文件相关的方法
http:搭建服务器相关
9、package.json
创建package.json文件:npm init
快速创建package.json文件:npm init --yes/npm init -y
10、下载Node.js软件包完成后发生的三件事
1、软件包会被存储在node_modules文件夹中,如果应用不存在此文件夹,npm会自动创建。
2、软件包会被记录在package.json文件中,包含软件包的名字以及版本号
3、npm会在应用中创建package-lock.json文件,用于记录软件包以及软件包的依赖包的下载地址及版本。
11、软件包依赖问题说明
11.1:为什么一个依赖包依赖的软件包不放在该依赖包的文件夹中呢:
会存在问题:
一、很多软件包都会有相同的依赖,导致开发者在一个项目会下载很多重复的软件包。
二、文件夹嵌套层次太深,导致文件夹在window系统中不能被直接删除。
11.2:所有的软件包都放置在node_modules文件夹中不会导致软件包的版本冲突嘛?
在目前的npm版本中,所有的软件包都会被直接放置在应用根目录的node_modules文件夹中,这样解决了文件夹嵌套层次过深和重复下载软件包的问题。当软件包版本冲突时,如果先下载先后下载的相同依赖的版本不一致,则后下载的版本会被放置在其软件包中的node_modules文件夹中,以此解决版本冲突问题。
12、node_modules文件夹中的软件包不用提交到git仓库中
13、查看软件包实际版本的方法
一、在node_modules文件夹找到相应的依赖包,找到他的package.json文件,可以在这个文件中的version字段中找到它的具体版本
二、通过npm list命令查看所有依赖软件包的具体版本,--depth选择指定查看依赖包的层级。即采用npm list --depth 0查看软件包实际版本
14、查看软件包的元数据
查看软件包的元数据 ---npm view+名字
查看软件包指定项 ------npm view +软件包名字 +指定项名字(如dependencies)
15、npm使用
下载安装指定版本:npm install +软件包@指定版本【npm install lodash@4.7.0】
删除执行软件包 : npm uninstall +软件包【npm uninstall lodash】
查看哪些软件包已经过期,对应的新版本又是什么:npm outdated
更新过期软件包:npm update
获取配置:npm config list
获取全局配置:npm config list -l
json格式显示:npm config list -l --json
设置npm配置:
获取npm下载地址:npm config get registry
获取npm用户配置文件:npm config get userconfig
更改npm镜像地址:npm config set registry +地址
如:npm config set registry https://registry.npm.taobao.org
16、项目依赖和开发依赖
项目依赖:无论是开发环境还是线上环境只要程序运行的过程中需要使用的软件包就是项目依赖。
开发依赖:在应用开发阶段使用,在生产环境中不需要使用的软件包。
在下载开发依赖时:加上--save dev 或者 -D
在开发环境中下载所有依赖软件包:npm install
在生产环境中下载时:npm install --prod
17、npx命令
1、临时安装软件包后删除软件包
2、执行本地安装的软件包
18、模块查找规则
一、在指定查找路径的情况下:
require("./server")
1.查找server.js
2.查找server.json
3.查找server文件夹,查看入口文件(package.json----main)
4.查找server文件夹中的index.js文件
二、在没有指定路径的情况下
require("server")
1.先查找是否是系统模块
2.在node_modules文件夹中查找
3.向上退一层,在上一层的node_modules文件夹中查找
4.可以一直往上退知道退到根目录
19、操作系统的一些知识
1、CPU
中央处理器,计算机核心部件,负责运算和指令调用。开发者编写的JavaScript代码在被编译成机器码后就是通过CPU执行的。
2、存储器
内存:用于临时存储数据,断电后数据丢失。由于数据读写速度快,计算机中的应用都是在内存中运行的。
磁盘:用于持久存储数据,断电后数据不丢失。内部有磁头依靠马达在盘片上读写数据,速度比内存慢。
计算机应用程序在没有运行时是存储在磁盘中的,当我们启动应用程序后,应用程序会被加载到内存中运行,应用程序中的指令会被中央处理器CPU来执行。
3、什么是I/O
I就是input,o就是output,I/O操作表示输入输出操作。
比如数据库的读写操作就是I/O操作,因为数据库文件是存储在磁盘中的。将内存中的数据读写入数据库对于内存来说是输出,查询数据库中的数据就是将磁盘的数据读取到内存,对内存来说是输入。
4、I/O模型
I/O操作的模型有两种:同步I/O(阻塞I/O)、异步I/O(非阻塞I/O)
一、CPU等待I/O操作执行完成获取操作结果后再去执行其他指令,这是同步I/O操作(阻塞I/O)
二、CPU不等待I/O操作执行完成,CPU在发出I/O指令后,内存和磁盘开始工作,CPU继续执行其他指令。当I/O操作完成后再通知CPU,I/O操作的结果是什么,这就是异步I/O。
异步I/O(非阻塞I/O)的同步API是在主线程中执行的,异步API是在底层的C++维护的线程中执行的。异步API的回调函数时一个同步代码,需要回到主线程中进行调用。
20、JavaScript在Node.js中为什么是多线程的
在Node.js代码运行环境中,它为JavaScript代码的执行提供了一个主线程,通常就是我们所说的单线程就是这个主线程,主线程用来执行所有的同步代码。但是Node.js代码运行环境本身就是由C++开发的,在Node.js内部依赖了一个叫做libuv的C++库,在这个库中维持了一个线程池,默认情况下在这个线程池存储了4个线程,JavaScript中的异步代码就是在这些线程中执行的,所以说JavaScript代码的运行过程不仅仅依靠了一个线程,其实本质上还是多线程的。
在Node应用程序启动后,并不会立即进入事件循环,而是先执行输入代码,从上到下开始执行,同步API立即执行,异步API交给C++维护的线程执行,异步API的回调函数被注册到对应的事件队列中。当所有输入代码执行完成后,开始进入事件循环。
JavaScript在Node.js中运行:Nodejs是单线程运行、基于事件驱动的,先同步(即按从上到下的顺序)执行整个整个js文件中的代码(此时的事件循环是暂停的),当遇到异步函数(setTimeout计时函数)时,会从线程池中寻求有用的线程来执行该异步函数,当异步函数执行完,就将回调函数放入消息队列里面。当整个js文件执行完后,事件循环开始执行,从消息队列里面取消息,开始执行里面的回调函数。
![](https://i-blog.csdnimg.cn/blog_migrate/c4e5b70660295b613fac9d2fea00c6fc.png)
![](https://i-blog.csdnimg.cn/blog_migrate/232c3cd1a21d82cf85b3494a8c706bec.png)
21、回调地狱问题
回调地狱就是回调函数多层嵌套导致代码难以维护的问题,基于回调函数的异步编程十分容易产生回调地狱的问题。
【Promise的链式调用比回调地狱更好维护】----里面的finally函数没有参数
22、Promise.all并发异步操作
执行完all中的异步函数后再执行then中的函数。
Promise.all([异步函数1,异步函数2,异步函数3]).then(res=>{})
23、异步函数
async声明异步函数的关键字,异步函数的返回值会被自动填充到Promise对象中。
await关键字后面只能放置返回Promise对象的API,可以暂停函数执行,等待Promise执行完后返回执行结果,await关键字只能出现在异步函数中。
async function run(){}
const fn=async ()=>{}
24、Event Loop机制概述
事件循环机制是用于管理异步API的回调函数什么时候回到主线程中执行。
Node.js采用的是异步I/O模型,且是事件驱动的【事件驱动就是当什么时候做什么事,做的事情就定义在回调函数中,可以将异步API的回调函数理解为事件处理函数,所以管理异步API回调函数什么时候回到主线程中调用的机制就叫做事件循环机制】
25、Event Loop的六个阶段
事件循环是一个循环体,在循环体中有六个阶段,在每个阶段中,都有一个事件队列,不同的时间队列存储了不同类型的异步API的回调函数。
1.Timers:用于存储定时器的回调函数【setInterval,setTimeout】
2.Pending callbacks:执行与操作系统相关的回调函数,比如启动服务器应用时监听端口操作的回调函数就在这里调用
3.ldel/Prepare:系统内部使用
4.IO Poll:存储I/O的回调函数队列,比如文件读写操作的回调函数。如果事件队列中有回调函数,执行他们直至清空队列。否则事件循环将在此阶段停留一段时间以等待新的回调函数进入,这个等待取决于以下两个条件:
一、setImmediate队列(check阶段)中存在要执行的回调函数
二、timers队列中存在要执行的回调函数。在这种情况下,事件循环将移至check阶段,然后移至Closing callbacks阶段,并最终从timers阶段进入下一次循环。
5.Check:存储setImmediate API的回调函数
6.Closing callbacks:执行与关闭事件相关的回调【例如关闭数据库连接的回调】
循环体会不断运行以检测是否存在没有调用的回调函数,事件循环机制会按照先进先出的方式执行他们直至队列为空。
25、宏任务与微任务
宏任务:setInterval,setTimeout,setImmediate,I/O
微任务:Promise.then,Promise.catch,Promise.finally,process.nextTick
26、宏任务与微任务的区别
1.微任务的回调函数被放置在微任务队列中,宏任务的回调函数被放置在宏任务队列中。
2.微任务优先级高于宏任务
当微任务事件队列中存在可以执行的回调函数时,事件循环在执行阶段的回调函数后会暂停进入事件循环的下一阶段,事件循环会立即进入微任务的事件队列中开始执行回调函数,当微任务队列中的回调函数执行完成后,事件循环在进入到下一个阶段开始执行回调函数。nextTick的优先级高于microTask,在执行任务时,只有nextTick中的所有回调函数执行完成后才会开始执行microTask。
不同阶段的宏任务的回调函数放置在不同的宏任务队列中,宏任务与宏任务之间没有优先级的概念,他们的执行顺序是按照时间循环的阶段顺序进行的。
27、process.nextTick与setImmediate()
1.process.nextTick()的方法的回调函数优先级最高,会在事件循环之前被调用。【微任务】---该微任务高于其他微任务,高于宏任务
2.setImmediate()表示立即执行,它是宏任务,回调函数会被放置在事件循环的check阶段。
如果应用中有大量的计算型任务,它是不适合放在主线程中执行的,因为计算任务会阻塞主线程,主线程一旦被阻塞,其他任务就需要等待,所以这种类型的任务最好交给由C++维护的线程去执行。
可以通过setImmediate方法将任务放入时间循环中的check阶段,因为代码在这个阶段不会阻塞主线程,也不会阻值事件循环。
结论:Node适合I/O密集型任务,不适合CPU密集型任务,因为主线程一旦阻塞,程序就卡住了。
28、网站的组成
web应用主要由三部分组成:用户界面、业务逻辑、数据
1.用户界面(视图层):用于将数据展示给用户的地方,采用HTML,CSS,JavaScript编写
2.业务逻辑(控制层):实现业务需求和控制业务流程的地方,可以采用Java,PHP,python,JavaScript编写。
3.数据(模型层):应用的核心代码,应用业务逻辑实现,用户界面的展示都是基于数据的,web应用中的数据通常是存储在数据库中的,数据库可以采用MySQL等。
29、什么是web服务器
服务器是指能够向外部(局域网或者万维网中的其他计算机)提供服务的机器(计算机)就是服务器。
在硬件方面,web服务器就是能够向外部提供网站访问服务的计算机。这台计算机中存储了网站运行所必须的代码文件和资源文件。
在软件方面,web服务器控制着用户如何访问网站中的资源文件,控制着用户如何与网站进行交互。
30、客户端
web应用中的客户端是指用户界面的载体,实际上就是浏览器。用户可以通过浏览器这个客户端访问网站应用的界面,通过用户界面与网站应用进行交互。
31、网站的运行
web应用是基于请求和响应模型的。
32、IP和域名
IP:互联网协议地址,标识网络中设备的地址,具有唯一性。
域名:是由一串用点分隔的字符串组成的互联网上某一台计算机或者计算机组的名称,用于在数据传输时标识计算机的电子方位。【如www.baidu.com】
33、DNS服务器
DNS:域名服务器,互联网域名解析系统,将域名转为IP
34、端口
是设备与外界通讯交流的出口,此处特指计算机中的虚拟端口。0~65535
通常web应用占用80端口,在浏览器中访问应用时80可以省略,因为默认就访问80。
35、URL
URL:统一资源定位符,表示我们要访问的资源在哪里以及要访问的资源是什么。
协议://+域名/IP+[:端口号]/+请求资源
如:https://127.0.0.1:8080/index.html
36、前台和台后,前端和后端
前台和后台都是指用户界面。前台是为了客户准备的,每个人都可以访问的用户界面。后台是为网站管理员准备的,只要登录以后才能访问的用户界面,用于管理网站应用中的数据。
前端是指开发客户端应用的程序员。
后端是值开发服务器端应用程序的程序员。
37、开发环境
在开发环境中,开发者机器既充当了客户端的角色又充当了服务器的角色。
38、创建web server
const http=require("http");// 导入http模块
const server=http.createServer();// 创建web服务器实例
// 为服务器实例绑定request事件,监听客户端请求
server.on('request',(req,res)=>{
let url=req.url;
let method=req.method;
let Content='<h1>404 NOT FOUND</h1>';
if(url==='/'|| url==="/index.html"){
Content='<h1>首页</h1>';
}
else if(url==='/about.html'){
Content='<h1>关于</h1>';
}
res.setHeader('Content-Type','text/html;charset=utf-8');
// 结束请求
res.end(Content);
})
// 启动服务器
server.listen(8080,function(){
console.log("this http server is running in http://127.0.0.1:8080");
})