Node.js

1.什么是nodejs

Node.js采用模块化结构,按照CommonJS规范定义和使用模块。模块与文件是一一对
应关系,即加载一个模块,实际上就是加载对应的一个模块文件。Node.js 被设计用来
开发大规模高并发的网络应用,这种网络应用的瓶颈之一是在 I/O 的处理效率上。由
于硬件及网络的限制,I/O 的速度往往是固定的,如何在此前提下尽可能处理更多的客
户请求,提高 CPU 使用效率,便成了开发人员面临的最大问题。得益于基于事件驱动
的编程模型,Node.js 使用单一的 Event loop 线程处理客户请求,将 I/O 操作分派至
各异步处理模块(这里一般人不理解,node.js包含很多模块,这些模块可以使用js直
接调用系统的api),既解决了单线程模式下 I/O 阻塞的问题,又避免了多线程模式下
资源分配及抢占的问题。

单线程模式:客户端发起一个 I/O 请求(数据库查询),然后等待服务器端返回 I/O 结
果,结果返回后再对其进行操作,但这种请求常常需要很长时间,这一过程中,服务器
无法接受新的请求,即阻塞式 I/O。这种处理方式虽然简单,却不实用,尤其是面对大量请求的时候

多线程模式:服务器为每个请求分配一个线程,所有任务均在自己的线程内执行,服
务器每创建一个线程,每个线程大概会占用 2M 的系统内存,而且线程之间的切换也
会降低服务器的处理效率,基于成本的考虑,这种处理方式也有一定的局限性。开发
多线程程序非常困难,容易出错。程序员需考虑死锁,数据不一致等问题,多线程的
程序极难调试和测试。基本上在程序运行出错的时候,程序员才知道自己的程序有错误。

事件驱动:客户发起 I/O 请求的同时传入一个函数,该函数会在 I/O 结果返回后被自
动调用,而且该请求不会阻塞后续操作。所有请求以及同时传入的回调函数均发送至
同一线程,该线程通常叫做 Event loop 线程,该线程负责在 I/O 执行完毕后,将结果
返回给回调函数。这里要注意的是 I/O 操作本身并不在该线程内执行,所以不会阻塞后续请求。

2.模块

编写稍大一点的程序时一般都会将代码模块化。在NodeJS中,一般将代码合理拆分到
不同的JS文件中,每一个文件就是一个模块,而文件路径就是模块名。在编写每个模
块时,都有require、exports、module三个预先定义好的变量可供使用。

require函数用于在当前模块中加载和使用别的模块,传入一个模块名,返回一个模块
导出对象。模块名可使用相对路径(以./开头),或者是绝对路径(以/或C:之类的盘符开头)。

exports对象是当前模块的导出对象,用于导出模块公有方法和属性。别的模块通过
require函数使用当前模块时得到的就是当前模块的exports对象。

通过module对象可以访问到当前模块的一些相关信息,但最多的用途是替换当前模块
的导出对象。例如模块导出对象默认是一个普通对象,如果想改成一个函数的话,可以使用以下方式。

3.node.js相对于传统服务器的好处? 为什么选js为它的开发代码?

1.Nodejs基于Javascript语言,不用再单独新学一门陌生的语言,从而减低了学习的门
槛。同时,Javascript语言在Web前端开发中至关重要,特别HTML5的应用必须要使
用,所以前后台统一语言,不仅可以实现程序员的全栈开发,还可以统一公共类库,代码标准化。
2.Nodejs并没有重新开发运行时环境,而是选择了目前最快的浏览器内核V8做为执行
引擎,保证了Nodejs的性能和稳定性。
3.动态语言:开发效率非常高,并有能力构建复杂系统,
4.性能和I/O负载:Nodejs非常好的解决了IO密集的问题,通过异步IO来实现。
5.操作性:实现了Nodejs对于内存堆栈的监控系统

为什么选择js:   JavaScript 则是支持函数式编程范型的语言,很好地契合了 Node.js 
基于事件驱动的编程模型。加之 Google 提供的 V8 引擎,使 JavaScript 语言的执行
速度大大提高。

4.常用js类定义的方法有哪些?

1.主要有构造函数原型和对象创建两种方法。原型法是通用老方法,对象创建是ES5
推荐使用的方法.目前来看,原型法更普遍.
1) 构造函数方法定义类
    function Person(){
        this.name = 'michaelqin';
    }
    Person.prototype.sayName = function(){
        alert(this.name);
    }

    var person = new Person();
    person.sayName();
2) 对象创建方法定义类

    var Person = {
        name: 'michaelqin',
        sayName: function(){ alert(this.name); }
    };

    var person = Object.create(Person);
    person.sayName();

5.js类继承的方法有哪些

原型链法,属性复制法和构造器应用法. 另外,由于每个对象可以是一个类,这些方法
也可以用于对象类的继承.
1) 原型链法

    function Animal() {
        this.name = 'animal';
    }
    Animal.prototype.sayName = {
        alert(this.name);
    };

    function Person() {}
    Person.prototype = Animal.prototype; // 人继承自动物
    Person.prototype.constructor = 'Person'; // 更新构造函数为人
2) 属性自制法

    function Animal() {
        this.name = 'animal';
    }
    Animal.prototype.sayName = {
        alert(this.name);
    };

    function Person() {}

    for(prop in Animal.prototype) {
        Person.prototype[prop] = Animal.prototype[prop];
    } // 复制动物的所有属性到人量边
    Person.prototype.constructor = 'Person'; // 更新构造函数为人
