Node.js_笔记

核心的JavaScript语法定义了最小限度的API,可以操作数值、文本数组等,但不包含输入输出功能。输出入是内嵌JavaScript的宿主环境的责任 。
宿主环境:浏览器、node
与限制JavaScript只能使用浏览器提供的API不同,Node给予JavaScript可以访问整个操作系统的权限,允许JavaScript读写文件、通过网络发送和接收数据

node.js的全局对象有global、process、console、module、exports
process获得node进程相关的信息,比如运行node程序时的命令行参数。或者设置进程相关信息,比如设置环境变量。
@TOC

介绍

可以在https://node.green/上获取到Node各个版本对ES语法的支持
Node.js的结构图
Node.js_笔记_第1张图片
libuv是提供异步功能的C库,在运行时负责一个事件循环,一个线程池、文件系统I/O、DNS相关的IO和网络IO。

node.js启动后,会开启一个JS主线程和libuv提供的线程池和Event Loop。当发现有IO操作就交给线程池并注册回调函数。

应用:高并发、实时聊天、实时消息推送、客户端逻辑强大的SPA
Restful API,可以处理数万条链接
实时websocket应用
前端工具链
桌面开发

Runtime和vm

Runtime :数据类型的确定由编译推迟至运行时,所以需要一个运行时系统来处理编译后的代码。

JavaScript引擎负责解析和JIT编译,例如编译成机器码。Runtime提供内建的库(例如常用数据类型、Window对象、DOM API),可以在运行时使用。

vm:通常认为是硬件和二进制文件之间的中间层
c++编译好的二进制文件交给OS直接调用
Java编译好的二进制文件交给Java虚拟机运行,对开发者屏蔽了不同操作系统的差异
Node的一个核心模块vm提供了一系列API用于在V8虚拟机环境中编译和运行代码。

Node.js是使 JavaScript 运行在浏览器之外,即服务端的开发平台, 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,也是一个

前端的JavaScript是由ECMAScript、DOM、BOM组合而成,Node.js是由ECMAScript、OS、File、Net、DB组成。

允许通过命令行运行用JavaScript编写的程序。大多数前端开发工具都要使用Node.js。Node自带两个命令行程序:node和npm。

使用 Node.js,可以开发具有复杂逻辑的网站、基于社交网络的大规模 Web 应用、Web Socket 服务器、 TCP/UDP 套接字应用程序、命令行工具、交互式终端程序、带有图形用户界面的本地应用程序、单元测试工具、客户端 JavaScript 编译器

Node.js 内建了 HTTP 服务器支持,也就是可以轻而易举地实现一个网站和服务器的组合。这和 PHP、Perl 不一样,因为在使用 PHP 的时候,必须先搭建一个 Apache 之类的HTTP 服务器,然后通过 HTTP 服务器的模块加载或 CGI 调用,才能将 PHP 脚本的执行结果呈现给用户。而当你使用 Node.js 时,不用额外搭建一个 HTTP 服务器,因为 Node.js 本身就内建了一个。这个服务器不仅可以用来调试代码,而且它本身就可以部署到产品环境,它的性能足以满足要求。
Node.js 还可以部署到非网络应用的环境下,比如一个命令行工具。Node.js 还可以调用C/C++ 的代码,这样可以充分利用已有的诸多函数库,也可以将对性能要求非常高的部分用C/C++ 来实现。

node 的 REPL 模式

REPL【Read-eval-print loop输入—求值—输出循环】
和python一样,Node.js 也有这样的功能,运行无参数的 node 将会启动一个 JavaScript 的交互式 shell
打开命令提示符,然后输入 node,进入 REPL 模式以后,会出现一个“>”提示符提示你输入命令,输入后按回车,Node.js 将会解析并执行命令。如果你执行了一个函数,那么 REPL 还会在下面显示这个函数的返回值。连续按两次 Ctrl + C 即可推出Node.js 的 REPL 模式。
Node.js_笔记_第2张图片

