运行
运行方式:
> node .\test.js
模块
CommonJS是模块化的标准,NodeJS是CommonJS的实现。
在Node中,一个JS文件就是一个模块。
模块分为“核心模块”和“文件模块”两大类:
核心模块:由node引擎提供的模块
文件模块:由用户自己创建的模块
引入其它模块规则:
核心模块引入:require()函数中直接写模块名
文件模块引入:绝对路径引入时,require()函数中写模块绝对路径;相对路径引入时,require()函数中写模块相对路径(须以‘./’或‘../’开头)
require ("modulepath");
// 上面只是引入了模块,但要使用模块内的内容,肯定得拿到对应模块的模块对象
// 下面语句展示了require()函数返回的模块对象,并通过模块对象去引用模块内容
var md_fs = require ("fs");
// 注意:文件模块引入时,若是引用相对路径,须以 ‘./’ 或 ‘../’ 开头
var md_md1 = require("./module1.js");
每个JS文件中的JS代码都是独立运行在一个作用域中,而不是全局作用域。
- 模块内声明全局变量:去掉 var
m = 1; //作用域是全局
var n = 2; //作用域是模块内
- Node中有一个全局对象global,声明的全局变量或方法都被管理在global里。
- 每个模块里的代码实际上都是包装在函数中运行的,通过console.log(arguments);可查看。
arguments有一个callee属性,保存的是当前执行的函数对象。
执行console.log(arguments.callee + "");可以将当前运行的函数(模块)内容打印出来。
在函数执行时,同时传递了5个实参:
exports - 对象,用于将变量或方法暴露到模块外
require - 函数,用来引入外部的模块
module - 代表当前模块本身,exports就是module的属性。(所以在导出时,既可以使用exports,也可以使用module.exports)
-- 使用exports例子:
exports.属性 = 属性值;
exports.方法 = 函数;
-- 使用module.exports例子:
module.exports.属性 = 属性值;
module.exports.方法 = 函数;
module.exports = {
属性 = 属性值;
...
方法 = 函数;
...
};
__filename - 当前模块的完整路径
__dirname - 当前模块所处文件夹
所以一个模块中的变量和函数在其它模块中无法直接访问。
下面举例演示:
/// module1.js
console.log("i am modul.1");
var x = 10;
var y = 20;
/// test.js
var md1 = require("./module1.js");
console.log(md1);
console.log(md1.x);
console.log("********************")
运行结果:
i am modul.1
{}
undefined
********************
为解决作用域问题,引入了exports。可以将需要暴露给外部的变量或方法设置为exports属性。
/// module1.js
console.log("i am modul.1");
var x = 10;
var y = 20;
exports.x = "i am x in module.1";
exports.fn = function(){
};
exports.divide = function(a, b) {
return a / b;
};
/// test.js
var md1 = require("./module1.js");
console.log(md1);
console.log(md1.x);
console.log(md1.divide(10, 2));
console.log("********************")
运行结果:
i am modul.1
{ x: 'i am x in module.1', fn: [Function (anonymous)] }
i am x in module.1
5
********************
注意:上边module1.js中,var x与exports.x是两个完全不同的变量。
总之,需要对外开放的变量或方法,使用exports进行修饰;而不需要对外开放只限于模块内使用的还是使用var声明。
npm
npm简介
npm(Node package manager),是帮助Node完成第三方模块的发布、安装、依赖等的包管理器。就好比Node的软件管家。
npm指令
- npm :帮助说明
- npm version :查看npm所有模块版本
- npm -v :查看npm版本
- npm search [packagename] :搜索模块包
- npm install [packagename] :在当前目录安装包
- npm install [packagename] --save :将包下载到项目根目录下的node_modules内,并添加在依赖文件package.json中的dependencies属性下。(?但新版本的npm似乎默认自动加了--save)
- npm install [packagename] -g :在全局模式安装包(npm安装路径)
- npm remove [packagename] :删除(/卸载)一个模块包
- npm init :在当前目录下初始化package.json
- npm install [filepath] :从本地安装包
- npm install [packagename] -registry=address :从镜像源安装包
- npm config set registry address :设置镜像源
Buffer
Buffer结构和操作方法都跟数组很像,但性能优于数组,是专门用于存储二进制数据的数据结构。Buffer的元素(内存中的一个Byte)显示时为16进制的两位数。
Buffer中的内存实际并不是通过JavaScript分配的,而是在底层通过C++申请,底层申请的内存空间是地址连续的。
Buffer的大小一旦确定,则不能更改。
// 创建一个Buffer,同时申请10Byte的内存空间
var buf3 = Buffer.alloc(10);
console.log(buf3, ":", buf3.length);
// 通过索引操作buf中的元素
buf3[0] = 88; //直接赋值十进制元素
buf3[1] = 255; //一个字节占8位,范围为00000000 ~ 11111111。十进制255对应的二进制为11111111,对应十六进制为ff
buf3[2] = 256; //十进制256对应的二进制为100000000,而buffer一个元素最大占8位,故只会取后八位00000000,对应十六进制为00
buf3[3] = 0xaa;
buf3[10] = 88; //buffer申请的内存空间为10Byte,最大索引位为9。给第10号索引位赋值为无效操作
console.log(buf3, ":", buf3.length);
/// 运行结果
<Buffer 00 00 00 00 00 00 00 00 00 00> : 10
<Buffer 58 ff 00 aa 00 00 00 00 00 00> : 10
// ---------------------------------------------------
var str1 = "Hello gaoxing";
var str2 = "Hello 高兴";
var buf1 = Buffer.from(str1); //将字符串转换为buffer
var buf2 = Buffer.from(str2);
console.log(str1, ":", str1.length); //字符串的长度
console.log(str2, ":", str2.length);
console.log(buf1, ":", buf1.length); //buffer占用内存的大小
console.log(buf2, ":", buf2.length);
console.log(buf2.toString()); //将缓冲区内数据转换成字符串
/// 运行结果
Hello gaoxing : 13
Hello 高兴 : 8
<Buffer 48 65 6c 6c 6f 20 67 61 6f 78 69 6e 67> : 13
<Buffer 48 65 6c 6c 6f 20 e9 ab 98 e5 85 b4> : 12
Hello 高兴
fs模块
// fs - 文件系统
var md_fs = require("fs");
// 检测文件是否存在
var bExists = md_fs.existsSync("hello.txt");
console.log(bExists);
// 同步操作
var fd = md_fs.openSync("./hello.txt", "w");
md_fs.writeSync(fd, "今天下雨了!");
md_fs.closeSync(fd);
// 异步操作
md_fs.open("hello.txt", "w", function(err, fd) {
if (!err) {
console.log("file open ok.");
md_fs.write(fd, "异步方式写入文件~", function(err) {
if (!err) {
console.log("file write ok.")
}
md_fs.close(fd, function(err) {
if (!err) {
console.log("file close ok.")
}
});
});
} else {
console.log("file open failed!", err);
}
});
// --------------------------
/// 简单文件写入
// asynchronous
md_fs.writeFile("hello.txt", "这是简单文件写入~", function(err) {
if (!err) {
console.log("simple write file by asynchronous ok.");
} else {
console.log("simple write file by asynchronous failed!");
}
});
md_fs.readFile("hello.txt", function(err, data) {
if (!err) {
console.log(data.toString());
}
});
// synchronize
md_fs.writeFileSync("hello.txt", "这是简单文件写入~");
// --------------------------
/// 流式文件写入
//同步、异步、简单文件的写入都不适合大文件的写入,性能较差,还容易导致内存溢出。
var ws = md_fs.createWriteStream("hello.txt");
ws.once("open", function() { //绑定一个一次性的事件
console.log("stream is open")
});
ws.once("close", function() {
console.log("stream is close")
});
ws.write("流式写入文件内容1");
ws.write("流式写入文件内容2");
ws.write("流式写入文件内容3");
//ws.close(); //这种方式可能导致流还没写完就关闭了流式句柄
ws.end(); //这种方式能正常的等流写完了再关闭流式句柄
// 流式文件读取也适合于大文件
// 创建一个可读流
var rs = md_fs.createReadStream("hello.txt");
rs.once("open", function() {
console.log("read stream open");
});
rs.once("close", function() {
console.log("read stream close");
});
// 如果要读取一个可读流中的数据,必须要为可读流绑定一个data事件,待data事件绑定完毕,会自动开始读取数据
rs.on("data", function(data) { //注意:对于大文件会分为多次读取,每次读取上限是65536字节
console.log(data);
});
// --------------------------
/// 下面演示如何将一个可读流和可写流串起来(通过管道)
var rs = md_fs.createReadStream("hello.txt");
rs.once("open", function() {
console.log("read stream open");
});
rs.once("close", function() {
console.log("read stream close");
});
var ws = md_fs.createWriteStream("hello1.txt");
ws.once("open", function() {
console.log("write stream open")
});
ws.once("close", function() {
console.log("write stream close")
});
rs.pipe(ws); //通过管道流式读写完后会自动关闭流句柄