3) 构造器应用法

    function Animal() {
        this.name = 'animal';
    }
    Animal.prototype.sayName = {
        alert(this.name);
    };

    function Person() {
        Animal.call(this); // apply, call, bind方法都可以.细微区别,后面会提到.
    }
  1. js类多重继承的实现方法是怎么样的?
就是类继承里边的属性复制法来实现.因为当所有父类的prototype属性被复制后,子
类自然拥有类似行为和属性.
  1. js里的作用域是什么样子的?
大多数语言里边都是块作作用域,以{}进行限定,js里边不是.js里边叫函数作用域,
就是一个变量在全函数里有效.比如有个变量p1在函数最后一行定义,第一行也有
效,但是值是undefined.
this指的是对象本身,而不是构造函数.只有函数执行的时候才能确定this到底指向
谁,实际上this的最终指向的是那个调用它的对象。
代码演示

    function Person() {
    }
    Person.prototype.sayName() { alert(this.name); }

    var person1 = new Person();
    person1.name = 'michaelqin';
    person1.sayName(); // michaelqin

9.apply, call和bind有什么区别?

三者都可以把一个函数应用到其他对象上,注意不是自身对象.apply,call是直接执行
函数调用,bind是绑定,执行需要再次调用.apply和call的区别是apply接受数组作为
参数,而call是接受逗号分隔的无限多个参数列表,
它们在功能上是没有区别的,都是改变this的指向,它们的区别主要是在于方法的实现
形式和参数传递上的不同
1. 函数.call(对象,arg1,arg2....)
2. 函数.apply(对象,[arg1,arg2,...])
3. var ss=函数.bind(对象,arg1,arg2,....)     ss()调用一下

代码演示

    function Person() {
    }
    Person.prototype.sayName() { alert(this.name); }

    var obj = {name: 'michaelqin'}; // 注意这是一个普通对象,它不是Person的实例
    1) apply
    Person.prototype.sayName.apply(obj, [param1, param2, param3]);

    2) call
    Person.prototype.sayName.call(obj, param1, param2, param3);

    3) bind
    var sn = Person.prototype.sayName.bind(obj);    
    sn([param1, param2, param3]); // bind需要先绑定,再执行 
    sn(param1, param2, param3); // bind需要先绑定,再执行
  1. caller, callee和arguments分别是什么?
arguments是函数的所有参数列表,它是一个类数组的变量
caller:在一个函数调用另一个函数时,被调用函数会自动生成一个caller属性,指向
调用它的函数对象。如果该函数当前未被调用,或并非被其他函数调用,则caller为null。
callee当函数被调用时,它的arguments.callee对象就会指向自身,也就是一个对自己
的引用。由于arguments在函数被调用时才有效,因此arguments.callee在函数未调用
时是不存在的(即null.callee),且解引用它会产生异常。
代码演示

    function parent(param1, param2, param3) {
        child(param1, param2, param3);
    }

    function child() {
        console.log(arguments); // { '0': 'mqin1', '1': 'mqin2', '2': 'mqin3' }
        console.log(arguments.callee); // [Function: child]
        console.log(child.caller); // [Function: parent]
    }

    parent('mqin1', 'mqin2', 'mqin3');

11.什么是闭包?以及闭包的优点,缺点,用处,及特性

 定义:闭包 当一个函数的返回值是另外一个函数,而返回的那个函数如果调用了其父函
数内部的变量,且返回的这个函数在外部被执行就产生了闭包.闭包是一个环境,具体指的就是外部函数
用处:
1.读取函数内部的变量;
2.这些变量的值始终保持在内存中,不会在外层函数调用后被自动清除。

优点:
1:变量长期驻扎在内存中;
2:避免全局变量的污染;
3:私有成员的存在 ;

特性:
1:函数套函数;
2:内部函数可以直接使用外部函数的局部变量或参数;
3:变量或参数不会被垃圾回收机制回收 GC;

缺点:
1.常驻内存 会增大内存的使用量 使用不当会造成内存泄露,由于闭包会使得函数中的
变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能
问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2.闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象
(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的
私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

12.defineProperty, hasOwnProperty, isEnumerable都是做什么用的?

1.Object.defineProperty(obj, prop, descriptor)用来给对象定义属性,有value, writable, 
configurable, enumerable, set/get等
2.hasOwnProerty用于检查某一属性是不是存在于对象本身,继承来的父亲的属性不算.
3.isEnumerable用来检测某一属性是否可遍历,也就是能不能用for..in循环来取到.

13.js常用设计模式的实现思路,单例,工厂,代理,装饰,观察者模式等

1) 单例: 任意对象都是单例,无须特别处理
    var obj = {name: 'michaelqin', age: 30};

2) 工厂: 就是同样形式参数返回不同的实例
    function Person() { this.name = 'Person1'; }
    function Animal() { this.name = 'Animal1'; }

    function Factory() {}
    Factory.prototype.getInstance = function(className) {
        return eval('new ' + className + '()');
    }

    var factory = new Factory();
    var obj1 = factory.getInstance('Person');
    var obj2 = factory.getInstance('Animal');
    console.log(obj1.name); // Person1
    console.log(obj2.name); // Animal1

14.说说字符串常用的十个函数?

