一、安装
1、去官网下载下来点击安装即可
node -v
出现相关信息,即证明安装成功
2、在node中执行文件
新建一个web-server.js文件
var http = require('http');
var serv = http.createServer(function (req,res) {
res.writeHead(200,{'Content-Type':'text/html'});
res.end('<marquee> Smashing krysliang!</marquee>');
});
serv.listen(3000)
上面代码表示创建了一个http服务器托管一个简单的html文档。并且监听3000断开。
在命令行中执行该文件
node web-server.js
然后在浏览器中输入localhost:3000,就可以看到下面的页面。
二、node.js中的JavaScript基础知识
这个部分分为两个部分,一部分是JavaScript基础(这部分我只会记一些我不太懂的,很基础的麻烦大家自己另外学习),一部分是V8中的JavaScript。
1、什么是V8?
V8 是 Google 开发的开源的 JavaScript 引擎,用于 Google Chrome 及 Chromium 中。
也就是说V8中的JavaScript的某些特性并不是所有浏览器都会支持。
2、函数中有一个属性length,此字段用来记录函数参数的个数。比如
function a(b,c) {}
console.log(a.length)//2
一些node.js的框架就是通过此属性来根据不同的参数个数提供不同的功能的。
3、reduce方法
刚开始觉得这个方法就是一个累加器,将数组中的值全部加起来,比如下面的用法
let arr = [2098,6679,45687,2340]
let reducer = function (prev,next) {
return prev+next
}
let total = arr.reduce(reducer,0)//表示初始值为0
console.log(total)//56804
其中reduce接收两个参数,第二个参数为可选的。第一个参数为必须的。
其第一个参数为一个函数,这个函数拥有下面四个参数
-
previousValue上一次函数执行返回的结果
-
currentValue当前元素的值
-
index当前元素的索引值
-
array调用reduce的数组
4、_proto_指向创建该对象的构造函数的原型。
比如,这样Dog就继承了Animal了。
function Animal(){}
function Dog(){}
Dog.prototype._proto_ = Animal.prototype
let Arr = {}
console.log(Arr.__proto__ == Object.prototype)//true
三、阻塞和非阻塞IO
1、了解一个概念
node.js是单线程的,也就是说服务器中无论有多少个请求,也是由一个主线程去处理的,所以如果在某些时候改变了全局变量的值,那么会影响之后请求的处理结果的。所以一定要慎重!
2、阻塞与非阻塞
看下面的php的代码以及对应的js的代码
print('hello')
sleep(5)
print('world')
console.log('hello')
this.timer = setTimeout(funtion(){console.log('world)},5000)
console.log('hhh')
这两段代码中,php中遇到了sleep(5)阻塞了进程的执行,也就是说进程在这里暂停了后面的代码不会继续执行,除非过了5秒。
而第二段代码中,因为事件轮询,且setTimeout是非阻塞的,所以后面的代码也会执行的。
而什么是事件轮询呢?从本质上来说,node会先注册事件,随后不停的去询问内核这些事件是否已经分发。当事件分发时,对应的回调函数就会被触发,然后继续执行下去。如果事件没有触发,则会继续执行其他代码,直到有新事件时再去执行对应的回调函数。
也就是说setTimeout仅仅只是注册了一个事件,而程序继续执行。所以这是异步的。相反,php的sleep是 同步的。
node的并发实现也采用了事件轮询,与timeout一样,所有像http、net这样的原声模块中的IO部分也都采用了事件轮询技术。node使用事件轮询,触发一个和文件描述符相关的通知。
文件描述符是抽象的句柄、存有对打开的文件、socket、管道等的引用。本质上说。当node接收到从浏览器发送的http请求的时候,底层的tcp连接会分配一个文件描述符。随后,如果客户端向服务器发送数据,node就会收到该文件描述符上的通知,然后触发JavaScript的回调函数。
3、刚刚说过,node.js是单线程的。所以在引入事件轮询的时候,对应的事件触发不一定会准时执行。
比如
var start = new Date()
setTimeout(function () {
console.log(new Date() - start)
for (var i = 0;i<1000000000;i++){}
},1000)
setTimeout(function () {
console.log(new Date() - start)
},2000)
可以看到这两个timeout事件都不是准时的,这是因为这两个事件都是在执行完主事件之后才去执行的,而第一个timeout,因为有一个很大的for循环,故会影响后一个timeout事件触发的时间。因此,JavaScript的timeout并不能严格遵守时钟设置。
所以很多优秀的node模块都不会采取类似于这样阻塞的方式(很大的for),执行任务也都采取了异步的方式。
四、node中的JavaScript
(一)global对象
在浏览器中,全局对象指的就是window对象。在window对象上定义的任何内容都可以被全局访问到。比如,setTimeout其实就是window.setTimeout,document其实是window的document。
而node中的有两个类似的代表着全局的的对象,
-
global:跟window类似,任何global对象的属性都可以被全局访问到。
-
process,所有全局执行上下文中的内容都在process对象。在node中,只有一个process对象。进程的名字是process.title。
1、process.nextTick,这个函数可以将一个函数的执行时间规划到下一个事件循环中:
console.log(1);
process.nextTick(function(){
console.log(3)
});
console.log(2);
可以把它看做,在执行完当前的其它代码,然后再执行指定的函数。
其实就等于下面这样的代码
console.log(1);
setTimeout(function(){
console.log(3)
},0);
console.log(2);
通过异步的方法在最近的将来调用指定的函数。
(二)、模块系统(这里不懂可以看我的另外一篇文章从模块化开始到webpack入门)
node引入了一种模块系统,该模块系统有三个核心的全局对象:require、module、exports
1、绝对和相对模块
绝对模块是只在node内部的node_modules这里查找的模块,或者是node内置的http这样的模块,绝对模块可以直接通过名字来require这个模块,无须添加路径名。
var http = require('http');
相对模块就是自定义的文件,比如同目录下的modulea.js文件就要像这样引入
require('./modulea.js')
2、暴露API
在模块内部使用exports来暴露模块内部的内容,将需要暴露的内容,赋给exports对象。
exports其实就是对module.exports的引用,默认情况下是一个对象。
比如modulea.js内部
exports.name = 'krysliang'
var age = 22
exports.getAge = () =>{
return age
}
或者像下面这样,将整个对象赋给module.exports
module .exports = {
name:'krysliang',
age:'22',
getAge:()=>{
return this.age
}
}
那么在index.js中就可以像下面这样使用
var modulea =require('./modulea.js')
console.log(modulea.name)
console.log(modulea.getAge())
还可以像下面这样
//module_a.js
function krysliang() {
this.name = 'krysliang'
}
krysliang.prototype.getAge = function () {
console.log('我的年龄是',22)
}
krysliang.prototype.getName = function () {
console.log('我的名字是',this.name)
}
module.exports = krys
//index.js
var Krysliang =require('./module_a.js')
var lwl = new Krysliang()
lwl.getName()
lwl.getAge()
这就是典型的OOP写法了。
(三)、事件
node.js中基础的API之一就是EventEmitter,也就是事件分发器,用它来分发事件处理,就是相当于一个监听器。node暴露了 Event EmitterAPI,在该API中定义了on、emit以及removeListener方法。它以process.EventEmitter形式暴露出来
var EventEmitter = require('events').EventEmitter,a = new EventEmitter();
a.on('event',()=>{console.log('事件被触发啦')})
a.emit('event')
事件是Node非阻塞设计的重要体现。node通常不会直接返回数据(因为这样可能会在等待某个资源的时候发生线程阻塞),而是采用分发事件来传递数据。
比如像HTTP服务,当请求到达的时候,Node会调用一个回调函数,这个时候数据有可能还没有完全到达
http.Server(funtion(req,res){
var buf = '';
req.on('data',funtion(){buf+=data })
req.on('end'.funtion(){ console.log('数据接收完毕')})
})
将请求数据内容进行缓存(data事件),等到所有数据都接收完毕后(end事件)在对数据进行处理。不管是否所有的数据都已经到达,node为了让你尽快知道请求已经到达服务器,都需要分发事件出来。
有些API会分发error事件。
不管某事件在将来会被触发多少次,我都希望只调用一次回调函数,这样的分发方法叫做once,比如
a.once('某个事件',function(){
})
(四)buffer
buffer是一个表示固定内存分配的全局对象(也就是说,要放到缓冲区中的字节数需要提前定下),可以有效在JavaScript中表示二进制数据。
该功能的一部分中功能就是可以对数据进行编码转换。
比如
var myBuffer = new Buffer('==iiij2i3h1i23h','base64');
将第一个数据作为base编码方式进行缓存
五、Node中重要的API。
node通过回调和事件机制来实现并发。
构建一个简单的命令行文件浏览器,其功能是允许用户读取和创建文件。
需求:
(1)程序需要在命令行运行,也就是程序要么通过node命令来执行或者是直接执行后通过终端提供交互给用户进行输入输出。
(2)程序启动后,需要显示当前目录下列表
(3)选择某个文件时,程序需要显示该文件内容。
(4)选择一个目录时,程序需要显示该目录下的信息
(5)运行结束后,程序退出。
步骤
(1)创建模块
(2)决定采用同步的fs还是异步的fs
(3)理解什么是流(stream)
(4)实现输入输出
(5)重构
(6)使用fs进行文件交互
(7)完成
1、创建模块
在文件目录下面创建一个package.json的文件,然后执行npm install
{
"name": "file-explorer",
"version": "0.0.1",
"description": "a command-file file explorer"
}
2、fs模块
需要注意的是,在node中,唯一一个提供同步和异步Api的模块就是fs模块。
比如,获取当前目录的文件列表中,同步的方法是readdirSync,异步的方法是readdir
//同步
console.log(require('fs').readdirSync(__dirname))
//异步
require('fs').readdor('.',async)
首先在node.js中, 标准的输入/输出管理模块stdioApi是全局对象process的一部分,所以在这里我们唯一的依赖就是fs模块,
//引入fs模块
var fs = require('fs')
为了获取文件列表,采用fs.readdir(此处采用异步)
fs.readdir(__dirname,function (err,files) {
//回调函数中的files是一个数组,用来显示当前目录下的文件列表
console.log(files)
})
然后执行文件,可以看到,当前目录下的文件名被打印出来。
3、理解什么是流
console.log会输出到控制台,事实上,console.log内部做了这样的事情:它在指定的字符串后面加上\n换行符,并将其写到stdout流中。
console.log可以使用process.stdout.write来替代,但是需要注意的是。process.stdout.write是不会在原来的字符串后面加上换行符的。
process全局对象中包含了三个流对象,分别对应三个UNIX标准流
-
stdin标准输入(可读流)
-
stdout标准输出(可写流)
-
stderr标准错误(可写流)
stdin流默认的状态是paused(暂停的。
此外,流的另一个属性是它默认的编码,如果在流上面设置了编码,那么就会得到编码后的字符串而不是原始的Buffer作为事件参数。
在node中,会接触到各种类型的流,比如TCP套接字、http请求等。简而言之,当涉及持续不断地对数据进行读写的时候,流就出现了。
4、输入和输出
这里用到的输入输出函数有如下几个
stdout.write(' \033[33mEnter your choice: \033[39m');//输出