一、概览 1、html可以在浏览器直接打开运行,是因为浏览器是html文件的解析器 而js文件不能在浏览器直接运行,是因为浏览器不支持解析js文件,需要有一个解析器来解析js文件 可以下载安装nodejs解析器,并且里面自带了npm,通过node.exe可以直接运行js文件,是通过命令行的形式运行的 要运行某个js文件,直接node + 要运行的js文件名称或者直接是js代码,按回车即可

js操作的是浏览器,node操作的是电脑系统、文件等等

2、编辑器也可以直接运行,推荐使用webstorm

3、在Ecmascript部分,node和js其实是一样的,比如数据类型的定义、语法结构、内置对象 在js中的顶层对象:window 在node中的顶层对象:global,没有window这个概念

二、模块 es6通过import export来进行模块化 nodejs通过require来进行模块化 html通过script标签来引入js文件 1、在nodeJs里面,一个js文件就是一个模块,每个模块都有自己的作用域, 我们使用var声明的变量,不是全局的,而是属于当前模块下的 比如:1.js var a = 100; console.log(a); //100 console.log(global.a); //undefined ,不是全局下面的变量 global.a = 200; console.log(a); //100 仍然是开始定义的100 console.log(global.a); //这时候才是200

2、__filename:表示当前文件的绝对路径 __dirname:表示当前文件所在目录的绝对路径 它们并非全局的,而是模块作用域下的 所以global.__filename是访问不到的,返回undefined console.log(__filename); //E:\webstorm文件夹\index.js console.log(__dirname); //E:\webstorm文件夹

3、模块路径 require(); //可以是绝对路径、相对路径 不能直接是require("1.js")这种不带./的,这种加载方式,会加载node中的核心模块,或者是node_modules里面的慕课

4、查找文件: 首先按照加载的模块的文件名进行查找; 如果没有找到,则会在模块文件名称后面自动加上.js的后缀,然后进行查找; 如果还没有找到,则会在模块文件名称后面自动加上.json的后缀,然后进行查找; 如果还没有找到,则会在模块文件名称后面自动加上.node的后缀,然后进行查找; 如果还没有找到,就会报错。

5、 在一个模块中,通过var定义的变量,其作用域范围是当前模块,外部不能够直接访问到; 如果我们想一个模块能够访问另一个模块中定义的变量,可以通过:

  • 把变量作为global对象的一个属性(不推荐) global.a = 100;
  • 使用模块对象 module,这个对象不是全局的,而是每个模块下的对象 模块对象 module用于:保存提供和当前模块有关的一些信息,下面有很多属性: exports:{}; //module的子对象,通过这个对象可以把一个模块中的局部变量暴露出去,供外部访问 因为exports也是一个对象,所以暴露变量出去之前,先声明一个key值,value值就是这个局部变量
var a = 100;
module.exports.name = a;
require();  //返回值就是module.exports对象

6、在模块作用域下,还有一个内置的模块对象,exports,它其实就是module.exports 所以: module.exports.name = a; 可以写成 exports.name = a; 【注意】:不要这样操作 module.exports = [1,2,3] 也不要:exports = [1,2,3],只是追加属性,不要修改

三、global对象 全局对象 局部: __filename __dirname 全局: 日期对象 var d = new Date() 数组 var arr = new Array(1,2,3) 定时器