charAt() 返回在指定位置的字符。
concat() 连接字符串。
fromCharCode() 从字符编码创建一个字符串。
indexOf() 检索字符串。
match() 找到一个或多个正则表达式的匹配。
replace() 替换与正则表达式匹配的子串。
search() 检索与正则表达式相匹配的值。
slice() 提取字符串的片断,并在新的字符串中返回被提取的部分。
split() 把字符串分割为字符串数组。
substr() 从起始索引号提取字符串中指定数目的字符。
substring() 提取字符串中两个指定的索引号之间的字符。
toLocaleLowerCase() 把字符串转换为小写。
toLocaleUpperCase() 把字符串转换为大写。
toLowerCase() 把字符串转换为小写。
toUpperCase() 把字符串转换为大写。
toString() 返回字符串。
valueOf() 返回某个字符串对象的原始值。

15.为什么使用node

总结起来node有以下几个特点:简单强大,轻量可扩展.简单体现在node使用的是
javascript,json来进行编码,人人都会;强大体现在非阻塞IO,可以适应分块传输数据,
较慢的网络环境,尤其擅长高并发访问;轻量体现在node本身既是代码,又是服务
器,前后端使用统一语言;可扩展体现在可以轻松应对多实例,多服务器架构,同时有海量的第三方应用组件.

16.node的构架是什么样子的?

主要分为三层,应用app >> V8及node内置架构 >> 操作系统. 
V8是node运行的环境,可以理解为node虚拟机.
node内置架构又可分为三层: 核心模块(javascript实现) >> c++绑定 >> libuv + CAes + http.

17.node有哪些核心模块?

EventEmitter, Stream, FS, Net和全局对象

18.node全局对象

process, console, Buffer和exports
  1. process有哪些常用方法?
process模块用来与当前进程互动,可以通过全局变量process访问,不必使用require命令
加载。它是一个EventEmitter对象的实例。
process对象提供一系列属性,用于返回系统信息:

process.pid:当前进程的进程号。
process.version:Node的版本,比如v0.10.18。
process.platform:当前系统平台,比如Linux。
process.title:默认值为“node”,可以自定义该值。
process.argv:当前进程的命令行参数数组。
process.env:指向当前shell的环境变量,比如process.env.HOME。
process.execPath:运行当前进程的可执行文件的绝对路径。
process.stdout:指向标准输出。
process.stdin:指向标准输入。
process.stderr:指向标准错误。

process对象提供以下方法:
process.exit():退出当前进程。
process.cwd():返回运行当前脚本的工作目录的路径。_
process.chdir():改变工作目录。
process.nextTick():将一个回调函数放在下次事件循环的顶部。

20.console有哪些常用方法?

console.log/console.info:用于标准输出流的输出,即在控制台显示一行信息
console.error/console.warning, 浏览器控制台输出前加一个警告标志,一个错误标志。
console.time/console.timeEnd, 主要用于统计一段代码运行的时间。console.time()置于代
码起始处,console.timeEnd():  方法置于代码结尾处。只需要向这两个方法传递统一个参
数,就可以看到在控制台中输入了以毫秒计的代码运行时间。
console.trace,:用于输出当前位置的栈信息,可以向console.trace()方法传递任意字符串
作为标志,类似于console.time()中的参数。
console.dir():用于将一个对象的信息输出到控件控制台。

21.node有哪些定时功能?

setTimeout()
setInterval()
setImmediate()
process.nextTick()
前两个是语言的标准,后两个是 Node 独有的。它们的写法差不多,作用也差不多,不太容易区别。

测试代码:
// test.js
setTimeout(() => console.log(1));
setImmediate(() => console.log(2));
process.nextTick(() => console.log(3));
Promise.resolve().then(() => console.log(4));
(() => console.log(5))();

运行结果如下:
$ node test.js
5
3
4
1
2
解释:首先,同步任务总是比异步任务更早执行。只有最后一行是同步任务,因此最早执行。
异步任务可以分成两种:1.追加在本轮循环的异步任务,2.追加在次轮循环的异步任务。

Node 规定,process.nextTick和Promise的回调函数,追加在本轮循环,即同步任务一旦执行完成,
就开始执行它们。而setTimeout、setInterval、setImmediate的回调函数,追加在次轮循环。这就是
说,文首那段代码的第三行和第四行,一定比第一行和第二行更早执行。

process.nextTick它是在本轮循环执行的,而且是所有异步任务里面最快执行的。Node 执行完所有
同步任务,接下来就会执行process.nextTick的任务队列。基本上,如果你希望异步任务尽可能快地
执行,那就使用process.nextTick。

Promise对象的回调函数,会进入异步任务里面的"微任务"(microtask)队列。微任务队列追加在
process.nextTick队列的后面,也属于本轮循环。所以,下面的总是先输出3,再输出4。注意,只有
前一个队列全部清空以后,才会执行下一个队列。

