【JavaWeb学习】Node.js

1. node

命令行窗口 在命令行窗口打开一个文件或调用一个程序时,系统会首先在当前目录下寻找文件程序,如果找不到就会依据环境变量path的路径寻找,直到找到为止。这都找不到才会报错。 → \rightarrow 可以在任意位置访问在环境变量中存储了路径的文件。
进程和线程 4.1节
Node.js Node.js是一个能够在服务器端运行JavaScript的开放源代码、跨平台JavaScript运行环境,直接和系统进行交互。Node采用Google开发的V8引擎运行js代码,使用事件驱动非阻塞异步IO模型等技术来提高性能,可以优化应用程序的传输量和规模。Node的大部分基本模块都用JavaScript编写,在Node出现之前,JS通常作为客户端程序设计语言使用,以JS写出的程序常在用户的浏览器上运行。Node把JS的战场从前端换到了服务器。
Node的用途 Web服务API,比如REST;实时多人游戏;后端的Web服务,比如跨域、服务器端的请求;基于Web的应用;多客户端通信,如即时通信。

  • Node的特点:单线程
    处理请求时是单线程,但在后台拥有一个I/O线程池。

1.1. 模块化 - CommonJS规范

1.1.1. 模块引用 模块定义 模块标识

在Node中,一个 js 文件就是一个模块。用require()函数引用外部模块,其参数为待引入模块的路径;如果使用相对路径,必须以'.''..'开头。使用require()引入模块以后,该函数会返回一个对象,该对象代表引入的模块。Node里每一个js文件中的代码都独立运行在一个函数中,相当于它们的作用域只在这个js文件的范围内,不是全局作用域,其他文件无法访问。 → \rightarrow 需要用exports暴露希望被看到的属性或方法。

// reqAndExp.js
exports.x = "I am exports";
// learn.js
var yytTool = require('./reqAndExp.js');
console.log(yytTool.x);

用模块require()引入外部模块时,使用的就是模块标识(路径)。通过模块标识可以找到指定的模块。模块分为核心模块和文件模块两大类。
核心模块 由Node引擎提供的模块,标识就是模块的名字;
文件模块 用户自己创建的模块,标识是文件的路径,常用相对路径。

在Node中有一个全局对象global,它的作用和网页中的window类似。在全局中创建的变量都会作为global的属性保存,在全局中创建的函数都会作为global的方法保存。

当Node在执行模块中的代码时,会首先在代码的最顶部添加function(exports, require, module, __filename, __dirname),在最底部添加}。也就是将模块中的所有代码封装到一个函数里,在函数执行时,同时传递进了5个实参。所以模块里的变量全都是局部变量。

  • exports 对象,将变量或函数暴露到外部;
  • require 函数,用来引入外部模块;
  • module 代表当前模块本身,exportsmodule的属性,可以用exportsmodule.exports导出变量和函数,本质是一样的;
// reqAndExp.js
exports.x = "I am exports";
module.exports.y = "I am module.exports";

console.log(exports === module.exports); // true
// learn.js
var yytTool = require('./reqAndExp');

console.log(yytTool.x); // 'I am exports
console.log(yytTool.y); // I am module.exports
console.log(__dirname); // e:\yyt\JavaWeb\learning\learn
console.log(__filename); // e:\yyt\JavaWeb\learning\learn\learn.js
  • __filename 当前模块的绝对路径;
  • __dirname 当前模块所在文件夹的完整路径。
1.1.2. exportsmodule.exports

module.exports时,可以给它赋值一个对象,统一导出,而exports只能一个一个导出:

exports.name = 'yyt';
exports.age = 16;
exports.sayName = function(){
    console.log("I am "+ this.name);
};
module.exports = {
    name:'lyl',
    age: 16,
    sayName: function(){
            console.log("I am "+ this.name);
        }
};

两种方法都是在修改module.exports属性,它们指向的是同一个位置。引用变量赋值时,如果两个引用变量指向同一个对象,让其中一个引用变量指向另一个对象,另一个依然指向之前的对象。 我们要用的是module.exportsexports只是指向了module.exports相同的位置,当然也可以通过exports.属性值的方式暴露内部变量。如果使用exports = {}的形式暴露,就相当于让它指向了一个新的变量,不再和module.exports指向同一个位置了,这种情况不应该出现,所以不能这样用。

1.2. 包