三-1、process对象(进程对象)——全局对象 process === global.process //true 可以在任何地方都能访问到,通过这个对象提供的属性和方法,使我们可以对当前运行的程序的进程进行访问和控制 1、process.argv console.log(process.argv); // [ 'D:\Program Files\nodejs\node.exe', 'E:\webstorm文件夹\2.js' ] 第一个是解析器,第二个是js文件 如果带上参数,那么返回的数组里面就会包含你运行时带入的参数 举例: node process.argv a=1 // [ 'D:\Program Files\nodejs\node.exe', 'E:\webstorm文件夹\2.js','a=1' ] 2、process.execPath 开启当前进程的这个可执行文件的绝对路径 3、env 返回用户环境信息 4、version 返回node版本信息 5、versions 返回node以及node依赖包版本信息 6、pid 返回当前进程的pid 进程是node.exe 7、title 返回当前进程的显示名称(Getters/Setters) 8、arch 返回当前CPU处理器架构 arm/ia32/x64 9、platform 返回当前操作平台 10、cwd() 返回当前进程的工作目录 11、chdir(directory) 改变当前进程的工作目录 12、memoryUsage() 返回node进程的内存使用情况,单位是byte 12、exit(code) 退出 13、kill(pid) 向进程发送信息 14、IO a、stdin、stdout:标准输入输出流(IO) 标准输入设备:向计算机输入数据和信息的设备,是计算机与用户或其他设备通信的桥梁。 例如:鼠标、键盘、摄像头等等 标准输出设备:是计算机硬件系统的终端设备,把计算机数据或信息以数字、字符、图像、声音等形式表现出来。例如:显示器、打印等等 stdin、stdout提供了操作输入数据和输出数据的方法,我们也通常称为IO操作 stdin:标准输入流 stdout:标准输出流 console.log(...) === process.stdout.write(...);

b、默认情况下,输入流是关闭的,要监听处理输入流数据,首先要开启输入流

//开启
process.stdin.resume();
监听用户的输入数据,输入完成按下回车
process.stdin.on('data',function(chunk){
	console.log('用户输入了:'+chunk);
})

会一直监听,用户输入完成按下回车显示数据,再按下仍然会显示第二次输入的数据

四、Buffer类:类似数组 1、定义:一个用于更好的操作二进制数据的类 我们在操作文件或者网络数据的时候,其实操作的就是二进制数据流;Node为我们提供了一个更加方便的去操作这种数据流的类Buffer,他是一个全局的类,global.Buffer === buffer

JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。 但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。 在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库。Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 Node.js 中处理I/O操作中移动的数据时,就有可能使用 Buffer 库。原始数据存储在 Buffer 类的实例中。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。

  • Buffer.alloc(size); 创建一个Buffer对象,并且为这个对象分配一个空间大小,大小是size的8位字节;当我们为一个buffer对象分配了一个空间大小以后,其长度是固定的,不能更改。也就是后续不能再增加内容了 <Buffer 72 75 6e 6f 6f 62> //控制台显示的是16进制的

  • Buffer.from(array): 返回一个被 array 的值初始化的新的 Buffer 实例(传入的 array 的元素只能是数字,不然就会自动被 0 覆盖),声明并赋值

  • Buffer.from(string[, encoding]): 返回一个被 string 的值初始化的新的 Buffer 实例,encoding默认是utf8

let bf2 = Buffer.from('miaov'); //bf2.length 5 返回的是字节数,不是字符串长度 let bf3 = Buffer.from('妙味'); //bf3.length 6 一个汉字占据3个字节

2、Buffer类方法

  • bf.length; //buffer的字节长度
  • bf[ index ]; //获取或者设置在index索引位置的8位字节内容
  • bf.write(string , [offset] , [length] , [encoding]); //根据参数offset偏移量和指定的encoding编码方式,将参数string写入数据写入buffer
  • bf.toString( [encoding],[start],[end] ); //根据encoding返回一个解码的string类型
  • bf.toJSON(); //返回一个JSON表示的Buffer实例,JSON.stringify将会默认调用来字符串序列化这个Buffer实例
  • bf.slice( [start],[end] ); //返回一个新的buffer,这个buffer将会和老的buffer引用相同的内存地址; 注意:修改这个新的buffer实例slice切片,也会改变原来的buffer
  • bf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])
let bf2 = Buffer.from('miaov');
console.log(bf2);  //<Buffer 6d 69 61 6f 76>  //十六进制
console.log(bf2.toString());  //'miaov'
for (let i=0;i<bf2.length;i++){  //m的十六进制是6d;十进制是109;二进制是0110 1101
     console.log(bf2[i]);  //109 105 97 111 118  十进制
     console.log(String.fromCharCode(bf2[i]))  // m i a o v  图形字符
}
let bf = Buffer.alloc(5);
let str = 'miaov';
//bf.write(str);
//console.log(bf);  //<Buffer 6d 69 61 6f 76>
// bf.write(str,1);
// console.log(bf);  //从buffer第1位开始写入,第0位用00补上,<Buffer 00 6d 69 61 6f>
bf.write(str,1,3);
console.log(bf);  //只写入三位,其他位用00补上,<Buffer 00 6d 69 61 00>
console.log(bf.toJSON());  //{ type: 'Buffer', data: [ 0, 109, 105, 97, 0 ] }