次轮循环的执行顺序,这就必须理解什么是事件循环:
事件循环是在主线程上完成的。Node 开始执行脚本时,会先进行事件循环的初始化,但是这时事件
循环还没有开始,会先完成下面的事情。
同步任务
发出异步请求
规划定时器生效的时间
执行process.nextTick()等等
上面这些事情都干完了,事件循环就正式开始了。
什么是事件循环?
事件循环会无限次地执行,一轮又一轮。只有异步任务的回调函数队列清空了,才会停止执行。
每一轮的事件循环,分成六个阶段。这些阶段会依次执行:
1.timers
2.I/O callbacks
3.idle, prepare
4.poll
5.check
6.close callbacks
每个阶段都有一个先进先出的回调函数队列。只有一个阶段的回调函数队列清空了,该执行的回调函
数都执行了,事件循环才会进入下一个阶段。
1)timers:这个是定时器阶段,处理setTimeout()和setInterval()的回调函数。进入这个阶段后,主
线程会检查一下当前时间,是否满足定时器的条件。如果满足就执行回调函数,否则就离开这个阶段。
2)I/O callbacks:除了以下操作的回调函数,其他的回调函数都在这个阶段执行。
setTimeout()和setInterval()的回调函数
setImmediate()的回调函数
用于关闭请求的回调函数,比如socket.on('close', ...)
3)idle, prepare:该阶段只供 libuv 内部调用,这里可以忽略。
4)Poll:这个阶段是轮询时间,用于等待还未返回的 I/O 事件,比如服务器的回应、用户移动鼠标
等等。这个阶段的时间会比较长。如果没有其他异步任务要处理(比如到期的定时器),会一直停留
在这个阶段,等待 I/O 请求返回结果。
5)check:该阶段执行setImmediate()的回调函数。
6)close callbacks:该阶段执行关闭请求的回调函数,比如socket.on('close', ...)。

由于setTimeout在 timers 阶段执行,而setImmediate在 check 阶段执行。所以,setTimeout会早于setImmediate完成。

22.node中的Buffer如何应用?

Buffer是用来处理二进制数据的,比如图片,mp3,数据库文件等.Buffer支持各种编码解码,二进制字
符串互转.Buffer类是一个全局类,是一个比较罕见不需要require( ‘buffer’ )就可以使用的类,Buffer
类似与数组也有length, 它里面的元素为16进制的两位数,即 0-255的数值,大小一经设置不可改变。

23.什么是EventEmitter?

1.Nodejs的大部分核心API都是基于异步事件驱动设计的,所有可以分发事件的对象都
是EventEmitter类的实例。由于Nodejs是单线程运行的,所以Nodejs需要借助事件轮
询,不断去查询事件队列中的事件消息,然后执行该事件对应的回调函数,有点类似windows的消息映射机制。
2.监听事件和分发事件:
EventEmitter实例可以使用on或addListener监听事件,emit()方法分发事件,如下所示:
const events = require('events'),
const EventEmitter = events.EventEmitter,
const util = require('util');
function myEmiter(){
  EventEmitter.call(this);
};
util.inherits(myEmiter,EventEmitter);//继承EventEmitter类
const myEmitterIns = new myEmiter();
myEmitterIns.on('data',(o)=>{
  console.log('receive the data:'+o.a);
});

或者使用class类:
class myEmiter extends EventEmitter{}//继承EventEmitter类
const myEmitterIns = new myEmiter();
 
myEmitterIns.on('data',(o)=>{
  console.log('receive the data:'+o.a);
});
myEmitterIns.emit('data',{a:1});

执行结果如下:receive the data:1

3.向事件监听回调函数传递参数
emit()方法可以传递任意的参数集合给回调函数,需要注意的一点是this关键字指向的
是调用emit方法的EventEmiter实例,但在箭头函数中例外,this指向的是全局this,因
为箭头函数中的this是在定义时绑定。如下所示:
class myEmiter extends EventEmitter{}
const myEmitterIns = new myEmiter();
myEmitterIns.on('data',function(data){
  console.log("普通回调函数中this:");
  console.log(this);
});
myEmitterIns.on('data1',(data1)=>{
  console.log("箭头回调函数中this:");
  console.log(this);
});
myEmitterIns.emit('data',{a:1});
myEmitterIns.emit('data1',{a:1});

这里讲到箭头函数中的this,就顺便说一下,为什么箭头函数能够实现定义时绑定
this,就是因为箭头函数内部根本就没有绑定this的机制,它使用的是外层作用域的
this,因此它也不能作为构造函数。

4.事件监听程序的执行顺序
EventEmiter实例可以绑定多个事件,当我们顺序触发这些事件时,EventEmiter会以
同步模式执行,既第一个事件处理函数没有完成,便不会触发下一个事件,如下所示:
class myEmiter extends EventEmitter{}
const myEmitterIns = new myEmiter();
myEmitterIns.on('data',function(data){
  console.time('data事件执行了');
  for(var i = 0 ; i< 100000; i++)
    for(var j = 0 ; j< 100000; j++)
      ;
  console.timeEnd('data事件执行了');
});
myEmitterIns.on('data1',(data1)=>{
  console.log("data1事件开始执行...");
});
myEmitterIns.emit('data',{a:1});
myEmitterIns.emit('data1',{a:1});

执行结果:data事件执行了: 4721.401ms  >   data1事件开始执行...


5.一次性事件监听
EventEmiter可以使用once监听某个事件,则该事件处理程序只会触发一次,之后emit
该事件都会被忽略,因为监听程序被注销了,如下所示:
myEmitterIns.once('one',(data)=>{
  console.log(data);
});
myEmitterIns.emit('one','this is first call!');
myEmitterIns.emit('one','this is second call!');
执行结果:this is first call!,不会执行第二个


6.EventEmitter有哪些典型应用:
1) 模块间传递消息 
2) 回调函数内外传递消息 
3) 处理流数据,因为流是在EventEmitter基础上实现的. 
4) 观察者模式发射触发机制相关应用