CommonJS的包规范允许将一组相关的模块组合到一起,形成一组完整的工具。包规范由包结构和包描述文件两部分组成。
(一个包就是把自己写的代码放到一个文件夹里,然后加一个描述文件告诉别人它们在干嘛。)

  • 包结构 用于组织包中的各种文件;
    包实际上是一个压缩文件,解压后还原为目录。符合规范的目录应该包含以下文件:
    package.json 描述文件(必需),里面不能有注释嗷
    bin 可执行二进制文件
    lib js代码
    doc 文档
    test 单元测试
  • 包描述文件 描述包的相关信息,以供外部读取分析;位于包的根目录下。
1.2.1. 包管理工具 npm(Node Package Manager)

CommonJS包规范是理论,npm是其中一种实践。对于Node而言,npm帮助其完成第三方模块的发布、安装和依赖等。借助npm,Node与第三方模块之间形成了一个很好的生态系统。
npm -v 查看版本
npm version 查看所有模块的版本
npm 帮助说明
npm search 包名 搜索模块包
npm install 包名 在当前目录安装包
npm install 包名 -g 全局模式安装包(一般是各种工具,计算机用而不是项目用)
npm remove/r 包名 移除包
npm install 包名 --save 安装包并添加到依赖中(常用)
npm install 下载当前项目依赖的包

1.2.2. 配置 cnpm

淘宝镜像 npm install -g cnpm --registry=https://registry.npmmirror.com

1.2.3. node 搜索包的流程

通过npm下载的包都放到node_module文件夹中,通过npm下载的包可以直接通过包名引用。node在使用模块名字来引入模块时,会首先在当前目录的node_module中寻找是否含有该模块,如果没有就去上一级目录的node_module中找,如果直到磁盘根目录都没有,那么报错。

2. 文件系统

2.1. Buffer 缓冲区

Node.js v18.4.0 文档
Buffer的结构和操作方法和数组很类似。
数组中不能存储二进制文件,而Buffer专门用来存储二进制数据。
使用buffer不需要引入模块,直接使用即可。

var str = "啾咪, hoh~";
// 将一个字符串保存到Buffer中
var buf = Buffer.from(str);
console.log(buf); // <Buffer e5 95 be e5 92 aa 2c 20 68 6f 68 7e>
// <Buffer e5 95 be('啾') e5 92 aa('咪') 2c(',') 20(' ') 68('h') 6f('o') 68('h') 7e('~')>
console.log(buf.toString()); // '啾咪, hoh~'
// 占用内存的大小
console.log(buf.length); // 12
// 字符串的长度
console.log(str.length); // 8

Buffer中存储的都是二进制数据,但是都以十六进制的形式显示。每一个元素的范围都是 00 ~ ff(00000000 ~ 11111111),一个元素占用一个字节。
一个汉字占用三字节,一个英文字母占用一字节。

// 创建指定大小的buffer
var buf = Buffer.alloc(8);
console.log(buf.length); // 8
// 通过索引操作buffer中的元素
for(let i=0; i<8; ++i){
    buf[i] = 12 + i;
}
console.log(buf); // <Buffer 0c 0d 0e 0f 10 11 12 13>

Buffer的大小一旦确定就不能改变(用超过buffer大小的索引来操作元素也不会报错,只是会无事发生罢了),实际上是对底层内存的直接操作。Buffer的内存不是通过JavaScript分配的,而是在底层通过C++申请的。
给buffer中的元素赋值时,只会取二进制表示的后8位(数值越界的话会被截断,比如256 = 1 0000 0000,在buffer中保存的会变成0 = 0000 0000)。
在控制台或页面中输出buffer中的数字时都会用十进制只能是十进制,做不到输出其他进制)。但是可以用字符串的形式查看数字的其他进制表示。

var buf = Buffer.alloc(8);
console.log(buf.length); // 8
// 通过索引操作buffer中的元素
for(let i=0; i<8; ++i){
    buf[i] = 12 + i;
}
console.log(buf[3].toString(8)); // 17
console.log(buf[3].toString(16)); // f

Buffer.allocUnsafe(size) 以这种方式创建的 Buffer 实例的底层内存不会被初始化。 新创建的 Buffer 的内容是未知的,可能包含敏感的数据(分配的是之前用过的内存,不会清空里面原有的数据,可能会有用户名、密码等敏感信息;而Buffer.alloc()会清空原有的数据)。

2.2. 文件系统 File System