3、Buffer中的静态方法(又叫类方法,即不需要实例化即可使用的方法)

  • Buffer.isEncoding(encoding):如果给定的编码encoding是有效的,返回true;否则返回false
  • Buffer.isBuffer(obj); 测试这个obj是否是一个Buffer
  • Buffer.byteLength(string,[encoding]):将会返回这个字符串的真实byte字节长度。encoding默认编码是utf8
let str1 = 'json';
console.log(str1.length);  //4 字符长度
console.log(Buffer.byteLength(str1));  //4 字节长度
  • Buffer.concat(list,[totalLength]):返回一个保存着将传入Buffer数组中所有buffer对象拼接在一起的buffer对象 list是buffer对象,返回值是多个buffer对象拼接成的一个buffer对象 totalLength是拼接后的字节长度,不传是计算后的所有的字节长度,(建议预先设置,节省计算时间)

五、文件系统模块 File System 1、该模块是核心模块,需要使用require导入后使用:let fs = require('fs'); 2、该模块提供了操作文件的一些API

  • 异步的打开一个文件: fs.open(path,flags,[mode],callback) path:要打开文件的路径; flags:打开文件的方式:读/写 mode:设置文件的模式:读/写/执行 callback:回调函数,接受2个参数:err、fd err:文件打开失败的错误保存在err里面,如果成功err为null fd:被打开文件的标识,和定时器类似,用于后续要操作文件的标识,即后续如果要操作某个打开的文件,把这个文件标识传到后续操作的某些方法里面去,即可知道操作的是当前文件

  • fs.open()的同步版: fs.openSync(path,flags,[mode])

  • 从指定的文档标识符fd读取文件数据 fs.read(fd,buffer,offset,length,position) fd:通过open方法成功打开一个文件返回的编号 buffer:buffer对象,用于存储打开文件内部的信息 offset:偏移量,数据从buffer对象里面的第几位开始存储,起始是0 length: 读取的内容长度 position:从文件的第几位开始读取,默认是0,即从头开始 callback:接受三个参数,err错误信息、length返回的新的buffer对象的长度;newBf:返回的新的buffer对象

  • fs.read的同步版本,返回bytesRead的个数 fs.readSync(fd,buffer,offset,length,position,callback)

*通过文件标识fd,向指定的文件中写入buffer fs.write(fd,buffer,offset,length,[position],callback) 同步版本:fs.writeSync(fd,buffer,offset,length,[position]) fd:通过open方法成功打开一个文件返回的编号 buffer:要写入到文件的数据 offset:从buffer对象中第几位开始写入数据,默认是0 length: 要写入的buffer数据的长度 position:向指定的文件的第几位开始写入buffer数据,默认是0,即从头开始写入数据,如果开头已经有数据,那么新的数据会覆盖原始数据 callback:接受三个参数,err错误信息、length写入数据的buffer对象的长度;newBf:写入数据的buffer对象

*通过文件标识fd,把data写入到文档中,如果data不是buffer对象的实例,则会把值强制转换成为一个字符串 fs.write(fd,data,[position],[encoding],callback) 同步版本:fs.writeSync(fd,data,[position],[encoding]) data:直接是一个字符串,此时不再从第几位写入了,而是全部写入到文件里,所以没有offset参数

  • 关闭一个打开的文件 fs.close(fd,callback) 同步版本:fs.closeSync(fd)

六、更好用的文件系统方法 1、fs.writeFile(filename,data,[options],callback) 异步的将数据写入一个文件,如果文件不存在就新建,如果文件原先存在,则会被替换;data可以是一个string,也可以是一个原生buffer 同步版本:fs.writeFileSync(filename,data,[options])

2、fs.appendFile(filename,data,[options],callback) 异步的将数据添加到一个文件的尾部,如果文件不存在就新建,如果文件原先存在,则会将数据追加到文件的尾部;data可以是一个string,也可以是一个原生buffer 同步版本:fs.appendFileSync(filename,data,[options])

3、fs.readFile(filename,[options],callabck) 异步读取一个文件的全部内容 同步版本:fs.readFileSync(filename,[options])