7.怎么捕获EventEmitter的错误事件:
监听error事件即可.如果有多个EventEmitter,也可以用domain来统一处理错误事件.
代码演示

    var domain = require('domain');
    var myDomain = domain.create();
    myDomain.on('error', function(err){
        console.log('domain接收到的错误事件:', err);
    }); // 接收事件并打印
    myDomain.run(function(){
        var emitter1 = new MyEmitter();
        emitter1.emit('error', '错误事件来自emitter1');
        emitter2 = new MyEmitter();
        emitter2.emit('error', '错误事件来自emitter2');
    });

8.EventEmitter中的newListenser事件有什么用处:
newListener可以用来做事件机制的反射,特殊应用,事件管理等.当任何on事件添加到
EventEmitter时,就会触发newListener事件,基于这种模式,我们可以做很多自定义处理.

代码演示
var emitter3 = new MyEmitter();
emitter3.on('newListener', function(name, listener) {
    console.log("新事件的名字:", name);
    console.log("新事件的代码:", listener);
    setTimeout(function(){ console.log("我是自定义延时处理机制"); }, 1000);
});
emitter3.on('hello', function(){
    console.log('hello node');
});

等等方法。

24.核心模块Stream

nodejs的核心模块,基本上都是stream的的实例,比如process.stdout、http.clientRequest。
stream是基于事件EventEmitter的数据管理模式.由各种不同的抽象接口组成,主要包括可写,可
读,可读写,可转换等几种类型.
在nodejs中,有四种stream类型:
Readable:用来读取数据,比如 fs.createReadStream()。
Writable:用来写数据,比如 fs.createWriteStream()。
Duplex:可读+可写,比如 net.Socket()。
Transform:在读写的过程中,可以对数据进行修改,比如 zlib.createDeflate()(数据压缩/解压)。

 stream(流)是Node.js提供的又一个仅在服务区端可用的模块,流是一种抽象的数据结构。Stream 
是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对
象就是一个 Stream,还有stdout(标准输出流)。


stream好处:非阻塞式数据处理提升效率,片断处理节省内存,管道处理方便可扩展等.
stream典型应用:文件,网络,数据转换,音频视频等.
stream错误捕获:监听error事件,方法同EventEmitter.


25.内置的fs模块架构

在 NodeJS 中,所有与文件操作都是通过 fs 核心模块来实现的,包括文件目录的创建、
删除、查询以及文件的读取和写入,在 fs 模块中,所有的方法都分为同步和异步两种实
现,具有 sync 后缀的方法为同步方法,不具有 sync 后缀的方法为异步方法,

fs模块主要由下面几部分组成: 
1) POSIX文件Wrapper,对应于操作系统的原生文件操作 
2) 文件流 fs.createReadStream和fs.createWriteStream 
3) 同步文件读写,fs.readFileSync和fs.writeFileSync 
4) 异步文件读写, fs.readFile和fs.writeFile

读写一个文件有多少种方法:
总体来说有四种: 
1) POSIX式低层读写 
2) 流式读写 
3) 同步文件读写 
4) 异步文件读写

怎么读取json配置文件:
主要有两种方式,第一种是利用node内置的require('data.json')机制,直接得到js对象; 第二种是读入
文件入内容,然后用JSON.parse(content)转换成js对象.二者的区别是require机制情况下,如果多
个模块都加载了同一个json文件,那么其中一个改变了js对象,其它跟着改变,这是由node模块的缓
存机制造成的,只有一个js模块对象; 第二种方式则可以随意改变加载后的js变量,而且各模块互不影
响,因为他们都是独立的,是多个js对象.

fs.watch和fs.watchFile有什么区别,怎么应用:
二者主要用来监听文件变动.fs.watch利用操作系统原生机制来监听,可能不适用网络文件系统; 
fs.watchFile则是定期检查文件状态变更,适用于网络文件系统,但是相比fs.watch有些慢,因为不是
实时机制.

26.node的网络模块架构是什么样子的?

node全面支持各种网络服务器和客户端,包括tcp, http/https, tcp, udp, dns, tls/ssl等.
实现一个简单的http服务器:
代码演示

    var http = require('http'); // 加载http模块
    http.createServer(function(req, res) {
        res.writeHead(200, {'Content-Type': 'text/html'}); // 200代表状态成功, 文档类型是给浏览器识别用的
        res.write(' 

我是标题啊!

初级'); // 返回给客户端的html数据 res.end(); // 结束输出流 }).listen(3000); // 绑定3ooo, 查看效果请访问 http://localhost:3000

27.child-process

1. 为什么需要child-process?
node是异步非阻塞的,这对高并发非常有效.可是我们还有其它一些常用需求,比如和操作系统
shell命令交互,调用可执行文件,创建子进程进行阻塞式访问或高CPU计算等,child-process就是为
满足这些需求而生的.child-process顾名思义,就是把node阻塞的工作交给子进程去做.

2.exec,execFile,spawn和fork都是做什么用的?
 exec可以用操作系统原生的方式执行各种命令,如管道 cat ab.txt | grep hello; execFile是执行一个
文件; spawn是流式和操作系统进行交互; fork是两个node程序(javascript)之间时行交互.

3.实现一个简单的命令行交互程序?
代码演示

    var cp = require('child_process');
    var child = cp.spawn('echo', ['你好', "钩子"]); // 执行命令
    child.stdout.pipe(process.stdout); // child.stdout是输入流,process.stdout是输出流
    // 这句的意思是将子进程的输出作为当前程序的输入流,然后重定向到当前程序的标准输出,即控制台
  1. node中的异步和同步怎么理解
1.异步: 每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任
务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的
排列顺序是不一致的、异步的。
2.同步: 后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的。
3.事件驱动:只有当事件发生时候才会调用回调函数,这种函数执行的方式叫做事件驱动。
4.事件循环:Event loop有大量的异步操作完成时需要调用相应回调函数,需要一种机制来管理执行
先后,这种机制就叫做事件循环。为一个回调函数队列,node.js不断查询队列中是否有事件,查询
到事件,调用相应javascript函数,机制为先进先出任务队列。
  1. 有哪些方法可以进行异步流程的控制
1) 多层嵌套回调 
2) 为每一个回调写单独的函数,函数里边再回调 
3) 用第三方框架比方async, promise等