通过Node来操作系统中的文件。
在Node中,与文件系统的交互是非常重要的,服务器的本质就是将本地文件发送给远程客户端。使用文件系统需要先引入fs模块(核心模块,直接引用,无需下载)var fs = require('fs');
fs模块中所有的操作都有同步异步两种形式可供选择。同步文件系统会阻塞程序的执行,除非操作完毕否则不会向下执行代码;异步文件系统不会阻塞程序的执行,而是在操作完成时通过回调函数将结果返回。
文件系统标志 flag

2.2.1. 同步文件写入
  • 打开文件 fs.openSync(path, flags[, mode]);
    path:路径
    flags:操作类型,'r'只读, 'w'可写;
    mode:文件的操作权限,一般不传
    返回一个文件描述符作为结果,通过描述符来操作文件。
  • 向文件中写入内容fs.writeSync(fd, string[, position[, encoding]])
    fd:文件描述符
    string: 要写入的内容
    position: 写入的起始位置,默认值null
    encoding: 编码方式,默认值’utf8’
    返回写入的字节数。
  • 保存并关闭文件fs.closeSync(fd)
    返回undefined。
var fs = require('fs');
var fd= fs.openSync('./learn.txt', 'w'); 
fs.writeSync(fd, "yyt's first fs option.");
fs.closeSync(fd);

同步代码里没有处理异常,是有隐患的,会产生阻塞。

2.2.2. 异步文件写入

没有返回值,通过回调函数将结果返回。

  • fs.open(path[, flags[, mode]], callback)
    回调函数的两个参数:
    err错误对象,没有错误则为null
    fd文件描述符。
  • fs.write(fd, string[, position[, encoding]], callback)
  • fs.close(fd[, callback])
var fs = require("fs");
var save = "异步写入";
fs.open("./learn.txt", "w", function(err, fd){
    if(!err){
        fs.write(fd, save, function(){
            if(!err){
                console.log("succeed.");
            }else{
                console.log(err);
            }
            fs.close(fd, function(){
                console.log("close.");
            });
        });
    }else{
        console.log(err);
    }
});
2.2.3. 简单文件写入
  • fs.writeFile(file, data[, options], callback) 异步
  • fs.writeFileSync(file, data[, options]) 同步
2.2.4. 流式文件写入

同步、异步、简单文件写入方式都不适合大文件写入,性能较差,容易导致内存溢出。
fs.createWriteStream(path[, options]) 创建一个可写流

var fs = require("fs");
// 创建一个可写流
var ws = fs.createWriteStream("learn.txt");
// 通过监听流的 open 和 close 事件来判断流的打开和关闭
// on 绑定一个长期的事件; once 绑定一个一次性时间,触发一次之后失效。
ws.once("open", function(){
    console.log("open a stram.");
})
ws.once("close", function(){
    console.log("close a stram.");
})
ws.write("write stram, woh~");
ws.write("write again.");
ws.write("emmmm......");
ws.end();
2.2.5. 简单文件读取
  • fs.readFile(path[, options], callback) 异步
var fs = require("fs");
fs.readFile("learn.txt", function(err, data){
    if(!err){
        console.log(data.toString());
    }else{
        console.log(err);
    }
});
  • fs.readFileSync(path[, options]) 同步
var fs = require("fs");
var data = fs.readFileSync("learn.txt");
console.log(data.toString());
2.2.6. 流式文件读取

适用于大文件,可以分多次将文件读取到内存中,一次最多读65536个字节。
fs.createReadStream(path[, options]) 创建一个可读流
读取可读流中的数据前必须为其绑定一个data事件,data事件绑定完毕后会自动开始读取数据,其回调函数中的data参数就是读到的数据。

var fs = require("fs");
// 创建一个可写流
var rs = fs.createReadStream("./imgs/bf.jpg");

rs.once("open", function(){
    console.log("open a read stram.");
})
rs.once("close", function(){
    console.log("close a read stram.");
})
rs.on("data", function(data){
    console.log("I am reading, wait me, please.");
    console.log(data);
});

复制文件:直接将可读流中的内容写入可写流中pipe()

var fs = require("fs");
// 创建一个可写流
var rs = fs.createReadStream("./imgs/bf.jpg");
var ws = fs.createWriteStream("./learn/copy_bf.jpg");
ws.once("open", function(){
    console.log("open a write stram.");
})
ws.once("close", function(){
    console.log("close a write stram.");
})
rs.once("open", function(){
    console.log("open a read stram.");
})
rs.once("close", function(){
    console.log("close a read stram.");
})
rs.pipe(ws);
2.2.7. fs模块的其他方法

现找吧还是 太多了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值