console 是 Node.js 提供的控制台对象,其中包含了向标准输出写入的操作,如 console.log、console.error 等。console.log 是我们最常用的输出指令,它和 C 语言中的 printf 的功能类似,也可以接受任意多个参数,支持 %d、%s 变量引用。
undefined 就是 console.log 的返回值。如果你输入了一个错误的指令,REPL 则会立即显示错误并输出调用栈。

process.argv命令行参数

单线程、非阻塞的事件编程模式

node中的同步异步
node.js是单线程,异步是通过循环事件队列来实现,同步则是阻塞式IO,这在高并发环境将是一个很大的性能问题,所以同步一般只在基础框架启动时使用,用来加载配置文件、初始化程序等。
node是异步非阻塞的,这对高并发十分有限,可是我们还有一些同步的应用需求,比如和操作系统的shell命令交互,调用可执行文件等,子进程就是负责node阻塞的工作

JavaScript是单线程的,意思是JavaScript的执行是单线程,他的宿主环境(node、浏览器)都是多线程的。
Node与进程相关的模块由process、child_process、cluster
Node.js是单进程单线程应用程序,但是V8引擎提供的异步执行回调接口可以处理大量的高并发。所以Node.js是单线程且支持高并发的脚本语言,IO密集型处理是强项,因为Node.js的IO请求都是异步的。

Node.js在底层访问I/O其实还是多线程,可以翻看fs模块源码,里面用libuv来处理I/O

主线程是单线程,所有阻塞的部分交给一个线程池处理,然后主线程通过一个队列跟线程池协作。为了处理异步 I/O,线程必须有事件循环,不断地检查有没有未处理的事件,依次予以处理。

传统的架构是多线程模型,为每个业务逻辑提供一个系统线程,通过系统线程切换来弥补同步式【即时回复】 I/O 调用时的时间开销。
Node.js 使用的是单线程模型,对于所有 I/O 都采用异步式的请求方式,避免了频繁的上下文切换。因为它使用了事件式编程和异步 I/O

Node.js 在执行的过程中会维护一个事件队列,程序在执行时进入事件循环等待下一个事件到来,每个异步式 I/O 请求完成后会被推送到事件队列,等待程序进程进行处理。
例如,对于简单而常见的数据库查询操作,按照传统方式实现的代码如下:
res = db.query(‘SELECT * from some_table’);
res.output();
以上代码在执行到第一行的时候,线程会阻塞,等待数据库返回查询结果,然后再继续处理。由于数据库查询可能涉及磁盘读写和网络通信,其延时可能相当大,线程会在这里阻塞等待结果返回。对于高并发的访问,一方面线程长期阻塞等待,另一方面为了应付新请求而不断增加线程,会浪费大量系统资源,同时线程的增多也会占用大量的 CPU 时间来处理内存上下文切换,而且还容易遭受低速连接攻击。
看看Node.js是如何解决这个问题的:
db.query(‘SELECT * from some_table’, function(res) {
res.output();
});
这段代码中 db.query 的第二个参数是一个回调函数。进程在执行到db.query 的时候,不会等待结果返回,而是直接继续执行后面的语句,直到进入事件循环。当数据库查询结果返回时,会将事件发送到事件队列,等到线程进入事件循环以后,才会调用之前的回调函数继续执行后面的逻辑。
Node.js 的异步机制是基于事件的,所有的磁盘 I/O、网络通信、数据库查询都以非阻塞的方式请求,返回的结果由事件循环来处理。

多线程

在支持HTML5的浏览器里,我们可以使用web worker来处理一些耗时运算。
对应Node.js也提供了cluster、child_process模块的web worker来解决。

EventEmitter 观察者模式的类

主要功能:订阅和发布消息,用于解决多模块交互而产生的耦合问题
应用:
在模块间传递消息
在回调函数内外传递修消息
处理流数据,因为流是在EventEmitter的基础上实现的
运用观察者模式收发消息的相关应用

