Node入门——基础知识

安装Node

网上教程比较多,就不赘述了。

REPL工具

mac环境下,在终端输入node就可以运行Node的REPL。

  • 可以方便地演这个一些Node API和JavaScript API 是否正确
  • 有时候忘了某个API的用法,可以用来测试
创建一个简单的Node脚本
const http = require('http');
const serv = http.createServer(function(req,res){
  res.writeHead(200,{'Content-Type': 'text/html'});
  res.end('<div>hello Node</div>');
});
serv.listen(3000);

一个简单的HTTP服务器来托管一个简单的HTML文档,打开http://localhost:3000/就可以访问了。至于细节后面会讲到。

NPM

NPM包管理器(NPM)可以在项目中轻松地对模块进行管理

  • 下载指定的包
  • 解决包的依赖
  • 运行测试脚本
  • 安装命令行工具
    NPM本身使用Node.js开发的,有二进制包的发布形式。

NPM有两个命令可以用来在仓库中搜索和查看模块:search和view

package.json
  • 创建一个package.json模块,可以自定义模块。
  • 你只需要将package.json文件发给别人,别人一执行npm install就可以将你项目中依赖的包也下一份带他自己的项目中。如果想交换依赖包的信息,每次都交换一个node_modules包简直就是馊主意。
  • package.json必须遵行JSON格式,必须保证所有的字符串,都是使用双引号而不是单引号
JavaScript基础
数据类型

基础类型:Number String Boolean null undefined
引用类型:Object Function Array

let a = 'test';
let b = new String('test');
typeof a; // "String"
typeof b;// "Object"
a instanceof string; // false;
b instanceof string; // true;
null instanceof Object; // false
[] instanceof Object; // true
typeof null; //   "object"
typeof []; //   "object"

还好,我们可以通过另一种方式来正确判断类型:

Object.prototype.toString.call([]); // "[object Array]"
函数

在JavaScript中,函数最为重要。
函数有一个很有意思的属性-参数数量,该属性致命函数申明时可以接收的参数数量。

let a = function(a,b,c){};
a.length; // 3

尽管这在浏览器很少使用,但是它对我们非常重要,因为一些流行的Node.js框架就是通过此属性来根据参数个数提供不同功能的。

闭包

在JavaScript中,每次调用函数,新的作用域就会出现。
自执行函数是一种机制,通过这种机制声明和调用一个匿名函数,能够达到仅定义一个新作用域的作用。

获取键
// 1、遍历包括从父类继承下来的属性
for key in obj {
}
// 2、为避免把父属性也取到,可以用a.hasOwnProperty(i)

// 3、Object.keys(a)也可以获取对象的键
数组
Array.isArray([]); // true
  • 遍历数组 :forEach (arr.forEach(function(){}))
  • 过滤数组:filter(arr.filter(function(){return xx;}))
  • 改变数组的每个值:map(arr.map(function(){return xx;}))
  • 接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值:reduce
    • numbers.reduce(getSum, 0); // numbers是一个数组,0表示其实索引,getSum是计算方法。
__proto__
function a(){}
function b(){}
a.prototype.__proto__  = b.prototype;
存取器

简单理解就是取值之前经过计算,有点想vue里面的computed属性。

function a() {
  this.a = 1;
  this.b = 2;
}
a.prototype.__defineGetter__('a',function(){
  return 3;
})
let obj = new a();
obj.a;
阻塞与非阻塞

**Node采用一个长期运行的进程。**即Node并不会删除执行环境。(这一地啊嘛对写出健壮的Nodejs程序,避免运行时错误是非常重要的!!!)

也就是说如果执行下面的代码,当有多个请求时,第一个请求能顺利拿到响应,而后面的请求就会得到一个空值。

let book = [
  'hello',
  'test'
];
function serveBooks () {
  // 给客户端返回的HTML代码
  let html = '<div>books.join('-')</div>';
  // 修改了状态
  books = [];
  return html;
}

采用了事件轮询:Node会先注册事件,随后不停地询问内核这些事件是否已经分发。当事件分发时,对应的回调函数就会被触发,然后继续执行下去。如果没有事件触发,则继续执行其他代码,直到有新事件,再去执行对应的回调函数。