4、fs.exists(path,callback) 检查指定路径的文件或者目录是否存在 同步版本:fs.existsSync(path)

5、fs.unlink(path,callback) 删除一个文件 同步版本:fs.unlink(path)

let fs = require('fs');
let filename = '2.txt';
//异步
if(fs.exists(filename,(err)=>{
    if(!err){
        fs.writeFile(filename,'hello',(err)=>{
            if(err){
                console.log("文件创建失败")
            }else{
                console.log("文件创建成功")
            }
        })
    }else{
        fs.appendFile(filename,'-world',(err)=>{
            if(err){
                console.log('文件追加失败')
            }else{
                console.log('文件追加成功')
            }
        })
    }
}));
//同步
if(!fs.existsSync(filename)){
    console.log('文件创建成功');
    fs.writeFileSync(filename,'hello')
}else{
    console.log('文件追加成功');
    fs.appendFileSync(filename,'-world')
}

6、fs.rename(oldPath,newPath,callback) 重命名 同步版本:fs.renameSync(oldPath,newPath)

7、fs.stat(path,callback) 读取文件信息的状态,不是文件内部的内容,返回值是一个json,包括文件类型、文件日期、文件大小等等 mode=33206表示该文件是文件类型,mode=16822表示该文件是文件夹 同步版本:fs.statSync(path)

8、fs.watch(filename,[options],[listener])
观察指定路径的改变,filename可以是文件或者目录 //listener是一个监听器,回调函数,有2个参数:事件event和被监听文件的名称filename

9、fs.mkdir(path,[mode],callback) //mode模式,是只读还是读写模式 创建文件夹 同步版本:fs.mkdirSync(path,[mode])

10、fs.readdir(path,callback) 读取文件夹 同步版本:fs.readdirSync(path) 回调函数包括2个参数,err错误信息和当前文件夹下面所有的文件和文件夹信息fileList

11、fs.rmdir(path,callback) 删除文件夹 同步版本:fs.rmdirSync(path)

七、使用node进行web开发 1、用户上网流程 用户输入https://www.baidu.com/ 通过浏览器发送https请求(类似于打电话拨号),请求baidu.com,这个域名可以转换为ip地址 通过ip定位,到网络中指定的一台机器上,然后该机器会接收到该用户发送过来的请求,然后对该请求进行处理,返回对应的内容

简言之: 用户通过浏览器发送一个http请求到指定的服务器 服务器接收到该请求,对该请求进行分析和处理 服务器处理完成之后,返回对应的数据到用户机器 浏览器接受到服务器返回的数据,并根据接收到的数据进行分析和处理

web开发:就是两台机器之间的数据交互,客户端与服务端的交互 由客户端发送一个http请求到指定的服务端 ——> 服务端接收并处理请求 ——> 返回数据到客户端,类比迅雷下载 http协议:

要进行web开发,首先就必须处理关于服务端接收请求的这一过程,所以要搭建服务器,该服务器可以接收来自任何客户端的链接和请求,然后进行对应处理

2、http模块 let http = require('http'); 使用该模块可以进行服务器的搭建

server.listen(); //如果不传参数,默认系统会自动分配端口号,但是不建议,要主动设置端口号及主机名

