第一章 绪论
第一讲.命令行窗口
命令行窗口也叫 小黑屏 cmd窗口 终端 shell
win+r,cmd,回车
常用指令:
- dir 列出当前目录下的所有文件
- cd+目录名 进入文件夹
- md+目录名 创建一个文件夹
- rd+目录名 删除一个文件夹
- 直接输入文件名+回车 打开文件
- e: 进入e盘
’ + '表示一个空格
目录:
- . 表示当前目录
- … 表示上一级目录
cd .. 回到上一级目录
环境变量(window系统中的变量)
分为用户变量和系统变量
path变量:存放的是在cmd命令行中可以直接打开的文件
当我们在命令行窗口打开一个文件或调用一个程序时,系统会先在当前目录下寻找文件程序,如果找到了,就直接打开,如果没找到,则会依次到环境变量的path中寻找,直到找到为止
如果没找到,则报错
所以我们可以将一些经常访问的程序和文件添加到path中,这样我们就可以在任意位置访问这些文件和程序.
修改之后要重启命令行才能生效
第二讲.进程和线程
进程:
进程负责为程序的运行提供必备的环境
进程就相当于工厂中的车间
线程:
是计算机中最小的计算单位,负责执行进程中的程序
线程相当于工厂中的工人
单线程是一个人干活,多线程是多个人干活
js是单线程
第三讲.Node历史
Node.js是一个能够在服务器端运行JS的开源代码,跨平台的JS运行环境
Node采用谷歌的V8引擎运行js代码,使用事件驱动,非阻塞和异步IO等技术提高性能,可优化应用程序的传输量和规模
Node.js创建起初的目的是创建高性能的web服务器
传统服务器是多线程的,每进来一个请求,就创建一个线程去处理请求
Node处理请求时是单线程的,但是在后台拥有一个IO线程池
第二章 Node.js基础
第一讲.操作JS
node书写js代码步骤:
- 安装node.js
- 打开cmd命令行
- 输入node,进入node状态,即可编写js代码
但是这样体验不是很好,我们更希望直接在.js文件中书写,然后用node执行
步骤:
- 打开cmd命令行
- 打开js所在文件夹
- 输入node+js文件名,即可执行
直接在目录中输入cmd,可以直接进入该目录所在的cmd
第二讲.模块化简介
JS的缺陷:
- 没有模块系统
- 标准库较少
- 没有标准接口
- 缺乏管理系统
模块化可以将一个完整的程序分成一个一个小的程序
降低耦合性,便于代码复用
如果程序设计的规模达到了一定程度
模块化可以有多种形式,但至少应该提供能够将代码分割为多个源文件的机制
CommonJS的模块功能可以帮我们解决这个问题,则必须对其进行模块化
CommonJS对模块的定义十分简单:
- 模块定义
- 模块引用
- 模块标识
模块化:
-
在node中,一个js文件就是一个模块
-
在node中,每一个js文件中的js代码,都是独立运行在一个函数中,而非全局作用域
因此,一个模块中的变量和函数在其他模块中无法访问
通过require( )函数来引入外部的模块
可以传递一个文件的路径作为参数,node将会自动根据该路径来引入外部模块
这里的路径如果使用相对路径,必须使用.或…开头
使用require引入模块,该函数会return一个对象,该对象代表的是引入的模块
var md=require('./02.module.js');
由于每个js文件都是封闭的,因此如果想向外部暴露变量和方法,可以将需要暴露给外部的变量和方法,设置为exports的属性:
exports.x=10;
exports.fn=function(){
console.log('a');
}
就不用var了
在引用时,这么写:
var a=require('./module01');
console.log(a.fn());
第三讲.模块化详解
模块标识:
require中的文件路径,称为模块标识
我们使用require引入外部模块时,使用的就是模块标识,我们可以通过模块标识来找到指定的模块
模块分为两类:
-
核心模块
由node引擎提供的模块,核心模块的标识就是模块的名字
var fs=require('fs');
-
文件模块
用户自己创建的模块,模块标识就是文件路径,可以说绝对或者相对路径
相对路径使用.或…开头
在全局对象有一个global,他的作用和网页中的window类似
在全局中创建的变量都会作为global的属性保存
在全局中创建的函数都会作为global的方法保存
可以通过global判断一个变量是不是全局变量
实际上模块中的代码都是包装在一个函数中执行的,并且在函数执行时,同时传递了五个实参
当node在执行模块中的代码时,它会首先在代码的最顶部添加如下代码:
function(exports,require,module,_filename,_dirname){
在代码的最底部,添加如下代码:
}//就是一个大括号
所以,我们可以使用exports和require
-
exports 用来将变量或函数暴露到外部
-
require 函数,用来引入外部的模块
-
module 代表的是当前模块自身
exports就是module的属性,所以既可以使用exports导出,也可以使用module.exports导出
-
_filename 当前模块的完整路径
-
_dirname 当前模块所在文件夹的完整路径
第四讲.exports和module.exports
我们不论使用exports还是module.exports,最后改变的都是module的exports属性
但是如果这样赋值:
exports={
a:10;
b:20;
}
module.exports={
a:10;
b:20;
}
第一个不能用,第二个可以用,因为exports是引用数据类型,它实质上是一个指针,如果直接修改exports,相当于将它指向了一个其他的对象.而修改module.exports,则是直接修改值本身
第五讲.包
CommonJS的包规范允许我们将一组相关的模块组合到一起,形成一组完整的工具
包规范:
-
包结构
用于组织包的各种文件
-
包描述文件
描述包的相关信息,以供外部读取分析
包实际上就是一个压缩文件,解压以后还原为目录.
符合规范的目录,除了主js文件以外,应该包含如下文件:
- package.json 描述文件,相当于包的说明书,必须有
- bin 可执行二进制文件
- lib js代码
- doc 文档
- test 单元测试
后面四个都是可有可无的
package.json,实质上就是一个json文件,用于描述非代码的相关信息
它是一个JSON格式的文件,其中的字段主要有:
-
dependencies 依赖
这个包使用时依赖于哪些包
-
devDependencies 开发依赖
开发时需要依赖什么,但是使用时并不需要
-
main 主文件目录
-
maintainers 开发者
-
version 版本
-
keywords 关键字
-
bugs 提交bugs的地址
等
在JSON中,不能写注释!!!
第六讲.npm
NPM: Node Package Manager Node包管理系统
类似于360安全卫士里的软件管家
对于Node而言,NPM帮助其完成了第三方模块的发布,安装和依赖(会自动下载所有依赖的文件).借助NPM,Node与第三方模块之间形成了很好的生态系统
Node中最重要的功能就是NPM
NPM的使用:(要先在命令行中输入npm)
-
npm±v 查看版本
-
npm 帮助说明
-
npm+search+包名 在网上搜索期望的包
-
npm+install+包名 在当前目录安装包
如果后面加一个–save 在安装包的同时加入到依赖中
-
npm+install+包名±g 全局模式安装包,一般都是些工具,适用于计算机,而非项目.
-
npm+init 在当前目录下创建.json文件(一路回车)
-
npm+remove+包名 删除包
-
npm+install 下载当前项目依赖的包,这是每一个包使用之前的必须指令!!!
第七讲.配置cnpm
npm是外网的,有时候不稳定
于是中国有一个镜像,将美国的东西都下载下来了
输入:
npm install -g cnpm -registry=https://registry.npm.taobao.org
然后以后使用的就是cnpm,而非npm去下载包
cnpm的文件格式与npm略有不同,目的是不会覆盖npm的官方版本
第八讲.搜索包的流程
通过npm下载的包,都放到了node_modules文件夹中
我们通过npm下载的包,直接通过包名引入即可
node在使用模块的名字引入模块时,会首先在当前目录的node_modules中寻找是否有该模块
如果有就直接用,如果没有则去上一个目录的node_modules中寻找
一直找到磁盘的根目录,如果一直没有则报错
第三章 文件系统
第一讲.Buffer缓冲区
从结构上看buffer很像一个数组,操作的方法也和数组类似.
数组中不能存储二进制数据,但是buffer可以,它的元素为16进制的两位数
使用buffer不需要引入模块,直接使用即可
var str="hello";
var buf=buffer.from(str);//将字符串内容转换成十六进制存储
buffer中的每一个元素的范围都是00-ff,实际上一个元素就表示内存中的一个字节
8bit=1byte
一个汉字占2个长度单位,3个字节空间
buffer中的内存不是通过JS分配的,而是直接在底层通过C++来创建的
也就是我们可以直接通过buffer来创建内存中的空间
以前创建指定大小的buffer:
var buf=new Buffer(10);//创建10字节大小的buffer
buffer的所有构造函数都不推荐我们使用,已经过期(上面那个不能用了)
现在创建buffer:
var buf=Buffer.alloc(10);
buf[0]=0xaa;//添加一个十六进制字符
buf[1]=88;//添加一个十进制字符
buf[10]=0xff;//会被忽略
buf[6]=556;//溢出了,只取后8位
Buffer的大小一旦确定,永远不能修改,Buffer是对底层内存的直接操控,其中的空间是连续的
如果下标越界,越界的部分会被忽略
只要Buffer中的数字在控制台或页面中输出,都会自动转换成十进制
如果想输出原有内容出来,可以将其转换为字符串:
console.log(buf[1].toString(16));//16指的是以16进制输出
当使用Buffer.alloc( )时,使用内存空间之前,会将内存中的内容全部清空
但是如果使用Buffer.allocUnsafe( ),就不会清空其中的内容,因此buffer中可能会有敏感数据
但是Unsafe方法的性能会更好一些
buf.toString( )方法可以将缓冲区中的内容直接转换成字符串
第二讲.文件系统简介
文件系统(File System,简称fs),通过node操作系统文件
在Node中,与文件系统的交互是非常重要的,服务器的本质就是将本地的文件发送给远程的服务端
Node通过fs模块和文件系统进行交互
该模块中提供了一些标准文件来打开,读取,写入文件,以及与其交互
使用fs需要先引fs模块,直接引入不需要下载:
var fs=require('fs');
fs模块中所有方法都有两种形式可以选择: 同步和异步
同步文件系统会阻塞程序的执行,也就是除非操作完毕,否则不会向下执行代码
异步文件不会阻塞程序的执行,而是在操作完成时,通过回调函数将结果返回
异步方法都有回调函数,同步方法都有返回值
第三讲.同步文件写入
手动写入文件的步骤:
- 打开文件
- 向文件中写入内容
- 保存并关闭文件
同步文件的写入:
-
打开文件
三个参数:
- path : 要打开文件的路径
- flags : 打开文件要做的操作类型(读还是写) r 只读 w 可写
- mode : 设置文件的操作权限,一般不传
会返回一个描述符(文件编号)作为结果,我们可以对该描述符进行各种操作
var fd=fs.openSync('hello.txt','w');
-
向文件中写入内容
参数:
- fd : 要写入的文件的描述符
- string : 要写入的内容
- position : 文件写入的起始位置(一般不传)
fs.writeSync(fd,'今天天气真不错');
-
保存并关闭文件
在服务器中,程序不会停止,因此文件需要单独手动关闭
参数: fd 要关闭的文件的描述符
fs.closeSync(fd);
第四讲.异步文件写入
异步方法没有返回值,由回调函数的参数返回
回调函数有两个参数:
-
err : 错误对象,如果没有错误则为null
如果使用只读打开文件,但是又没有创建文件,就会报错
-
fd : 文件的描述符
异步写入的特点是不阻塞其他进程,当文件读取以后,接下来的操作会交给线程池来进行,由线程池判断谁先谁后
因此,open下面的代码可能会先于open的回调函数执行.
因此,对文件的写入操作应该写在回调函数中
异步文件写入步骤:
-
引入fs模块
var fs=require("fs")
-
打开文件
方法 : fs.open( )
参数:
- path : 文件路径
- flags : 操作标志
- mode : 通常省略
- callback : 回调函数 不能省略
fs.open('hello.txt','w',function(err,fd){ if(!err){ console.log(fd); }else{ console.log(err); })
-
写入内容
方法 : fs.write( )
参数:
-
fd : 文件描述符
-
text : 写入的内容
-
callback : 回调函数
参数只有一个,就是err
fs.open('hello.txt','w',function(err,fd){ if(!err){ fs.write(fd,'今天天气真不错',function(err){ if(!err) console.log('写入成功'); }) } })
-
-
保存并关闭文件
只要回调函数中的内容完成之后,就可以关闭文件
方法 : fs.close( )
参数: fd callback
回调函数的参数同样只有err
fs.open('hello.txt','w',function(err,fd){ if(!err){ fs.write(fd,'今天天气真不错',function(err){ if(!err) console.log('写入成功'); }) } fs.close(fd,function(err){}); })
异步的优点:
程序会继续执行,不必等待系统响应
但是同步更符合人类思维,便于编程
而异步不符合人类函数,代码可读性差,但是有性能优势
异步可以处理异常,而同步不能处理异常
第五讲.简单文件写入
不论是同步还是异步,都比较麻烦,在实际开发中用的不多
也分简单同步和简单异步
简单同步 : fs.writeFileSync(file,data,[option]);
简单异步 : fs.writeFile(file,data,[option],callback);
参数:
-
file : 要操作的文件路径
-
data : 要写入的数据
-
option : 写入的设置,可选参数,一般不写
以对象形式传入,可选参数有 : 编码形式,mode,操作权限
{ flag:'a'; }
-
callback : 回调函数
回调函数的参数 : err
写入步骤:以异步为例
-
引入fs模块
var fs=require('fs');
-
写入
fs.writeFile('hello.txt','今天天气真不戳',function(err){ if(!err) console.log('写入成功'); })
它会自动打开和关闭
文件写入时,默认是从头开始写,会覆盖曾经写过的内容
如果不想从头写,要将flags写成 a ,意为追加
之前写的是相对路径,但是也可以写绝对路径,不过由于’\'是转义字符,因此要写双斜杠代替斜杠才能表示路径:
fs.writeFile("C:\\User\\Desktop\\hello.txt",'今天天气真不戳',{flag:'a'},function(err){
if(!err) console.log('写入成功');
})
或者,不写\,而是写/,可以只写一个,也可以达到相同的效果
第六讲.流式文件写入
之前写入文件的方式,都是已经准备好了所有要写的内容,然后一次性写入
因此必须在写之前准备好所有文件,这样会存在一个问题,如果文件过大,会导致占用过多的内存空间
因此,前面所有的文件写入,都不适合大文件的写入,性能较差,且容易导致内存溢出
由此引入了流式文件写入,相当于在文件上通了一根水管,源源不断的将文件送进去,而不是一次性写入
步骤:
-
打开文件
var fs=require('fs');
-
创建一个可写流(就是一个水管)
方法 : fs.createWriteStream(path,[option])
参数 : 写入文件的路径,写入设置
var ws=fs.createWriteStream('hello.txt');
这样ws就相当于一根水管,hello.txt就相当于一个水池,二者成功连接上
-
像文件中输出内容
方法 : ws.write( )
参数 : 字符串
ws.write('今天天气真不戳');
它的特点是只要ws还在,想什么时候写就什么时候写.
但是目前还没有关闭,可能对性能有所损伤
可以通过监听流的open和close事件来监听流的打开和关闭
监听方法 : ws.once( )
ws.on( ) 和 ws.once( )都可以绑定一个事件,不同的是on绑定的是长期有效的事件,而once绑定的是一次性事件
on是事件每次触发都会执行方法,once是只有第一次触发会执行方法
由于open只会出现一次,所以这里用once比较好
参数:
- 要监听的事件
- 回调函数
ws.once('open',function(){
console.log('流打开了');
})
流式写入是异步写入,在关闭流时,不能从接受端断开,因为还会有内容在流中没有传输完成,因此不能使用ws.colse( )方法
但是可以从发送端断开,这样流中的文件可以正常传输
从发送端断开的方法 : ws.end( )
ws.end();
第七讲.简单文件读取
文件的读取和写入类似,同样有同步文件读取,异步文件读取,这两个不再讲
另外就是简单文件读取和流式文件读取
简单文件读取方法 :
- 同步简单读取 : fs.readFileSync(path,[option])
- 异步简单读取 : fs.readFile(path,[option],callback)
参数:
-
path : 要读取文件的路径
-
potion : 要读取文件的设置
-
callback : 回调函数
参数:
- err : 异常
- data : 读取到的数据,返回类型为Buffer,需要toString( )一下才能用
返回值是Buffer,目的是在读取图片,音频等非文本文件时,也可以正常读取,而不是出现乱码
步骤:
-
打开文件
var fs=require('fs');
-
读取文件
fs.readFile('hello.text',function(err,data){ if(!err) console.log(data.toString()); })
在读取数据之后,还可以再写出去
-
写入文件
fs.readFile('an.jpg',function(err,data){ if(!err){ fs.writeFile('hello.jpg',data,function(err){ if(!err) console.log('文件写入成功'); }) } })
读取+写入,即可完成一个复制的功能.
第八讲.流式文件读取
与流式写入类似,同样适用于大文件,可以分多次读取到内存中
步骤 :
-
引入文件
var fs=require('fs');
-
创建一个可读流
方法 : fs.createReadStream(path,[option])
参数 : 读取文件的路径,读取设置
var rs=fs.createReadStream('an.jpg');
-
监听流的开启和关闭
rs.once('open',function(){ console.log('可读流打开了'); }) rs.once('close',function(){ console.log('可读流关闭了'); })
如果要读取一个可读流的数据,则必须为可读流绑定一个data事件,data事件绑定完毕,会自动开始读取数据
-
读取数据
读取到的数据通过data参数接收
rs.on('data',function(data){ console.log(data); })
会自动判断文件的大小,然后分次读取
读完了会自动关闭,但是如果不读就不会关闭.
-
写入数据
先监听可写流的开启和关闭.
rs.on('data',function(data){ ws.write(data); }) rs.once('close',function(){ console.log('可读流关闭了'); ws.end();//在数据读取完成之后,可读流先关闭,然后才关闭可写流 })
问题:要给读取和写入都设置监听,过于复杂
在rs中有一个方法 : rs.pipe(ws)
它可以将可读流中的方法直接写到可写流之中
当读写结束之后,会自动关闭,可以不用监听.
var fs=require('fs');
var rs=fs.createReadStream('an.jpg');
var ws=fs.createWriteStream('hello.jpg');
rs.pipe(ws);
第九讲.fs模块的其他方法
Sync表示同步,没有Sync则表示异步,每个方法只取一种进行说明
验证路径是否存在 : fs.existsSync(path,[option])
只有同步的可以用
var fs=require('fs');
var isExists=fs.existsSync('a.mp3');
返回值为true表示存在,false表示不存在
获取文件信息 : fs.stat(path,callback)
它会给我们返回一个对象stat,其中保存了当前对象状态的相关信息
stat对象中有以下内容:
- isFile( )方法 是否是文件
- isDirectory( )方法 是否是文件夹
- size属性 文件大小
- birthtime属性 文件的创建时间
var fs=require('fs');
fs.stat('a.mp3',function(err,stat){
console.log(stat.size);
})
删除文件 : fs.unlink(path,callback)
fs.unlinkSync('hello.txt');
会将文件删除
列出文件 : fs.readdir(path,[option],callback)
读取一个目录的目录结构
回调函数两个参数:
- err 异常
- files 是一个字符串数组,每一个元素就是一个文件夹或文件的名字
fs.readdir('hello.txt',function(err,files){
console.log(files);
})
截断文件 : fs.truncate(path,len,callback)
将文件修改为指定子节的大小
fs.truncateSync('hello.txt',9);
创建文件夹 : fs.mkdir(path,[mode],callback)
fs.mkdirSync('hello')
删除文件夹 : fs.rmdirSync(path)
fs.rmdirSync('hello');
重命名文件 : fs.rename(oldPath,newPath,callback)
参数:
- oldPath 旧的路径(名字)
- newPath 新的路径(名字)
- callback 回调函数
fs.rename('a.mp3','b.mp3',function(err){
if(!err) console.log('修改成功');
})
不仅可以传递文件名,也可以传递一个新的路径,这样也可以实现文件的移动
监视文件的修改 : fs.watchFile(filename,[option],listener)
参数:
-
filename : 要监视的文件的名字
-
options : 配置选项
-
listener : 回调函数,当文件发生变化时,回调函数会执行
参数:
- curr : 当前文件的状态
- prev : 修改前文件的状态
这两个对象都是stats对象,属性和可用方法与stats相同
fs.watchFile('hello.txt',function(curr,prev){
console.log('修改前文件大小'+prev.size);
console.log('修改后文件大小'+curr.size);
})
会一直监听着这个文件,只要文件发生了变化,回调函数就会执行