官方文档
https://nodejs.org/docs/latest-v16.x/api/
官方文档永远是最好的老师
Node.js简介
node意为节点,直译过来就是节点js,通俗的来说Nodejs就是运行在服务端的js环境
之前写的JS全是在浏览器运行的,而Node.js可以让JS代码直接在服务器运行
使用Nodejs执行js文件
从简单的Hello Node开始,我们再命令行窗口输入node就可以进入交互界面:
可以用js的语句输出一段话
1 | let name="hello"; |
运行node hello.js
即可在服务端运行js文件了
Node.js基础模块化简介
为了敲代码有更舒适的体验,编写js代码没啥问题,但是假如要用nodejs的函数和代码没有tab提示那就难受了所以IDEA配置一下:
enable那个code assistance即可
CommonJS规范:
ES的缺陷(ECMAscript):
- 没有模块
- 标准库少
- 没有标准接口
- 缺乏管理系统
在Node.js中模块也就是其他语言中的库,比如python的import这样式儿的
用require("文件名")
这种形式去引入模块,文件名中的.js可以省略
一个模块中的变量和函数在其他js文件中无法访问,因为它不是全局用域,相当于包裹在一个函数内,想要解决这个问题,我们得向外部暴露(export)属性exports.x="test"
,这样就向外部暴露了属性
这样console.log(require('xx.js"))
返回的是以下内容
想要获得这个x只需var md=require("xx.js");console.log(md.x)
即可,方法也同理
想暴露在外用export,不想暴露就直接写里边儿即可
模块化详解
模块分为2大类:核心模块,文件模块
核心模块就是Node.js自带的模块,文件模块就是开发者自己写的js文件做成的模块,这两本质一样
核心模块的标识就是模块的名字,文件模块的标识是文件的路径
在Node中没有windows
全局变量,取而代之的是global
在js里定义全局变量就不要再变量名前加var
即可
要证明程序运行在函数内,就使用console.log(arguments)
,arguments参数只在函数体里面有,arguments.callee
返回的是当前的函数是谁arguments.callee+''
把这个对象变为字符串,输出完整的函数:
1 | function (exports, require, module, __filename, __dirname) { |
也就是说Node会自动给我们添加function (exports, require, module, __filename, __dirname)
所以为什么能用export呢,因为他是我们函数的一个属性console.log(__filename)
打印当前模块的路径__dirname
就是js文件所在文件夹的路径
exports和module.exports
这两东西有啥区别呢?
1 | module.exports={ |
1 | var test=require("./module"); |
在module.exports模式中可以这样取定义暴露变量,而exports是不可以的
同时:
1 | var obj={}; |
由于a指向了obj.a,所以obj.a也跟着改变了,这一点和其他语言貌似有些不同,他等号就是指向了
1 | var obj={}; |
假如把a重新赋值一个对象,那obj.a仍然不会改变的:
这里要搞清楚,这是深拷贝和浅拷贝,这里涉及到栈内存
和堆内存
:
这样普通的基本类型赋值是在栈内存进行的,所以不会有指向问题
但对象类型就不一样了:
当我们new了一个对象时,会在堆内存中创建一个对象地址,我们赋值操作给的是地址,所以才会有上面的结果
但是当我们再给obj2进行赋值时,是在堆内存进行赋值,所以没有影响堆内存中的对象,因此对obj一点影响也没有
包简介
包可以理解为,更高级的模块,里面有很多简单模块整合在一起
npm简介
npm全程node package manager,叫做node包管理系统
常用命令:
1 | npm -v |
假如想在当前目录去安装包,在当前位置执行npm init
先初始化,再npm install
即可
要引入就要在package.json文件所在路劲创建js文件,上面的init就是创建json文件--save
:它的特点是,在安装的同时,把这个包设置为了依赖文件:
加了之后下次npm install
后面啥也不跟,就会自动安装math类
配置cnpm
cnpm是啥呢,由于npm用的是外国的网,所以会很慢,中国人为了解决这一问题搞了个镜像,叫做cnpm,这样就可以对着中国的镜像服务器去下载包了,cnpm大大的加快了我们下载的速度
配置cnpm:
运行npm install -g cnpm --registry=https://registry.npmmirror.com
下载cnpm:
然后我们就可以用cnpm去下载express框架的包,cnpm的指令和npm一模一样,在这之前我们先初始化一下我们需要安装包的文件夹:
entrypoint就是哪个js文件需要引用包,这就是入口,这里最好用一下index.js
接下来在这个文件夹安装express即可:
cnpm大大的加快了我们的speed
node搜索包的流程
node在使用模块名引入时,它首先在当前目录的node_moudle去找到是否有这个模块,如果没有就去上一级找node_moudle,如果上级也没有,那就上上级,一直到根目录都没有,那就报错
[*]Buffer缓冲区
啥是buffer缓冲区呢?实际上buffer和数组十分的类似,但是数组能存音乐,能存视频吗,那肯定是不可以的,buffer就是专门用来储存二进制数据的,使用buffer不需要引入模块,直接使用即可
1 | var str="hello kino"; |
不是说二进制吗,为什么是十六进制
确实是二进制,但是在计算机中,二进制是以十六进制去呈现的
buffer中的每一个元素都是从00-ff
,也就是0-255
也就是我们的hello中一个字母就是一个字节buf.length
表示占用内存大小
创建指定大小的buffer:
1 | var buf2=new Buffer(1024);//创造大小为1024也就是1k的buffer |
但是这种方法已经被废弃了,所以会出现报错:
因为有安全隐患,因此使用下面的方法:
1 | var buf2=Buffer.alloc(20);//20字节大小 |
可以看到可以用数组的样子去操控buffer,但是为什么是58呢?因为是十六进制
改为0x58也可以,可以识别十六进制字符串
buffer的大小一经确定,再也不可以修改了,buffer是对底层内存的直接操作
加入我们console.log(buf[1])
这样不会输出十六进制,只会输出十进制
假如需要转换就在后面加一个toString(16)即可也就是console.log(buf[2].toString(16))
1 | Buffer.allocUnsafe(20);//创建指定大小的内存,但buffer中可能有敏感信息 |
敏感信息就是初始化的时候可能不是00,也就是这个内存空间可能是之前其他代码用过的
如果我们需要将buffer缓冲区的数据转化为字符串的话直接console.log(buf.toString())
即可
[*]同步文件写入
fs全称file system也就是文件系统,顾名思义也就是对文件进行操作的系统,那必然也有I/O操作,也就是input和output
使用fs模块需要先引入一下,由于是核心模块所以不需要下载
可以看到有很多方法
1 | var fs=require("fs"); |
这儿就是同步文件写入的方法
[*]异步文件写入
在说异步之前,先说一下同步和异步文件写入之间的区别吧
同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;
异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其
他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
异步会大大的加大我们写入的效率
1 | var fs=require("fs"); |
简单地通过异步方法打开一个文件,异步方式中callback是必不可少的参数:
回调函数里有2个参数,第一个是err错误对象,第二个是fd,是文件的描述符
若没错误则err为null,假如出错了就会报错,所以err是第一个参数
1 | var fs=require("fs"); |
当我们只给了一个r权限,就报错了,如上
1 | var fs=require("fs"); |
按照逻辑来看,f应该是有值的对不对,但是这是错的,因为异步的方式是没有顺序可谈的:
可以看到是undefined也就是先执行了,这就是异步方式,不会造成堵塞
1 | var fs=require("fs"); |
[*]简单文件写入
1 | /* |
不需要打开和关闭
有很多打开状态,假如我们想追加内容,我们只需要用a模式
[*]流式文件写入
1 | /* |
[*]简单文件读取
1 | /* |
也可以读取图片,视频,假如不加tostring读出来的是buffer二进制内容
1 | var fs=require("fs"); |
这样可以实现图片复制
[*]流式文件读取
适用一些大文件
1 | var fs=require("fs"); |
文件内容一大就会分次读:pipe()
:可以直接将可读流文件传到可写流:
fs模块其他方法
1 | fs.existsSync(path)//验证路径是否存在 |
stat就是文件的状态,stat.size获取大小
完结撒花
About this Post
This post is written by Boogipop, licensed under CC BY-NC 4.0.