res.writeHead( 200,'',{ //200,默认返回ok content-type : 'text/html;charset=utf-8' //会将返回信息当成html输出到页面,html标签起作用 content-type : 'text/plain' //会将返回信息当成文本文件输出到页面,html标签不起作用 } )

/*
* 搭建一个http的服务器,用于处理用户发送的http请求
* 需要使用node提供一个模块  http
* */
//加载一个http模块
let http = require('http');
//通过http模块下的createServer创建并返回一个web服务器对象
let server = http.createServer();
server.on('error',(err)=>{
    console.log(err)
})
server.on('listening',()=>{
    console.log('linstening...')
})
server.on('request',(req,res)=>{  //req是客户端发送的数据,res是服务端返回的数据
    console.log('有客户端请求了')
    //console.log(req);  //req里面有请求头、域名后面附带的路径信息/a/index.html、method方式
    //console.log(res);  //res里面包括response正文信息(即网页上显示的内容)以及正文之外的一些信息,比如返回头信息(网页上不可见)
    res.writeHead('200','',{
        'content-type':'text/html;charset=utf-8'
    })
    // res.write('hello');
    // res.end();
    //可以合并为一步
    res.end('hello');
})
server.listen(8080,'localhost');

3、url模块

let url = require('url'); url.parse(); //专门用于解析url路径,返回json格式数据

4、使用querystring模块方法对get和post提交的数据进行处理

let qs = require('querystring'); get方式发送数据 qs.parse('foo=bar&abc=xyz&abc=123'); //{foo: 'bar',abc: ['xyz', '123']}

post方式发送数据

let http = require('http');
let url = require('url');
let fs = require('fs');
let qs = require('querystring');
let server = http.createServer();
let filename = __dirname + '/html/';
server.on('error',(err)=>{
    console.log(err)
})
server.on('listening',()=>{
    console.log('listening')
})
server.on('request',(req,res)=>{
    var urlStr = url.parse( req.url );  //返回一个对象,里面的pathname属性和req.url一样
    switch (urlStr.pathname) {
        case '/':
            getData(filename+'index.html',req,res);
            break;
        case '/user':
            getData(filename+'user.html',req,res);
            break;
        case '/login':
            getData(filename+'login.html',req,res);
            break;
        case '/login/check':
            /*console.log(req.method);  //get
            console.log(urlStr.query);  //username=xiaoxiao&password=1
            console.log(qs.parse(urlStr.query));  //[Object: null prototype] { username: 'xiaoxiao', password: '1' }*/
            if(req.method.toUpperCase() === 'POST'){
                let str = '';
                req.on('data',(chunk)=>{
                    str += chunk;
                })
                req.on('end',()=>{
                    console.log(str);  //username=13795232495&password=111
                    console.log(qs.parse(str));  //[Object: null prototype] { username: '13795232495', password: '111' }
                })
            }
            break;
        default:
            break;
    }
})
function getData(filename,req,res) {
    fs.readFile(filename,(err,data)=>{
        //console.log(data.toString());  //输出页面内的全部标签内容
        if(err){
            res.writeHead(404,'',{
                'content-type':'text/html;charset=utf8'
            })
            res.end('404')
        }else{
            res.writeHead(200,'',{
                'content-type':'text/html;charset=utf8'
            })
            res.end(data)
        }
    })
}
server.listen(8082,'localhost');
/*
* 搭建一个http的服务器,用于处理用户发送的http请求
* 需要使用node提供一个模块  http
* */
//加载一个http模块
let http = require('http');
let url = require('url');
//通过http模块下的createServer创建并返回一个web服务器对象
let server = http.createServer();
server.on('error',(err)=>{
    console.log(err)
})
server.on('listening',()=>{
    console.log('linstening...')
})
server.on('request',(req,res)=>{  //req是客户端发送的数据,res是服务端返回的数据
    console.log('有客户端请求了')
    console.log(req.url);  //req里面有请求头、域名后面附带的路径信息/a/index.html、method方式
    //console.log(res);  //res里面包括response正文信息(即网页上显示的内容)以及正文之外的一些信息,比如返回头信息(网页上不可见)
    switch (req.url) {
        case '/':
            res.writeHead(200,'',{
                'content-type':'text/html;charset=utf-8'
            })
            res.end('这是首页');
            break;
        case '/user':
            res.writeHead(200,'',{
                'content-type':'text/html;charset=utf-8'
            })
            res.end('这是用户页');
            break;
        default:
            res.writeHead(404,'',{
                'content-type':'text/html;charset=utf-8'
            })
            res.end('404');
            break;
    }

    // res.write('hello');
    // res.end();
    //可以合并为一步
    //res.end('hello')

})
server.listen(8080,'localhost');

【注意】: 1、所谓异步,就是执行成功了之后会执行一个回调函数,下面的代码会继续执行,即使报错了也不妨碍下面代码的执行;同步,就是成功了就继续执行下面的代码,报错了就卡在这儿了,下面的代码也就执行不了了。 2、所谓的参数filename,包括文件的名称和路径(如果有路径的话),比如在task文件夹下创建index.html 那么filename='task/index.html' 要包括文件的名称,不能仅仅是一个空路径,'task/'