30.怎样调节node执行单元的内存大小

 用--max-old-space-size 和 --max-new-space-size 来设置 v8 使用内存的上限

31.有没有用过apply(), bind(), call()这些方法,他们之间的区别

call() 、apply()可以看作是某个对象的方法,通过调用方法的形式来间接调用函数。bind() 就是将某
个函数绑定到某个对象上。call() 和apply()的第一个参数相同,就是指定的对象。这个对象就是该函
数的执行上下文。call()和apply()的区别就在于,两者之间的参数。call()在第一个参数之后的  后续所
有参数就是传入该函数的值。apply() 只有两个参数,第一个是对象,第二个是数组,这个数组就是该函数的参数。
bind() 方法和前两者不同在于: bind() 方法会返回执行上下文被改变的函数而不会立即执行,而前两
者是直接执行该函数。他的参数和call()相同。
这三个方法的作用都是改变函数的执行上下文!

32.promise

promise出现的目的一为处理JavaScript里的异步,再就是避免回调地狱。
Promise对象有三种状态,他们分别是:
pending: 等待中,或者进行中,表示还没有得到结果
resolved(Fulfilled): 已经完成,表示得到了我们想要的结果,可以继续往下执行
rejected: 也表示得到结果,但是由于结果并非我们所愿,因此拒绝执行

Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected,只要这
两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved

Promise什么情况下catch不到错误:
1.异步代码的运行时错误无法被自动 reject 进而被 catch 捕获,而是直接报错
2.resolve的东西,一定会进入then的第一个回调,肯定不会进入catch

33.async、await解释

async、await是es7的标准:
1.async/await 是一种编写异步代码的新方法。之前异步代码的方案是回调和 promise。
2.async/await 是建立在 promise 的基础上。
3.async/await 像 promise 一样,也是非阻塞的。
4.async/await 让异步代码看起来、表现起来更像同步代码。这正是其威力所在。

34.流程控制库async,自己在项目中常用到哪些方法

Async的内容分为三部分:
流程控制:简化十种常见流程的处理
集合处理:如何使用异步操作处理集合中的数据
工具类:个常用的工具类



使用Asycn模块需要安装,它不是node自带的。其次,该模块有大约20多个流程控制方法,我们在这里仅分析常用的
series, parallel, waterfall, auto这四种,并且尽量从原理上进行分析。
1.series()适合无依赖的异步串行执行:异步的串行执行(有先后顺序)
async.series([function(callback){
    fs.readFile('file1.txt','utf-8',callback)
},function(callback){
    fs.readFile('file2.txt','utf-8',callback)
}],function(err,results){
    //results => [file1.txt,file2.txt]
})
2.当前一个的结果是后一个调用的输入时,series()就无法满足需求。下面介绍的waterfall()可以解决这个问题
async.waterfall([
    function(callback){
        fs.readFile('file1.txt','utf-8',function(err,content){
            callback(err,content);
        });
    },
    function(arg1,callback){
        //arg1 => file2.txt
        fs.readFile(arg1,'utf-8',function(err,content){
            callback(err,content);
        });
    },
    function(arg1,callback){
        //arg1 => file3.txt
        fs.readFile(arg1,'utf-8',function(err,content){
            callback(err,content)
        });
    }
],function(err,result){
    //result => file4.txt
})
3.异步的并行执行(同时进行)
async.parallel([function(callback){
    fs.readFile('file1.txt','utf-8',callback)
},function(callback){
    fs.readFile('file2.txt','utf-8',callback)
}
],function(err,results){
    //results => [file1.txt,file2.txt]
})
4.自动依赖处理:
在现实的业务环境中,具有很多复杂的依赖关系,有异步的也有同步的,经常让人感觉混乱,理不清顺序,为此async提供
一个强大的方法auto()实现业务处理
如下面这个例子:
connectMongoDB和connectRedis依赖readConfig,uploadAsserts依赖compileAsserts,startup则依赖所有完成,依赖关系如下
var deps = {
    readConfig:function(callback){
        //rad config file
        callback();
    },
    connectMongoDB:['readConfig',function(callback){
        //connect to mongodb
        callback();
    }],
    connectRedis:['readConfig',function(callback){
        //connect to redis
        callback();
    }],
    complieAsserts:function(callback){
        //compile asserts
        callback();
    },
    uploadAsserts:['compileAsserts',function(callback){
        //upload to assert
        callback();
    }],
    startup:['connectMongoDB','connectRedis','uploadAsserts',function(callback){
        //start up 
    }]
}
async.auto(deps);// auto方法能根据依赖关系自动分析,以最佳的顺序执行以上业务 