Node如何做到高并发,即用Node书写的简单的服务器就能够处理每秒上千个请求

首先要明白堆栈的概念:
即执行堆栈,如果一个函数调用又去调用另一个函数的话,v8就会把它添加到调用堆栈上。

由于Node是运行在单线程中的,所以,当调用堆栈展开时,Node就无法处理其他客户端或者HTTP请求了。这时候,你会想,这么说来Node的最大并发量不是为1嘛?!其实就是这样,Node并不提供真正的并行操作。实际上,Node能够处理高并发的原因在于,Node调用堆栈执行非常快,即使有一个请求需要访问数据库或者硬盘,也不会被挂起,Node只是告诉事件轮询访问完数据库或者磁盘之后通知一下,接着Node就继续执行下一个请求了。

Node应用依托在一个拥有大量共享状态的进程中。
所以,在一个HTTP请求中,如果某个回调函数发生了错误,整个进程都会遭殃。

var http = require('http');

http.createServer(function(){
	throw new Error('错误不会被捕获')}).listen(3000);

因为错误未被捕获,若访问web服务器,进程会崩溃。

Node之所以这么处理是因为,在发生未被捕获的错误时,进程的状态就不确定了。之后可能就无法正常工作了,并且如果错误始终不处理的话,就会一直抛出意料之外的错误,这样很难调试。

在Node中,许多像http、net这样的原声模块都会分发error事件。如果该事件未处理,就会抛出未捕获的异常。

在JavaScript中,当错误发生时,在错误信息中可以看到一系列的函数调用,这称为堆栈跟踪。
如下面的代码:

function c() {
  b();
}
function b() {
  a();
}
function a() {
  throw new Error('here');
}
c();

在这里插入图片描述
从图片中可以看到导致错误发生的函数调用路径。
接下来,我们试一下引入事件轮询:

function c() {
  b();
}
function b() {
  a();
}
function a() {
  setTimeout(function() {
    throw new Error('here');
  },10);
}
c();

在这里插入图片描述
你会发现有用的跟踪信息不见了。
所以,在Node.js中,每步都要正确进行错误处理。一旦遗漏,发生错误就很难追踪了,因为上下文信息都丢失了

一定要记住一句话线程中的所有状态都是维护在一个内存空间的,写程序的时候一定要格外小心

Node中的Javascript
global对象

在浏览器中,全局对象值得就是window对象。在window对象上定义的任何内容都可以被全局访问到。比如,setTimeout其实就是window.setTimeout,document其实就是window.document。

Node中有两个类似但却格子代表不同含义的对象,如下所示。

  • global:和window一样,任何global对象上的属性都可以被全局访问到
  • process:所有全局执行上下文中的内容都在process对象中。在浏览器中,只有一个window对象,在Node中,也只有一个process对象
绝对模块和相对模块

绝对模块是指Node通过在其内部node_modules查找到的模块,或者Node内置的如fs这样的模块。
相对模块是将require指向一个相对工作目录中的JavaScript文件。

如果没有通过NPM来安装贸易额不再node_modules目录中,而且Node自带模块中没有以此命名的模块,就会找不到模块而报错,因此需要在require参数前加上./

事件

Node.js中的基础API之一就是EventEmmitter。无论是在Node中还是在浏览器中,大量代码都依赖于所监听或者分发的事件。

在Node中,Node暴露了Event EmitterAPI,该API上定义了on、emit以及removeListener方法。它以process.EventEmitter形式暴露出来。

可以将事件API添加到自己的类中:

let EventEmitter = process.EventEmiiter,
MyClass = function(){};
MyClass.prototype.__proto__ = EventEmitter.prototype

这样,MyClass的实例都具备了事件功能。

事件是Node非阻塞设计的重要体现。Node通常不会直接返回数据(因为这样可能会在等待某个资源的时候发生线程阻塞),而是采用分发事件来传递数据的方式。

buffer

buffer是一个表示固定内存分配的全局对象(也就是说,要放到缓冲区的字节数需要提前定下),它就好比是一个由八位字节元素组成的数组,可以有效地在JavaScript中表示二进制数据。

在Node中,绝大部分进行数据I/O操作的API都用buffer来接收和返回数据

参考《了不起的Node.js》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值