node.js中的stream流

基于EventEmitter的数据管理模式,由各种不同的抽象接口组成,主要包括可写、可读、可读写、可转换等类型

流是非阻塞数据处理模式,可以提升效率、节省内存、有助于处理管道且可扩展等。
流的应用:文件读写、网络请求、数据转换、音频、视频等方面有很广泛的应用
监听error事件,可以捕获流的错误事件

使用

文本编辑器,在其中输入:

console.log('Hello World'); 
console.log('%s: %d', 'Hello', 25);

将文件保存为 helloworld.js,打开终端,进入 helloworld.js 所在的目录,执行以下命令:
node helloworld.js

在成功运行 PHP 之前先要配置一个功能强大而复杂的 HTTP 服务器,譬如 Apache、IIS 或 Nginx,还需要将 PHP 配置为 HTTP 服务器的模块,或者使用FastCGI 协议调用 PHP 解释器。这种架构是“浏览器  HTTP 服务器  PHP 解释器”的组织方式,而Node.js采用了一种不同的组织方式,Node.js 将“HTTP服务器”这一层抽离,直接面向浏览器用户。

创建一个 HTTP 服务器吧。建立一个名为 app.js 的文件

//app.js 
var http = require('http'); 
http.createServer(function(req, res) { 
 res.writeHead(200, {'Content-Type': 'text/html'}); 
 res.write('

Node.js

'
); res.end('

Hello World

'
); }).listen(3000); console.log("HTTP server is listening at port 3000.");

Node.js 提供了 require 函数来调用其他模块,而且模块都是基于文件的。var http = require(‘http’),其中 http 是 Node.js 的一个核心模块,其内部是用 C++ 实现的,外部用 JavaScript 封装。我们通过require 函数获取了这个模块,然后才能使用其中的对象。

运行 node app.js命令,打开浏览器访问 http://127.0.0.1:3000
这个程序调用了 Node.js 提供的http 模块,对所有 HTTP 请求答复同样的内容并监听 3000 端口。在终端中运行这个脚本时,我们会发现它并不像 Hello World 一样结束后立即退出,而是一直等待,直到按下 Ctrl + C 才会结束。这是因为 listen 函数中创建了事件监听器,使得 Node.js 进程不会退出事件循环。

因为 Node.js 只有在第一次引用到某部份时才会去解析脚本文件,以后都会直接访问内存,避免重复载入,开发 Node.js 实现的 HTTP 应用时会发现,无论你修改了代码的哪一部份,都必须终止Node.js 再重新运行才会奏效。
supervisor 可以监视你对代码的改动,并自动重启 Node.js。【相当于持续重启,有选择的使用】
首先使用 npm 安装 supervisor:
$ npm install -g supervisor
接下来,使用 supervisor 命令启动 app.js:
$ supervisor app.js

模块

模块是 Node.js 应用程序的基本组成部分,文件和模块是一一对应的。这个文件可能是 JavaScript 代码、JSON 或者编译过的 C/C++ 扩展。

创建模块

在 Node.js 中,创建一个模块即创建一个文件。Node.js 提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象

//module.js 
var name; 
exports.setName = function(thyName) { 
 name = thyName; 
}; 
exports.sayHello = function() { 
 console.log('Hello ' + name); 
}; 

//getmodule.js 
var myModule1 = require('./module');
 myModule1.setName('1'); 
 var myModule = require('./module');
 myModule.setName('BYVoid'); 
myModule1.sayHello(); 

运行node getmodule.js,结果是:
Hello BYVoid

require 不会重复加载模块,也就是说无论调用多少次获得的模块都是最后一次。

封装一个对象到模块的写法:

//hello.js 
function Hello() { 
 var name; 
 
 this.setName = function(thyName) { 
 name = thyName; 
 }; 
 
 this.sayHello = function() { 
 console.log('Hello ' + name); 
 }; 
}; 
module.exports = Hello;//如果是exports.Hello = Hello;