async适合处理一系列按照特定顺序执行的复杂操作。

35.Node.js单线程、其应用场景

1.NodeJS的特点:
1. 它是一个Javascript运行环境
2. 依赖于Chrome V8引擎进行代码解释
3. 事件驱动
4. 非阻塞I/O
5. 轻量、可伸缩,适于实时数据交互应用
6. 单进程,单线程

优点:
1. 高并发(最重要的优点)
2. 适合I/O密集型应用
缺点:
1. 不适合CPU密集型应用;CPU密集型应用给Node带来的挑战主要是:由于JavaScript单线程的原因,如果有长时间运行
的计算(比如大循环),将会导致CPU时间片不能释放,使得后续I/O无法发起;解决方案:分解大型运算任务为多个小任
务,使得运算能够适时释放,不阻塞I/O调用的发起;
2. 只支持单核CPU,不能充分利用CPU
3. 可靠性低,一旦代码某个环节崩溃,整个系统都崩溃, 原因:单进程,单线程,
解决方案:1)Nnigx反向代理,负载均衡,开多个进程,绑定多个端口;
         2)开多个进程监听同一个端口,使用cluster模块;
4. 开源组件库质量参差不齐,更新快,向下不兼容
5. Debug不方便,错误没有stack trace

适合NodeJS的场景:
1. RESTful API:这是NodeJS最理想的应用场景,可以处理数万条连接,本身没有太多的逻辑,只需要请求API,组织数
据进行返回即可。它本质上只是从某个数据库中查找一些值并将它们组成一个响应。由于响应是少量文本,入站请求也是
少量的文本,因此流量不高,一台机器甚至也可以处理最繁忙的公司的API需求。
2. 大量Ajax请求的应用: 例如个性化应用,每个用户看到的页面都不一样,缓存失效,需要在页面加载的时候发起Ajax
请求,NodeJS能响应大量的并发请求,NodeJS适合运用在高并发、I/O密集、少量业务逻辑的场景

36.node如何实现高并发

nodejs是单线程且支持高并发的脚本语言。可为什么单线程的nodejs可以支持高并发呢?
取决于node的事件循环/事件驱动
1、每个Node.js进程只有一个主线程在执行程序代码,形成一个执行栈(execution context stack)。
2、主线程之外,还维护了一个"事件队列"(Event queue)。当用户的网络请求或者其它的异步操作到来时,node都会把
它放到Event Queue之中,此时并不会立即执行它,代码也不会被阻塞,继续往下走,直到主线程代码执行完毕。
3、主线程代码执行完毕完成后,然后通过Event Loop,也就是事件循环机制,开始到Event Queue的开头取出第一个事
件,从线程池中分配一个线程去执行这个事件,接下来继续取出第二个事件,再从线程池中分配一个线程去执行,然后第
三个,第四个。主线程不断的检查事件队列中是否有未执行的事件,直到事件队列中所有事件都执行完了,此后每当有新
的事件加入到事件队列中,都会通知主线程按顺序取出交EventLoop处理。当有事件执行完毕后,会通知主线程,主线程执行回调,线程归还给线程池。
4、主线程不断重复上面的第三步。

总结:
1、Nodejs与操作系统交互,我们在 Javascript中调用的方法,最终都会通过 process.binding 传递到 C/C++ 层面,最终由
他们来执行真正的操作。Node.js 即这样与操作系统进行互动。
2、nodejs所谓的单线程,只是主线程是单线程,所有的网络请求或者异步任务都交给了内部的线程池去实现,本身只负责
不断的往返调度,由事件循环不断驱动事件执行。
3、Nodejs之所以单线程可以处理高并发的原因,得益于libuv层的事件循环机制,和底层线程池实现。
4、Event loop就是主线程从主线程的事件队列里面不停循环的读取事件,驱动了所有的异步回调函数的执行,Event loop
总共7个阶段,每个阶段都有一个任务队列,当所有阶段被顺序执行一次后,event loop 完成了一个 tick。

37.事件循环以及其优先级?


38.MongoDB的聚合函数,用到过哪些参数

MongoDB的聚合函数的使用,有利于我们对MongoDB中的集合进行进一步的拆分:
操作符介绍:
$project:包含、排除、重命名和显示字段
$match:查询,需要同find()一样的参数
$limit:限制结果数量
$skip:忽略结果的数量
$sort:按照给定的字段排序结果
$group:按照给定表达式组合结果
$unwind:分割嵌入数组到自己顶层文件

39.为什么我们要使用MongoDB?

特点:高性能、易部署、易使用,存储数据非常方便。
主要功能特性有:
1.面向集合存储,易存储对象类型的数据。适合存储对象及JSON形式的数据。
2.模式自由。没有数据类型限制
3.支持动态查询。Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。
4.支持完全索引,包含内部对象。包括文档内嵌对象及数组。Mongo的查询优化器会分析查询表达式,并生成一个高效的查询计划。
5.支持复制和故障恢复。Mongo数据库支持服务器之间的数据复制,支持主-从模式及服务器之间的相互复制。
复制的主要目标是提供冗余及自动故障转移。
6.使用高效的二进制数据存储,包括大型对象(如视频等)。支持二进制数据及大型对象(如照片或图片)
7.自动处理碎片,以支持云计算层次的扩展性。自动分片功能支持水平的数据库集群,可动态添加额外的机器。
8.支持Python,PHP,Ruby,Java,C,C#,Javascript,Perl及C++语言的驱动程序,社区中也提供了对Erlang及.NET等平台的驱动程序。
9.文件存储格式为BSON(一种JSON的扩展)

