一、Node.js 下载
Node 是一个工具,安装方式看官网即可 → https://nodejs.org/zh-cn/download/
$ node --version
v10.19.0
npm 是 Node 生态下的包管理器,它有一个替代品 yarn,与前者比速度更快,去官网下载即可
$npm --version
6.13.4
$yarn --version
1.22.0
二、Node.js 特点
1. 共享状态并发
Node 为 JavaScript 引入了一个复杂的概念:共享状态并发
Node 采用一个长期运行的进程,相反,Apache 会产生多个线程(每个清切一个线程),每次刷新状态
(能力越大,责任越大)
2. 非阻塞 IO
// PHP
print('Hello')
sleep(5)
print('World')
print('xxx')
// Node
console.log('Hello')
setTiemout(function(){
console.log('World')
},5000)
console.log('xxx')
PHP 的 sleep() 阻塞了线程的执行。当程序进入睡眠时,就什么事情也不做了
Node 使用时间轮询,因此这里的 setTimeout 是非阻塞的,即 xxx 在 Hello 打印出后也会立即打印
3. 单线程(如何处理高并发?)
事件轮询是 Node IO 的基础核心
许多优秀的模块都是非阻塞的,执行任务也都采用异步的方式
当一个函数执行时,统一事件不可能有第二个函数也在执行,如何处理高并发?
- Node 的最大并发量是1,没错,Node 并不提供真正的并行操作,因为那样需要引入更多的并行执行线程
- 关键在于,在调用栈执行非常快的情况下,同一时刻无需处理多个请求
所以说 V8 搭配非阻塞 IO 是最好的组合
- V8 执行 JavaScript 速度非常快
- 非阻塞 IO 确保了单线程执行时不会因为有数据库访问或硬盘访问等操作而导致被挂起
三、Node 中的 JavaScript
1. global对象 和 process对象
浏览器中全局对象是 window,Node 中,有两个类似却各自代表着不含义的对象
- global:任何 global 对象上的属性都可以被全局访问到
- 浏览器中的 window 上的属性可以被全局访问到
- Node 中的 global 上的属性可以被全局访问到
- process:所有全局执行上下文中的内容都在 process 对象中
- 浏览器窗口名字 window.name
- Node 进程的名字是 process.title
2. 模块系统
JavaScript 语言标准中未为模块依赖以及模块独立定义专门的 API
因此在浏览器侧,引入多个模块会出现对全局命名空间的污染及命名冲突的问题
而在 Node 侧,内置了很多使用的模块作为基础工具集,并能够通过 npm 安装更多的模块
安装模块和引入模块的方法如下,引入时候可以直接通过名字,而无需添加路径
// 安装模块
$ yarn add colors
// 引入模块
require('colors')
我们还可以自定义模块,引入时要添加路径
// module_a.js
console.log('This is a.')
// module_b.js
console.log('This is b.')
// main.js
require('module_a')
让模块暴露一个 API 成为 require 调用的返回值,依靠 module 和 exports 这两个全局变量
默认情况下,每个模块暴露一个空对象,我们可以继续把它当对象使用,也可以改写
// 对象形式
module.exports = {
"小明":100,
"小白":99
}
module.exports = function(){
console.log('这次的模块是一个函数哦')
}
3. 事件
Node.js 中的基础 API 之一就是 EventEmmitter。
浏览器中负责处理事件相关的 DOM API 主要包括 addEventListener、removeEventListener 以及 dispatchEvent
Node 中,暴露了 Event Emmitter API,该 API 上定义了 on、emmit 以及 removeListener
// eventemitter/index.js
const EventEmitter = require('event').EventEmitter
const a = new EventEmitter
a.on('event',function(){
console.log('event called')
})
a.emit('event')
// Node 内部使用
let EventEmitter = process.EventEmitter
let MyClass = function(){}
MyClass.prototype.__proto__ = EventEmitter.prototype
let a = new MyClass
a.on('xx',function(){
// do something
})
用户提交表单时,通常监听请求的 data 和 end 事件
http.Server(function(req,res){
let buf = ''
req.on('data',function(data){
buf += data
})
req.on('end',function(){
console.log('数据接收完毕')
})
})
这是 Node.js 中很常见的例子:将请求数据内容进行缓冲(data事件),等到所有数据都接收完毕后(end事件)再对数据进行处理
在 Node 中,事件机制就是一个很好的机制,能够通知你尚未发生但即将发生的事情
不管某个事件在将来触发多少次,我都希望只调用一次回调函数,Node 为这类需求提供了一个简洁的方法
a.once('oneEvent',function(){
// 不管你触发多少次,我只执行一次
})
四、buffer
除了 模块之外,Node 还弥补了语言另外一个不足之处,那就是对二进制数据的处理。
buffer 是一个表示固定内存分配的全局对象(也就是说,要放到缓存区中的字节数需要提前定下)
// buffers/index.js
const mybuffer = new Buffer('==ii1j2i3h1i23h','base64')
console.log(mybuffer)
require('fs').writeFile('logo.png',mybuffer)
「@浪里淘沙的小法师」