//gethello.js 
var Hello = require('./hello'); //那么是require('./hello').Hello
hello = new Hello(); 
hello.setName('BYVoid'); 
hello.sayHello();

NPM

包管理工具,何为包package,Node.js 根 据 CommonJS 规范实现了包机制,开发了 npm来解决包的发布和获取需求。
用于描述一个文件或一个目录,一个包的配置:

  • package.json配置文件(在包目录中运行npm init 会根据交互式问答产生一个符合标准的package.json)
  • GZIP压缩文件
  • 解析GZIP的URL
  • 为注册表添加< name > @< version >的URL信息

NPM脚本: NPM允许在package.json文件中使用script定义脚本命令
npm run;//查看所有脚本命令(脚本文件一般位于node_modules/.bin子目录里)

Node.js 在调用某个包时,会首先检查包中 package.json 文件的 main 字段,将其作为包的接口模块,如果 package.json 或 main 字段不存在,会尝试寻找 index.js 或 index.node 作为包的接口。

{
    //...
    "script":{
        "build":"node index.js"
    }
    //...
    "main" : "./lib/interface.js" 
}

//npm run build等同于node index.js

二进制文件应该在 bin 目录下;
JavaScript 代码应该在 lib 目录下;
文档应该在 doc 目录下;
单元测试应该在 test 目录下