MongoDB要注意的问题:
1 因为MongoDB是全索引的,所以它直接把索引放在内存中,因此最多支持2.5G的数据。如果是64位的会更多。
2 因为没有恢复机制,因此要做好数据备份
3 因为默认监听地址是127.0.0.1,因此要进行身份验证,否则不够安全;如果是自己使用,建议配置成localhost主机名

40.node.js的事件模型/事件循环

Node.js不是用多个线程为每个请求执行工作的,相反而是它把所有工作添加到一个事件队列中,然后有一个单
独线程,来循环提取队列中的事件。事件循环线程抓取事件队列中最上面的条目,执行它,然后抓取下一个条
目。当执行长期运行或有阻塞I/O的代码时,注意这里:它不会被阻塞,会继续提取下一个事件,而对于被阻塞
的事件Node.js会从线程池中取出一个线程来运行这个被阻塞的代码,同时把当前事件本身和它的回调事件一同
添加到事件队列(callback嵌套callback)。
当Node.js事件队列中的所有事件都被执行完成时,Node.js应用程序终止。

nodejs的依赖库,包括大名鼎鼎的V8、libuv。
V8: 我们都知道,是google开发的一套高效javascript运行时,nodejs能够高效执行 js 代码的很大原因主要在它。
libuv:是用C语言实现的一套异步功能库,nodejs高效的异步编程模型很大程度上归功于libuv的实现,而libuv则是我们今天重点要分析的。
nodejs实现异步机制的核心便是libuv,libuv承担着nodejs与文件、网络等异步任务的沟通桥梁.
nodejs的网络I/O、文件I/O、DNS操作、还有一些用户代码都是在 libuv 工作的。
归纳下nodejs里的异步事件:
非I/O:
1.定时器(setTimeout,setInterval)
2.microtask(promise)
3.process.nextTick
4.setImmediate
5.DNS.lookup
I/O:
1.网络I/O
2.文件I/O
3.一些DNS操作

libuv内部还维护着一个默认4个线程的线程池,这些线程负责执行文件I/O操作、DNS操作、用户异步代码。当 js 层传递给
 libuv 一个操作任务时,libuv 会把这个任务加到队列中。之后分两种情况:
1、线程池中的线程都被占用的时候,队列中任务就要进行排队等待空闲线程。
2、线程池中有可用线程时,从队列中取出这个任务执行,执行完毕后,线程归还到线程池,等待下个任务。同时以事件的
方式通知event-loop,event-loop接收到事件执行该事件注册的回调函数。

事件循环原理:
一.node 的初始化
1.1.初始化 node 环境。
1.2.执行输入代码。
1.3.执行 process.nextTick 回调。
1.4.执行 microtasks。
![事件循环图.png](https://upload-images.jianshu.io/upload_images/2668212-61177a5b84011786.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

二.进入 event-loop阶段 (事件循环)
1.进入 timers 阶段
1.1.检查 timer 队列是否有到期的 timer 回调,如果有,将到期的 timer 回调按照 timerId 升序执行。
1.2.检查是否有 process.nextTick 任务,如果有,全部执行。
1.3.检查是否有microtask,如果有,全部执行。
1.4.退出该阶段。

2.进入IO callbacks阶段。 ------------- 执行你的 fs.read, socket 等 IO 操作的回调函数, 同时也包括各种 error 的回调
2.1.检查是否有 pending(延迟) 的 I/O 回调。如果有,执行回调。如果没有,退出该阶段。
2.2.检查是否有 process.nextTick 任务,如果有,全部执行。
2.3.检查是否有microtask,如果有,全部执行。
2.4.退出该阶段。

3.进入 idle,prepare 阶段:
3.1.这两个阶段与我们编程关系不大

4.进入 poll 阶段 --------作用是等待异步请求和数据
4.1.首先检查是否存在尚未完成的回调,如果存在,那么分两种情况。
第一种情况:
1.如果有可用回调(可用回调包含到期的定时器还有一些IO事件等),执行所有可用回调。
2.检查是否有 process.nextTick 回调,如果有,全部执行。
3.检查是否有 microtaks(微任务Promise),如果有,全部执行。
4.退出该阶段。
第二种情况:
1.如果没有可用回调。
2.检查是否有 setImmediate 回调,如果有,退出 poll 阶段。如果没有,阻塞在此阶段,等待新的事件通知。

4.2.如果不存在尚未完成的回调,退出poll阶段。

5.进入 check 阶段。
5.1.如果有setImmediate回调,则执行所有setImmediate回调。
5.2.检查是否有 process.nextTick 回调,如果有,全部执行。
5.3.检查是否有 microtaks(微任务Promise),如果有,全部执行。
5.4.退出 check 阶段

6.进入 closing 阶段。
6.1.如果有setImmediate回调,则执行所有setImmediate回调。
6.2.检查是否有 process.nextTick 回调,如果有,全部执行。
6.3.检查是否有 microtaks(微任务Promise),如果有,全部执行。
6.4.退出 closing 阶段

7.检查是否有活跃的 handles(定时器、IO等事件句柄)。
7.1.如果有,继续下一轮循环。
7.2.如果没有,结束事件循环,退出程序。

41.node 消息队列


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