JavaScript 的单元测试可使用一个 Node.js 的 模 块,Karma(https://github.com/karma-runner/karma)在多浏览器里执行,并且每当修改并保存源文件后,就会自动执行。这一点很重要。如果你能自律编写单元测试,每当文件保存时就执行单元测试这一能力将能够在早期发现缺陷。这点对 JavaScript 来说尤其有用,因为 JavaScript 没有编译器,不能在早期验证代码的合法性。有效的单元测试常常扮演一个伪编译器的角色,它们能够立刻反馈代码的质量,并且一有缺陷,就能检测到。

JavaScript 中有很多自动化文档生成工具。JSDoc(https://github.com/jsdoc3/jsdoc)有着和Javadoc 类似的标记和输出,Dox(https://github.com/visionmedia/dox)是一个生成文档的Node.js 模块。
Docco(http://jashkenas.github.io/docco/)是一个 Node.js 模块,它将代码和注释组织成一种类似文章的格式。虽然Docco 不直接验证和执行代码规范,使用它却能鼓励大家使用良好的代码结构和注释,而不是不假思索地复制粘贴。

package.json 是 CommonJS 规定的用来描述包的文件,完全符合规范的 package.json 文件应该含有以下字段。

  • name:包的名称,必须是唯一的,由小写英文字母、数字和下划线组成,不能包含
    空格。
  • description:包的简要说明。
  • version:符合语义化版本识别规范的版本字符串。
  • keywords:关键字数组,通常用于搜索。
  • maintainers:维护者数组,每个元素要包含 name、email(可选)、web(可选)
    字段
  • contributors:贡献者数组,格式与maintainers相同。包的作者应该是贡献者
    数组的第一个元素。
  • bugs:提交bug的地址,可以是网址或者电子邮件地址。
  • licenses:许可证数组,每个元素要包含 type (许可证的名称)和 url (链接到
    许可证文本的地址)字段。
  • repositories:仓库托管地址数组,每个元素要包含 type (仓库的类型,如 git )、
    url (仓库的地址)和 path (相对于仓库的路径,可选)字段。
  • dependencies:包的依赖,一个关联数组,由包名称和版本号组成。
{ 
 "name": "mypackage", 
 "description": "Sample package for CommonJS. This package demonstrates the required 
elements of a CommonJS package.", 
 "version": "0.7.0", 
 "keywords": [ 
 "package", 
 "example" 
 ], 
 "maintainers": [ 
 { 
 "name": "Bill Smith", 
 "email": "[email protected]", 
 } 
 ], 
 "contributors": [ 
 { 
 "name": "BYVoid", 
 "web": "http://www.byvoid.com/" 
 } 
 ], 
 "bugs": { 
 "mail": "[email protected]", 
 "web": "http://www.example.com/bugs" 
 }, 
 "licenses": [ 
 { 
 "type": "GPLv2", 
 "url": "http://www.example.org/licenses/gpl.html" 
 } 
 ], 
 "repositories": [ 
 { 
 "type": "git", 
 "url": "http://github.com/BYVoid/mypackage.git" 
 } 
 ], 
 "dependencies": { 
 "webkit": "1.2", 
 "ssl": { 
 "gnutls": ["1.0", "2.0"], 
 "openssl": "0.9.8" 
 } 
 } 
} 

在使用 npm 安装包的时候,有两种模式
本地模式:把某个包作为工程运行时的一部分时
使用 npm 安装包的命令格式为:

npm [install/i] [package_name]

安装成功放置在当前目录的 node_modules 子目录下,require 在加载模块时会尝试搜寻 node_modules 子目录
npm 本地模式仅仅是把包安装到 node_modules 子目录下,其中的 bin 目录没有包含在 PATH 环境变量中,不能直接在命令行中调用。
全局模式:要在命令行下使用包

npm [install/i] -g [package_name]

使用全局模式安装时,npm 会将包安装到系统目录,譬如 /usr/local/lib/node_modules/,同时 package.json 文件中 bin 字段包含的文件会被链接到 /usr/local/bin/。/usr/local/bin/ 是在PATH 环境变量中默认定义的,因此就可以直接在命令行中运行 xxx.js命令了。
注:使用全局模式安装的包并不能直接在 JavaScript 文件中用 require 获得,因为 require 不会搜索 /usr/local/lib/node_modules/。

https://engineering.fb.com/2016/10/11/web/yarn-a-new-package-manager-for-javascript/

yarn主要有以下特色:
安装速度快(cache和依赖解析做得好)
模块安装保证幂等性
锁定各依赖模块的版本(npm 使用的semver默认是指定了一个range)
兼容性好(兼容旧有npm的工作流)
(见上面的链接,以及yarn的github)

创建全局链接

npm 提供了一个命令 npm link,在本地包和全局包之间创建符号链接。

npm link 命令不支持 Windows。

(1)使用全局模式安装的包不能直接通过 require 使用,但通过 npm link命令可以打破这一限制。
通过 npm install -g express 安装了 express,
在工程的目录下运行命令:
$ npm link express
./node_modules/express -> /usr/local/lib/node_modules/express
在 node_modules 子目录中发现一个指向安装到全局的包的符号链接。通过这种方法,就可以把全局包当本地包来使用了。
(2)将本地的包链接到全局。
在包目录( package.json 所在目录)中运行 npm link 命令。
开发包时,利用这种方法可以非常方便地在不同的工程间进行测试。

e.g:
npm install -g browser-sync
显示:
E:\nodejs\node_global\browser-sync -> E:\nodejs\node_global\node_modules\browser-sync\dist\bin.js
browser-sync start --server

发布包

包目录下运行
npm init :生成一个符合 npm 规范的 package.json 文件
npm adduser :获得一个账号用于今后维护自己的包
npm whoami :测验是否已经取得了账号
npm publish:发布
访问 http://search.npmjs.org/ 就可以找到刚刚发布的包了。可以在世界的任意一台计算机上使用 npm install byvoidmodule 命令来安装它
更新
在 package.json 文件中修改 version 字段,然后npm publish
npm unpublish 命令来取消发布。

调试

node debug debug.js:打开了一个 Node.js 的调试终端
node-inspector

控制流很大程度上要靠事件和回调函数来组织,一个逻辑要拆分为若干个单元。

Mocha

一个基于Node.js和浏览器的集合各种特性的JavaScript测试框架

你可能感兴趣的:(Node,node.js)