Node.js

1.什么是nodejs

Node.js采用模塊化結(jié)構(gòu),按照CommonJS規(guī)范定義和使用模塊。模塊與文件是一一對
應(yīng)關(guān)系,即加載一個模塊,實(shí)際上就是加載對應(yīng)的一個模塊文件。Node.js 被設(shè)計用來
開發(fā)大規(guī)模高并發(fā)的網(wǎng)絡(luò)應(yīng)用,這種網(wǎng)絡(luò)應(yīng)用的瓶頸之一是在 I/O 的處理效率上。由
于硬件及網(wǎng)絡(luò)的限制,I/O 的速度往往是固定的,如何在此前提下盡可能處理更多的客
戶請求,提高 CPU 使用效率,便成了開發(fā)人員面臨的最大問題。得益于基于事件驅(qū)動
的編程模型,Node.js 使用單一的 Event loop 線程處理客戶請求,將 I/O 操作分派至
各異步處理模塊(這里一般人不理解,node.js包含很多模塊,這些模塊可以使用js直
接調(diào)用系統(tǒng)的api),既解決了單線程模式下 I/O 阻塞的問題,又避免了多線程模式下
資源分配及搶占的問題。

單線程模式:客戶端發(fā)起一個 I/O 請求(數(shù)據(jù)庫查詢),然后等待服務(wù)器端返回 I/O 結(jié)
果,結(jié)果返回后再對其進(jìn)行操作,但這種請求常常需要很長時間,這一過程中,服務(wù)器
無法接受新的請求,即阻塞式 I/O。這種處理方式雖然簡單,卻不實(shí)用,尤其是面對大量請求的時候

多線程模式:服務(wù)器為每個請求分配一個線程,所有任務(wù)均在自己的線程內(nèi)執(zhí)行,服
務(wù)器每創(chuàng)建一個線程,每個線程大概會占用 2M 的系統(tǒng)內(nèi)存,而且線程之間的切換也
會降低服務(wù)器的處理效率,基于成本的考慮,這種處理方式也有一定的局限性。開發(fā)
多線程程序非常困難,容易出錯。程序員需考慮死鎖,數(shù)據(jù)不一致等問題,多線程的
程序極難調(diào)試和測試?;旧显诔绦蜻\(yùn)行出錯的時候,程序員才知道自己的程序有錯誤。

事件驅(qū)動:客戶發(fā)起 I/O 請求的同時傳入一個函數(shù),該函數(shù)會在 I/O 結(jié)果返回后被自
動調(diào)用,而且該請求不會阻塞后續(xù)操作。所有請求以及同時傳入的回調(diào)函數(shù)均發(fā)送至
同一線程,該線程通常叫做 Event loop 線程,該線程負(fù)責(zé)在 I/O 執(zhí)行完畢后,將結(jié)果
返回給回調(diào)函數(shù)。這里要注意的是 I/O 操作本身并不在該線程內(nèi)執(zhí)行,所以不會阻塞后續(xù)請求。

2.模塊

編寫稍大一點(diǎn)的程序時一般都會將代碼模塊化。在NodeJS中,一般將代碼合理拆分到
不同的JS文件中,每一個文件就是一個模塊,而文件路徑就是模塊名。在編寫每個模
塊時,都有require、exports、module三個預(yù)先定義好的變量可供使用。

require函數(shù)用于在當(dāng)前模塊中加載和使用別的模塊,傳入一個模塊名,返回一個模塊
導(dǎo)出對象。模塊名可使用相對路徑(以./開頭),或者是絕對路徑(以/或C:之類的盤符開頭)。

exports對象是當(dāng)前模塊的導(dǎo)出對象,用于導(dǎo)出模塊公有方法和屬性。別的模塊通過
require函數(shù)使用當(dāng)前模塊時得到的就是當(dāng)前模塊的exports對象。

通過module對象可以訪問到當(dāng)前模塊的一些相關(guān)信息,但最多的用途是替換當(dāng)前模塊
的導(dǎo)出對象。例如模塊導(dǎo)出對象默認(rèn)是一個普通對象,如果想改成一個函數(shù)的話,可以使用以下方式。

3.node.js相對于傳統(tǒng)服務(wù)器的好處? 為什么選js為它的開發(fā)代碼?

1.Nodejs基于Javascript語言,不用再單獨(dú)新學(xué)一門陌生的語言,從而減低了學(xué)習(xí)的門
檻。同時,Javascript語言在Web前端開發(fā)中至關(guān)重要,特別HTML5的應(yīng)用必須要使
用,所以前后臺統(tǒng)一語言,不僅可以實(shí)現(xiàn)程序員的全棧開發(fā),還可以統(tǒng)一公共類庫,代碼標(biāo)準(zhǔn)化。
2.Nodejs并沒有重新開發(fā)運(yùn)行時環(huán)境,而是選擇了目前最快的瀏覽器內(nèi)核V8做為執(zhí)行
引擎,保證了Nodejs的性能和穩(wěn)定性。
3.動態(tài)語言:開發(fā)效率非常高,并有能力構(gòu)建復(fù)雜系統(tǒng),
4.性能和I/O負(fù)載:Nodejs非常好的解決了IO密集的問題,通過異步IO來實(shí)現(xiàn)。
5.操作性:實(shí)現(xiàn)了Nodejs對于內(nèi)存堆棧的監(jiān)控系統(tǒng)

為什么選擇js:   JavaScript 則是支持函數(shù)式編程范型的語言,很好地契合了 Node.js 
基于事件驅(qū)動的編程模型。加之 Google 提供的 V8 引擎,使 JavaScript 語言的執(zhí)行
速度大大提高。

4.常用js類定義的方法有哪些?

1.主要有構(gòu)造函數(shù)原型和對象創(chuàng)建兩種方法。原型法是通用老方法,對象創(chuàng)建是ES5
推薦使用的方法.目前來看,原型法更普遍.
1) 構(gòu)造函數(shù)方法定義類
    function Person(){
        this.name = 'michaelqin';
    }
    Person.prototype.sayName = function(){
        alert(this.name);
    }

    var person = new Person();
    person.sayName();
2) 對象創(chuàng)建方法定義類

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

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

5.js類繼承的方法有哪些

原型鏈法,屬性復(fù)制法和構(gòu)造器應(yīng)用法. 另外,由于每個對象可以是一個類,這些方法
也可以用于對象類的繼承.
1) 原型鏈法

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

    function Person() {}
    Person.prototype = Animal.prototype; // 人繼承自動物
    Person.prototype.constructor = 'Person'; // 更新構(gòu)造函數(shù)為人
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];
    } // 復(fù)制動物的所有屬性到人量邊
    Person.prototype.constructor = 'Person'; // 更新構(gòu)造函數(shù)為人
3) 構(gòu)造器應(yīng)用法

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

    function Person() {
        Animal.call(this); // apply, call, bind方法都可以.細(xì)微區(qū)別,后面會提到.
    }
  1. js類多重繼承的實(shí)現(xiàn)方法是怎么樣的?
就是類繼承里邊的屬性復(fù)制法來實(shí)現(xiàn).因?yàn)楫?dāng)所有父類的prototype屬性被復(fù)制后,子
類自然擁有類似行為和屬性.
  1. js里的作用域是什么樣子的?
大多數(shù)語言里邊都是塊作作用域,以{}進(jìn)行限定,js里邊不是.js里邊叫函數(shù)作用域,
就是一個變量在全函數(shù)里有效.比如有個變量p1在函數(shù)最后一行定義,第一行也有
效,但是值是undefined.
this指的是對象本身,而不是構(gòu)造函數(shù).只有函數(shù)執(zhí)行的時候才能確定this到底指向
誰,實(shí)際上this的最終指向的是那個調(diào)用它的對象。
代碼演示

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

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

9.apply, call和bind有什么區(qū)別?

三者都可以把一個函數(shù)應(yīng)用到其他對象上,注意不是自身對象.a(chǎn)pply,call是直接執(zhí)行
函數(shù)調(diào)用,bind是綁定,執(zhí)行需要再次調(diào)用.a(chǎn)pply和call的區(qū)別是apply接受數(shù)組作為
參數(shù),而call是接受逗號分隔的無限多個參數(shù)列表,
它們在功能上是沒有區(qū)別的,都是改變this的指向,它們的區(qū)別主要是在于方法的實(shí)現(xiàn)
形式和參數(shù)傳遞上的不同
1. 函數(shù).call(對象,arg1,arg2....)
2. 函數(shù).apply(對象,[arg1,arg2,...])
3. var ss=函數(shù).bind(對象,arg1,arg2,....)     ss()調(diào)用一下

代碼演示

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

    var obj = {name: 'michaelqin'}; // 注意這是一個普通對象,它不是Person的實(shí)例
    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需要先綁定,再執(zhí)行 
    sn(param1, param2, param3); // bind需要先綁定,再執(zhí)行
  1. caller, callee和arguments分別是什么?
arguments是函數(shù)的所有參數(shù)列表,它是一個類數(shù)組的變量
caller:在一個函數(shù)調(diào)用另一個函數(shù)時,被調(diào)用函數(shù)會自動生成一個caller屬性,指向
調(diào)用它的函數(shù)對象。如果該函數(shù)當(dāng)前未被調(diào)用,或并非被其他函數(shù)調(diào)用,則caller為null。
callee當(dāng)函數(shù)被調(diào)用時,它的arguments.callee對象就會指向自身,也就是一個對自己
的引用。由于arguments在函數(shù)被調(diào)用時才有效,因此arguments.callee在函數(shù)未調(diào)用
時是不存在的(即null.callee),且解引用它會產(chǎn)生異常。
代碼演示

    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.什么是閉包?以及閉包的優(yōu)點(diǎn),缺點(diǎn),用處,及特性

 定義:閉包 當(dāng)一個函數(shù)的返回值是另外一個函數(shù),而返回的那個函數(shù)如果調(diào)用了其父函
數(shù)內(nèi)部的變量,且返回的這個函數(shù)在外部被執(zhí)行就產(chǎn)生了閉包.閉包是一個環(huán)境,具體指的就是外部函數(shù)
用處:
1.讀取函數(shù)內(nèi)部的變量;
2.這些變量的值始終保持在內(nèi)存中,不會在外層函數(shù)調(diào)用后被自動清除。

優(yōu)點(diǎn):
1:變量長期駐扎在內(nèi)存中;
2:避免全局變量的污染;
3:私有成員的存在 ;

特性:
1:函數(shù)套函數(shù);
2:內(nèi)部函數(shù)可以直接使用外部函數(shù)的局部變量或參數(shù);
3:變量或參數(shù)不會被垃圾回收機(jī)制回收 GC;

缺點(diǎn):
1.常駐內(nèi)存 會增大內(nèi)存的使用量 使用不當(dāng)會造成內(nèi)存泄露,由于閉包會使得函數(shù)中的
變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會造成網(wǎng)頁的性能
問題,在IE中可能導(dǎo)致內(nèi)存泄露。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除。
2.閉包會在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。所以,如果你把父函數(shù)當(dāng)作對象
(object)使用,把閉包當(dāng)作它的公用方法(Public Method),把內(nèi)部變量當(dāng)作它的
私有屬性(private value),這時一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值。

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

1.Object.defineProperty(obj, prop, descriptor)用來給對象定義屬性,有value, writable, 
configurable, enumerable, set/get等
2.hasOwnProerty用于檢查某一屬性是不是存在于對象本身,繼承來的父親的屬性不算.
3.isEnumerable用來檢測某一屬性是否可遍歷,也就是能不能用for..in循環(huán)來取到.

13.js常用設(shè)計模式的實(shí)現(xiàn)思路,單例,工廠,代理,裝飾,觀察者模式等

1) 單例: 任意對象都是單例,無須特別處理
    var obj = {name: 'michaelqin', age: 30};

2) 工廠: 就是同樣形式參數(shù)返回不同的實(shí)例
    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.說說字符串常用的十個函數(shù)?

charAt() 返回在指定位置的字符。
concat() 連接字符串。
fromCharCode() 從字符編碼創(chuàng)建一個字符串。
indexOf() 檢索字符串。
match() 找到一個或多個正則表達(dá)式的匹配。
replace() 替換與正則表達(dá)式匹配的子串。
search() 檢索與正則表達(dá)式相匹配的值。
slice() 提取字符串的片斷,并在新的字符串中返回被提取的部分。
split() 把字符串分割為字符串?dāng)?shù)組。
substr() 從起始索引號提取字符串中指定數(shù)目的字符。
substring() 提取字符串中兩個指定的索引號之間的字符。
toLocaleLowerCase() 把字符串轉(zhuǎn)換為小寫。
toLocaleUpperCase() 把字符串轉(zhuǎn)換為大寫。
toLowerCase() 把字符串轉(zhuǎn)換為小寫。
toUpperCase() 把字符串轉(zhuǎn)換為大寫。
toString() 返回字符串。
valueOf() 返回某個字符串對象的原始值。

15.為什么使用node

總結(jié)起來node有以下幾個特點(diǎn):簡單強(qiáng)大,輕量可擴(kuò)展.簡單體現(xiàn)在node使用的是
javascript,json來進(jìn)行編碼,人人都會;強(qiáng)大體現(xiàn)在非阻塞IO,可以適應(yīng)分塊傳輸數(shù)據(jù),
較慢的網(wǎng)絡(luò)環(huán)境,尤其擅長高并發(fā)訪問;輕量體現(xiàn)在node本身既是代碼,又是服務(wù)
器,前后端使用統(tǒng)一語言;可擴(kuò)展體現(xiàn)在可以輕松應(yīng)對多實(shí)例,多服務(wù)器架構(gòu),同時有海量的第三方應(yīng)用組件.

16.node的構(gòu)架是什么樣子的?

主要分為三層,應(yīng)用app >> V8及node內(nèi)置架構(gòu) >> 操作系統(tǒng). 
V8是node運(yùn)行的環(huán)境,可以理解為node虛擬機(jī).
node內(nèi)置架構(gòu)又可分為三層: 核心模塊(javascript實(shí)現(xiàn)) >> c++綁定 >> libuv + CAes + http.

17.node有哪些核心模塊?

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

18.node全局對象

process, console, Buffer和exports
  1. process有哪些常用方法?
process模塊用來與當(dāng)前進(jìn)程互動,可以通過全局變量process訪問,不必使用require命令
加載。它是一個EventEmitter對象的實(shí)例。
process對象提供一系列屬性,用于返回系統(tǒng)信息:

process.pid:當(dāng)前進(jìn)程的進(jìn)程號。
process.version:Node的版本,比如v0.10.18。
process.platform:當(dāng)前系統(tǒng)平臺,比如Linux。
process.title:默認(rèn)值為“node”,可以自定義該值。
process.argv:當(dāng)前進(jìn)程的命令行參數(shù)數(shù)組。
process.env:指向當(dāng)前shell的環(huán)境變量,比如process.env.HOME。
process.execPath:運(yùn)行當(dāng)前進(jìn)程的可執(zhí)行文件的絕對路徑。
process.stdout:指向標(biāo)準(zhǔn)輸出。
process.stdin:指向標(biāo)準(zhǔn)輸入。
process.stderr:指向標(biāo)準(zhǔn)錯誤。

process對象提供以下方法:
process.exit():退出當(dāng)前進(jìn)程。
process.cwd():返回運(yùn)行當(dāng)前腳本的工作目錄的路徑。_
process.chdir():改變工作目錄。
process.nextTick():將一個回調(diào)函數(shù)放在下次事件循環(huán)的頂部。

20.console有哪些常用方法?

console.log/console.info:用于標(biāo)準(zhǔn)輸出流的輸出,即在控制臺顯示一行信息
console.error/console.warning, 瀏覽器控制臺輸出前加一個警告標(biāo)志,一個錯誤標(biāo)志。
console.time/console.timeEnd, 主要用于統(tǒng)計一段代碼運(yùn)行的時間。console.time()置于代
碼起始處,console.timeEnd():  方法置于代碼結(jié)尾處。只需要向這兩個方法傳遞統(tǒng)一個參
數(shù),就可以看到在控制臺中輸入了以毫秒計的代碼運(yùn)行時間。
console.trace,:用于輸出當(dāng)前位置的棧信息,可以向console.trace()方法傳遞任意字符串
作為標(biāo)志,類似于console.time()中的參數(shù)。
console.dir():用于將一個對象的信息輸出到控件控制臺。

21.node有哪些定時功能?

setTimeout()
setInterval()
setImmediate()
process.nextTick()
前兩個是語言的標(biāo)準(zhǔn),后兩個是 Node 獨(dú)有的。它們的寫法差不多,作用也差不多,不太容易區(qū)別。

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

運(yùn)行結(jié)果如下:
$ node test.js
5
3
4
1
2
解釋:首先,同步任務(wù)總是比異步任務(wù)更早執(zhí)行。只有最后一行是同步任務(wù),因此最早執(zhí)行。
異步任務(wù)可以分成兩種:1.追加在本輪循環(huán)的異步任務(wù),2.追加在次輪循環(huán)的異步任務(wù)。

Node 規(guī)定,process.nextTick和Promise的回調(diào)函數(shù),追加在本輪循環(huán),即同步任務(wù)一旦執(zhí)行完成,
就開始執(zhí)行它們。而setTimeout、setInterval、setImmediate的回調(diào)函數(shù),追加在次輪循環(huán)。這就是
說,文首那段代碼的第三行和第四行,一定比第一行和第二行更早執(zhí)行。

process.nextTick它是在本輪循環(huán)執(zhí)行的,而且是所有異步任務(wù)里面最快執(zhí)行的。Node 執(zhí)行完所有
同步任務(wù),接下來就會執(zhí)行process.nextTick的任務(wù)隊(duì)列。基本上,如果你希望異步任務(wù)盡可能快地
執(zhí)行,那就使用process.nextTick。

Promise對象的回調(diào)函數(shù),會進(jìn)入異步任務(wù)里面的"微任務(wù)"(microtask)隊(duì)列。微任務(wù)隊(duì)列追加在
process.nextTick隊(duì)列的后面,也屬于本輪循環(huán)。所以,下面的總是先輸出3,再輸出4。注意,只有
前一個隊(duì)列全部清空以后,才會執(zhí)行下一個隊(duì)列。

次輪循環(huán)的執(zhí)行順序,這就必須理解什么是事件循環(huán):
事件循環(huán)是在主線程上完成的。Node 開始執(zhí)行腳本時,會先進(jìn)行事件循環(huán)的初始化,但是這時事件
循環(huán)還沒有開始,會先完成下面的事情。
同步任務(wù)
發(fā)出異步請求
規(guī)劃定時器生效的時間
執(zhí)行process.nextTick()等等
上面這些事情都干完了,事件循環(huán)就正式開始了。
什么是事件循環(huán)?
事件循環(huán)會無限次地執(zhí)行,一輪又一輪。只有異步任務(wù)的回調(diào)函數(shù)隊(duì)列清空了,才會停止執(zhí)行。
每一輪的事件循環(huán),分成六個階段。這些階段會依次執(zhí)行:
1.timers
2.I/O callbacks
3.idle, prepare
4.poll
5.check
6.close callbacks
每個階段都有一個先進(jìn)先出的回調(diào)函數(shù)隊(duì)列。只有一個階段的回調(diào)函數(shù)隊(duì)列清空了,該執(zhí)行的回調(diào)函
數(shù)都執(zhí)行了,事件循環(huán)才會進(jìn)入下一個階段。
1)timers:這個是定時器階段,處理setTimeout()和setInterval()的回調(diào)函數(shù)。進(jìn)入這個階段后,主
線程會檢查一下當(dāng)前時間,是否滿足定時器的條件。如果滿足就執(zhí)行回調(diào)函數(shù),否則就離開這個階段。
2)I/O callbacks:除了以下操作的回調(diào)函數(shù),其他的回調(diào)函數(shù)都在這個階段執(zhí)行。
setTimeout()和setInterval()的回調(diào)函數(shù)
setImmediate()的回調(diào)函數(shù)
用于關(guān)閉請求的回調(diào)函數(shù),比如socket.on('close', ...)
3)idle, prepare:該階段只供 libuv 內(nèi)部調(diào)用,這里可以忽略。
4)Poll:這個階段是輪詢時間,用于等待還未返回的 I/O 事件,比如服務(wù)器的回應(yīng)、用戶移動鼠標(biāo)
等等。這個階段的時間會比較長。如果沒有其他異步任務(wù)要處理(比如到期的定時器),會一直停留
在這個階段,等待 I/O 請求返回結(jié)果。
5)check:該階段執(zhí)行setImmediate()的回調(diào)函數(shù)。
6)close callbacks:該階段執(zhí)行關(guān)閉請求的回調(diào)函數(shù),比如socket.on('close', ...)。

由于setTimeout在 timers 階段執(zhí)行,而setImmediate在 check 階段執(zhí)行。所以,setTimeout會早于setImmediate完成。

22.node中的Buffer如何應(yīng)用?

Buffer是用來處理二進(jìn)制數(shù)據(jù)的,比如圖片,mp3,數(shù)據(jù)庫文件等.Buffer支持各種編碼解碼,二進(jìn)制字
符串互轉(zhuǎn).Buffer類是一個全局類,是一個比較罕見不需要require( ‘buffer’ )就可以使用的類,Buffer
類似與數(shù)組也有l(wèi)ength, 它里面的元素為16進(jìn)制的兩位數(shù),即 0-255的數(shù)值,大小一經(jīng)設(shè)置不可改變。

23.什么是EventEmitter?

1.Nodejs的大部分核心API都是基于異步事件驅(qū)動設(shè)計的,所有可以分發(fā)事件的對象都
是EventEmitter類的實(shí)例。由于Nodejs是單線程運(yùn)行的,所以Nodejs需要借助事件輪
詢,不斷去查詢事件隊(duì)列中的事件消息,然后執(zhí)行該事件對應(yīng)的回調(diào)函數(shù),有點(diǎn)類似windows的消息映射機(jī)制。
2.監(jiān)聽事件和分發(fā)事件:
EventEmitter實(shí)例可以使用on或addListener監(jiān)聽事件,emit()方法分發(fā)事件,如下所示:
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});

執(zhí)行結(jié)果如下:receive the data:1

3.向事件監(jiān)聽回調(diào)函數(shù)傳遞參數(shù)
emit()方法可以傳遞任意的參數(shù)集合給回調(diào)函數(shù),需要注意的一點(diǎn)是this關(guān)鍵字指向的
是調(diào)用emit方法的EventEmiter實(shí)例,但在箭頭函數(shù)中例外,this指向的是全局this,因
為箭頭函數(shù)中的this是在定義時綁定。如下所示:
class myEmiter extends EventEmitter{}
const myEmitterIns = new myEmiter();
myEmitterIns.on('data',function(data){
  console.log("普通回調(diào)函數(shù)中this:");
  console.log(this);
});
myEmitterIns.on('data1',(data1)=>{
  console.log("箭頭回調(diào)函數(shù)中this:");
  console.log(this);
});
myEmitterIns.emit('data',{a:1});
myEmitterIns.emit('data1',{a:1});

這里講到箭頭函數(shù)中的this,就順便說一下,為什么箭頭函數(shù)能夠?qū)崿F(xiàn)定義時綁定
this,就是因?yàn)榧^函數(shù)內(nèi)部根本就沒有綁定this的機(jī)制,它使用的是外層作用域的
this,因此它也不能作為構(gòu)造函數(shù)。

4.事件監(jiān)聽程序的執(zhí)行順序
EventEmiter實(shí)例可以綁定多個事件,當(dāng)我們順序觸發(fā)這些事件時,EventEmiter會以
同步模式執(zhí)行,既第一個事件處理函數(shù)沒有完成,便不會觸發(fā)下一個事件,如下所示:
class myEmiter extends EventEmitter{}
const myEmitterIns = new myEmiter();
myEmitterIns.on('data',function(data){
  console.time('data事件執(zhí)行了');
  for(var i = 0 ; i< 100000; i++)
    for(var j = 0 ; j< 100000; j++)
      ;
  console.timeEnd('data事件執(zhí)行了');
});
myEmitterIns.on('data1',(data1)=>{
  console.log("data1事件開始執(zhí)行...");
});
myEmitterIns.emit('data',{a:1});
myEmitterIns.emit('data1',{a:1});

執(zhí)行結(jié)果:data事件執(zhí)行了: 4721.401ms  >   data1事件開始執(zhí)行...


5.一次性事件監(jiān)聽
EventEmiter可以使用once監(jiān)聽某個事件,則該事件處理程序只會觸發(fā)一次,之后emit
該事件都會被忽略,因?yàn)楸O(jiān)聽程序被注銷了,如下所示:
myEmitterIns.once('one',(data)=>{
  console.log(data);
});
myEmitterIns.emit('one','this is first call!');
myEmitterIns.emit('one','this is second call!');
執(zhí)行結(jié)果:this is first call!,不會執(zhí)行第二個


6.EventEmitter有哪些典型應(yīng)用:
1) 模塊間傳遞消息 
2) 回調(diào)函數(shù)內(nèi)外傳遞消息 
3) 處理流數(shù)據(jù),因?yàn)榱魇窃贓ventEmitter基礎(chǔ)上實(shí)現(xiàn)的. 
4) 觀察者模式發(fā)射觸發(fā)機(jī)制相關(guān)應(yīng)用

7.怎么捕獲EventEmitter的錯誤事件:
監(jiān)聽error事件即可.如果有多個EventEmitter,也可以用domain來統(tǒng)一處理錯誤事件.
代碼演示

    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可以用來做事件機(jī)制的反射,特殊應(yīng)用,事件管理等.當(dāng)任何on事件添加到
EventEmitter時,就會觸發(fā)newListener事件,基于這種模式,我們可以做很多自定義處理.

代碼演示
var emitter3 = new MyEmitter();
emitter3.on('newListener', function(name, listener) {
    console.log("新事件的名字:", name);
    console.log("新事件的代碼:", listener);
    setTimeout(function(){ console.log("我是自定義延時處理機(jī)制"); }, 1000);
});
emitter3.on('hello', function(){
    console.log('hello node');
});

等等方法。

24.核心模塊Stream

nodejs的核心模塊,基本上都是stream的的實(shí)例,比如process.stdout、http.clientRequest。
stream是基于事件EventEmitter的數(shù)據(jù)管理模式.由各種不同的抽象接口組成,主要包括可寫,可
讀,可讀寫,可轉(zhuǎn)換等幾種類型.
在nodejs中,有四種stream類型:
Readable:用來讀取數(shù)據(jù),比如 fs.createReadStream()。
Writable:用來寫數(shù)據(jù),比如 fs.createWriteStream()。
Duplex:可讀+可寫,比如 net.Socket()。
Transform:在讀寫的過程中,可以對數(shù)據(jù)進(jìn)行修改,比如 zlib.createDeflate()(數(shù)據(jù)壓縮/解壓)。

 stream(流)是Node.js提供的又一個僅在服務(wù)區(qū)端可用的模塊,流是一種抽象的數(shù)據(jù)結(jié)構(gòu)。Stream 
是一個抽象接口,Node 中有很多對象實(shí)現(xiàn)了這個接口。例如,對http 服務(wù)器發(fā)起請求的request 對
象就是一個 Stream,還有stdout(標(biāo)準(zhǔn)輸出流)。


stream好處:非阻塞式數(shù)據(jù)處理提升效率,片斷處理節(jié)省內(nèi)存,管道處理方便可擴(kuò)展等.
stream典型應(yīng)用:文件,網(wǎng)絡(luò),數(shù)據(jù)轉(zhuǎn)換,音頻視頻等.
stream錯誤捕獲:監(jiān)聽error事件,方法同EventEmitter.


25.內(nèi)置的fs模塊架構(gòu)

在 NodeJS 中,所有與文件操作都是通過 fs 核心模塊來實(shí)現(xiàn)的,包括文件目錄的創(chuàng)建、
刪除、查詢以及文件的讀取和寫入,在 fs 模塊中,所有的方法都分為同步和異步兩種實(shí)
現(xiàn),具有 sync 后綴的方法為同步方法,不具有 sync 后綴的方法為異步方法,

fs模塊主要由下面幾部分組成: 
1) POSIX文件Wrapper,對應(yīng)于操作系統(tǒng)的原生文件操作 
2) 文件流 fs.createReadStream和fs.createWriteStream 
3) 同步文件讀寫,fs.readFileSync和fs.writeFileSync 
4) 異步文件讀寫, fs.readFile和fs.writeFile

讀寫一個文件有多少種方法:
總體來說有四種: 
1) POSIX式低層讀寫 
2) 流式讀寫 
3) 同步文件讀寫 
4) 異步文件讀寫

怎么讀取json配置文件:
主要有兩種方式,第一種是利用node內(nèi)置的require('data.json')機(jī)制,直接得到j(luò)s對象; 第二種是讀入
文件入內(nèi)容,然后用JSON.parse(content)轉(zhuǎn)換成js對象.二者的區(qū)別是require機(jī)制情況下,如果多
個模塊都加載了同一個json文件,那么其中一個改變了js對象,其它跟著改變,這是由node模塊的緩
存機(jī)制造成的,只有一個js模塊對象; 第二種方式則可以隨意改變加載后的js變量,而且各模塊互不影
響,因?yàn)樗麄兌际仟?dú)立的,是多個js對象.

fs.watch和fs.watchFile有什么區(qū)別,怎么應(yīng)用:
二者主要用來監(jiān)聽文件變動.fs.watch利用操作系統(tǒng)原生機(jī)制來監(jiān)聽,可能不適用網(wǎng)絡(luò)文件系統(tǒng); 
fs.watchFile則是定期檢查文件狀態(tài)變更,適用于網(wǎng)絡(luò)文件系統(tǒng),但是相比fs.watch有些慢,因?yàn)椴皇?實(shí)時機(jī)制.

26.node的網(wǎng)絡(luò)模塊架構(gòu)是什么樣子的?

node全面支持各種網(wǎng)絡(luò)服務(wù)器和客戶端,包括tcp, http/https, tcp, udp, dns, tls/ssl等.
實(shí)現(xiàn)一個簡單的http服務(wù)器:
代碼演示

    var http = require('http'); // 加載http模塊
    http.createServer(function(req, res) {
        res.writeHead(200, {'Content-Type': 'text/html'}); // 200代表狀態(tài)成功, 文檔類型是給瀏覽器識別用的
        res.write('<meta charset="UTF-8"> <h1>我是標(biāo)題??!</h1> <font color="red">初級</font>'); 
        // 返回給客戶端的html數(shù)據(jù)
        res.end(); // 結(jié)束輸出流
    }).listen(3000); // 綁定3ooo, 查看效果請訪問 http://localhost:3000 

27.child-process

1. 為什么需要child-process?
node是異步非阻塞的,這對高并發(fā)非常有效.可是我們還有其它一些常用需求,比如和操作系統(tǒng)
shell命令交互,調(diào)用可執(zhí)行文件,創(chuàng)建子進(jìn)程進(jìn)行阻塞式訪問或高CPU計算等,child-process就是為
滿足這些需求而生的.child-process顧名思義,就是把node阻塞的工作交給子進(jìn)程去做.

2.exec,execFile,spawn和fork都是做什么用的?
 exec可以用操作系統(tǒng)原生的方式執(zhí)行各種命令,如管道 cat ab.txt | grep hello; execFile是執(zhí)行一個
文件; spawn是流式和操作系統(tǒng)進(jìn)行交互; fork是兩個node程序(javascript)之間時行交互.

3.實(shí)現(xiàn)一個簡單的命令行交互程序?
代碼演示

    var cp = require('child_process');
    var child = cp.spawn('echo', ['你好', "鉤子"]); // 執(zhí)行命令
    child.stdout.pipe(process.stdout); // child.stdout是輸入流,process.stdout是輸出流
    // 這句的意思是將子進(jìn)程的輸出作為當(dāng)前程序的輸入流,然后重定向到當(dāng)前程序的標(biāo)準(zhǔn)輸出,即控制臺
  1. node中的異步和同步怎么理解
1.異步: 每一個任務(wù)有一個或多個回調(diào)函數(shù)(callback),前一個任務(wù)結(jié)束后,不是執(zhí)行后一個任
務(wù),而是執(zhí)行回調(diào)函數(shù),后一個任務(wù)則是不等前一個任務(wù)結(jié)束就執(zhí)行,所以程序的執(zhí)行順序與任務(wù)的
排列順序是不一致的、異步的。
2.同步: 后一個任務(wù)等待前一個任務(wù)結(jié)束,然后再執(zhí)行,程序的執(zhí)行順序與任務(wù)的排列順序是一致的、同步的。
3.事件驅(qū)動:只有當(dāng)事件發(fā)生時候才會調(diào)用回調(diào)函數(shù),這種函數(shù)執(zhí)行的方式叫做事件驅(qū)動。
4.事件循環(huán):Event loop有大量的異步操作完成時需要調(diào)用相應(yīng)回調(diào)函數(shù),需要一種機(jī)制來管理執(zhí)行
先后,這種機(jī)制就叫做事件循環(huán)。為一個回調(diào)函數(shù)隊(duì)列,node.js不斷查詢隊(duì)列中是否有事件,查詢
到事件,調(diào)用相應(yīng)javascript函數(shù),機(jī)制為先進(jìn)先出任務(wù)隊(duì)列。
  1. 有哪些方法可以進(jìn)行異步流程的控制
1) 多層嵌套回調(diào) 
2) 為每一個回調(diào)寫單獨(dú)的函數(shù),函數(shù)里邊再回調(diào) 
3) 用第三方框架比方async, promise等

30.怎樣調(diào)節(jié)node執(zhí)行單元的內(nèi)存大小

 用--max-old-space-size 和 --max-new-space-size 來設(shè)置 v8 使用內(nèi)存的上限

31.有沒有用過apply(), bind(), call()這些方法,他們之間的區(qū)別

call() 、apply()可以看作是某個對象的方法,通過調(diào)用方法的形式來間接調(diào)用函數(shù)。bind() 就是將某
個函數(shù)綁定到某個對象上。call() 和apply()的第一個參數(shù)相同,就是指定的對象。這個對象就是該函
數(shù)的執(zhí)行上下文。call()和apply()的區(qū)別就在于,兩者之間的參數(shù)。call()在第一個參數(shù)之后的  后續(xù)所
有參數(shù)就是傳入該函數(shù)的值。apply() 只有兩個參數(shù),第一個是對象,第二個是數(shù)組,這個數(shù)組就是該函數(shù)的參數(shù)。
bind() 方法和前兩者不同在于: bind() 方法會返回執(zhí)行上下文被改變的函數(shù)而不會立即執(zhí)行,而前兩
者是直接執(zhí)行該函數(shù)。他的參數(shù)和call()相同。
這三個方法的作用都是改變函數(shù)的執(zhí)行上下文!

32.promise

promise出現(xiàn)的目的一為處理JavaScript里的異步,再就是避免回調(diào)地獄。
Promise對象有三種狀態(tài),他們分別是:
pending: 等待中,或者進(jìn)行中,表示還沒有得到結(jié)果
resolved(Fulfilled): 已經(jīng)完成,表示得到了我們想要的結(jié)果,可以繼續(xù)往下執(zhí)行
rejected: 也表示得到結(jié)果,但是由于結(jié)果并非我們所愿,因此拒絕執(zhí)行

Promise對象的狀態(tài)改變,只有兩種可能:從pending變?yōu)閒ulfilled和從pending變?yōu)閞ejected,只要這
兩種情況發(fā)生,狀態(tài)就凝固了,不會再變了,會一直保持這個結(jié)果,這時就稱為 resolved

Promise什么情況下catch不到錯誤:
1.異步代碼的運(yùn)行時錯誤無法被自動 reject 進(jìn)而被 catch 捕獲,而是直接報錯
2.resolve的東西,一定會進(jìn)入then的第一個回調(diào),肯定不會進(jìn)入catch

33.async、await解釋

async、await是es7的標(biāo)準(zhǔn):
1.async/await 是一種編寫異步代碼的新方法。之前異步代碼的方案是回調(diào)和 promise。
2.async/await 是建立在 promise 的基礎(chǔ)上。
3.async/await 像 promise 一樣,也是非阻塞的。
4.async/await 讓異步代碼看起來、表現(xiàn)起來更像同步代碼。這正是其威力所在。

34.流程控制庫async,自己在項(xiàng)目中常用到哪些方法

Async的內(nèi)容分為三部分:
流程控制:簡化十種常見流程的處理
集合處理:如何使用異步操作處理集合中的數(shù)據(jù)
工具類:個常用的工具類



使用Asycn模塊需要安裝,它不是node自帶的。其次,該模塊有大約20多個流程控制方法,我們在這里僅分析常用的
series, parallel, waterfall, auto這四種,并且盡量從原理上進(jìn)行分析。
1.series()適合無依賴的異步串行執(zhí)行:異步的串行執(zhí)行(有先后順序)
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.當(dāng)前一個的結(jié)果是后一個調(diào)用的輸入時,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.異步的并行執(zhí)行(同時進(jìn)行)
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.自動依賴處理:
在現(xiàn)實(shí)的業(yè)務(wù)環(huán)境中,具有很多復(fù)雜的依賴關(guān)系,有異步的也有同步的,經(jīng)常讓人感覺混亂,理不清順序,為此async提供
一個強(qiáng)大的方法auto()實(shí)現(xiàn)業(yè)務(wù)處理
如下面這個例子:
connectMongoDB和connectRedis依賴readConfig,uploadAsserts依賴compileAsserts,startup則依賴所有完成,依賴關(guān)系如下
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方法能根據(jù)依賴關(guān)系自動分析,以最佳的順序執(zhí)行以上業(yè)務(wù) 

async適合處理一系列按照特定順序執(zhí)行的復(fù)雜操作。

35.Node.js單線程、其應(yīng)用場景

1.NodeJS的特點(diǎn):
1. 它是一個Javascript運(yùn)行環(huán)境
2. 依賴于Chrome V8引擎進(jìn)行代碼解釋
3. 事件驅(qū)動
4. 非阻塞I/O
5. 輕量、可伸縮,適于實(shí)時數(shù)據(jù)交互應(yīng)用
6. 單進(jìn)程,單線程

優(yōu)點(diǎn):
1. 高并發(fā)(最重要的優(yōu)點(diǎn))
2. 適合I/O密集型應(yīng)用
缺點(diǎn):
1. 不適合CPU密集型應(yīng)用;CPU密集型應(yīng)用給Node帶來的挑戰(zhàn)主要是:由于JavaScript單線程的原因,如果有長時間運(yùn)行
的計算(比如大循環(huán)),將會導(dǎo)致CPU時間片不能釋放,使得后續(xù)I/O無法發(fā)起;解決方案:分解大型運(yùn)算任務(wù)為多個小任
務(wù),使得運(yùn)算能夠適時釋放,不阻塞I/O調(diào)用的發(fā)起;
2. 只支持單核CPU,不能充分利用CPU
3. 可靠性低,一旦代碼某個環(huán)節(jié)崩潰,整個系統(tǒng)都崩潰, 原因:單進(jìn)程,單線程,
解決方案:1)Nnigx反向代理,負(fù)載均衡,開多個進(jìn)程,綁定多個端口;
         2)開多個進(jìn)程監(jiān)聽同一個端口,使用cluster模塊;
4. 開源組件庫質(zhì)量參差不齊,更新快,向下不兼容
5. Debug不方便,錯誤沒有stack trace

適合NodeJS的場景:
1. RESTful API:這是NodeJS最理想的應(yīng)用場景,可以處理數(shù)萬條連接,本身沒有太多的邏輯,只需要請求API,組織數(shù)
據(jù)進(jìn)行返回即可。它本質(zhì)上只是從某個數(shù)據(jù)庫中查找一些值并將它們組成一個響應(yīng)。由于響應(yīng)是少量文本,入站請求也是
少量的文本,因此流量不高,一臺機(jī)器甚至也可以處理最繁忙的公司的API需求。
2. 大量Ajax請求的應(yīng)用: 例如個性化應(yīng)用,每個用戶看到的頁面都不一樣,緩存失效,需要在頁面加載的時候發(fā)起Ajax
請求,NodeJS能響應(yīng)大量的并發(fā)請求,NodeJS適合運(yùn)用在高并發(fā)、I/O密集、少量業(yè)務(wù)邏輯的場景

36.node如何實(shí)現(xiàn)高并發(fā)

nodejs是單線程且支持高并發(fā)的腳本語言。可為什么單線程的nodejs可以支持高并發(fā)呢?
取決于node的事件循環(huán)/事件驅(qū)動
1、每個Node.js進(jìn)程只有一個主線程在執(zhí)行程序代碼,形成一個執(zhí)行棧(execution context stack)。
2、主線程之外,還維護(hù)了一個"事件隊(duì)列"(Event queue)。當(dāng)用戶的網(wǎng)絡(luò)請求或者其它的異步操作到來時,node都會把
它放到Event Queue之中,此時并不會立即執(zhí)行它,代碼也不會被阻塞,繼續(xù)往下走,直到主線程代碼執(zhí)行完畢。
3、主線程代碼執(zhí)行完畢完成后,然后通過Event Loop,也就是事件循環(huán)機(jī)制,開始到Event Queue的開頭取出第一個事
件,從線程池中分配一個線程去執(zhí)行這個事件,接下來繼續(xù)取出第二個事件,再從線程池中分配一個線程去執(zhí)行,然后第
三個,第四個。主線程不斷的檢查事件隊(duì)列中是否有未執(zhí)行的事件,直到事件隊(duì)列中所有事件都執(zhí)行完了,此后每當(dāng)有新
的事件加入到事件隊(duì)列中,都會通知主線程按順序取出交EventLoop處理。當(dāng)有事件執(zhí)行完畢后,會通知主線程,主線程執(zhí)行回調(diào),線程歸還給線程池。
4、主線程不斷重復(fù)上面的第三步。

總結(jié):
1、Nodejs與操作系統(tǒng)交互,我們在 Javascript中調(diào)用的方法,最終都會通過 process.binding 傳遞到 C/C++ 層面,最終由
他們來執(zhí)行真正的操作。Node.js 即這樣與操作系統(tǒng)進(jìn)行互動。
2、nodejs所謂的單線程,只是主線程是單線程,所有的網(wǎng)絡(luò)請求或者異步任務(wù)都交給了內(nèi)部的線程池去實(shí)現(xiàn),本身只負(fù)責(zé)
不斷的往返調(diào)度,由事件循環(huán)不斷驅(qū)動事件執(zhí)行。
3、Nodejs之所以單線程可以處理高并發(fā)的原因,得益于libuv層的事件循環(huán)機(jī)制,和底層線程池實(shí)現(xiàn)。
4、Event loop就是主線程從主線程的事件隊(duì)列里面不停循環(huán)的讀取事件,驅(qū)動了所有的異步回調(diào)函數(shù)的執(zhí)行,Event loop
總共7個階段,每個階段都有一個任務(wù)隊(duì)列,當(dāng)所有階段被順序執(zhí)行一次后,event loop 完成了一個 tick。

37.事件循環(huán)以及其優(yōu)先級?


38.MongoDB的聚合函數(shù),用到過哪些參數(shù)

MongoDB的聚合函數(shù)的使用,有利于我們對MongoDB中的集合進(jìn)行進(jìn)一步的拆分:
操作符介紹:
$project:包含、排除、重命名和顯示字段
$match:查詢,需要同find()一樣的參數(shù)
$limit:限制結(jié)果數(shù)量
$skip:忽略結(jié)果的數(shù)量
$sort:按照給定的字段排序結(jié)果
$group:按照給定表達(dá)式組合結(jié)果
$unwind:分割嵌入數(shù)組到自己頂層文件

39.為什么我們要使用MongoDB?

特點(diǎn):高性能、易部署、易使用,存儲數(shù)據(jù)非常方便。
主要功能特性有:
1.面向集合存儲,易存儲對象類型的數(shù)據(jù)。適合存儲對象及JSON形式的數(shù)據(jù)。
2.模式自由。沒有數(shù)據(jù)類型限制
3.支持動態(tài)查詢。Mongo支持豐富的查詢表達(dá)式。查詢指令使用JSON形式的標(biāo)記,可輕易查詢文檔中內(nèi)嵌的對象及數(shù)組。
4.支持完全索引,包含內(nèi)部對象。包括文檔內(nèi)嵌對象及數(shù)組。Mongo的查詢優(yōu)化器會分析查詢表達(dá)式,并生成一個高效的查詢計劃。
5.支持復(fù)制和故障恢復(fù)。Mongo數(shù)據(jù)庫支持服務(wù)器之間的數(shù)據(jù)復(fù)制,支持主-從模式及服務(wù)器之間的相互復(fù)制。
復(fù)制的主要目標(biāo)是提供冗余及自動故障轉(zhuǎn)移。
6.使用高效的二進(jìn)制數(shù)據(jù)存儲,包括大型對象(如視頻等)。支持二進(jìn)制數(shù)據(jù)及大型對象(如照片或圖片)
7.自動處理碎片,以支持云計算層次的擴(kuò)展性。自動分片功能支持水平的數(shù)據(jù)庫集群,可動態(tài)添加額外的機(jī)器。
8.支持Python,PHP,Ruby,Java,C,C#,Javascript,Perl及C++語言的驅(qū)動程序,社區(qū)中也提供了對Erlang及.NET等平臺的驅(qū)動程序。
9.文件存儲格式為BSON(一種JSON的擴(kuò)展)

MongoDB要注意的問題:
1 因?yàn)镸ongoDB是全索引的,所以它直接把索引放在內(nèi)存中,因此最多支持2.5G的數(shù)據(jù)。如果是64位的會更多。
2 因?yàn)闆]有恢復(fù)機(jī)制,因此要做好數(shù)據(jù)備份
3 因?yàn)槟J(rèn)監(jiān)聽地址是127.0.0.1,因此要進(jìn)行身份驗(yàn)證,否則不夠安全;如果是自己使用,建議配置成localhost主機(jī)名

40.node.js的事件模型/事件循環(huán)

Node.js不是用多個線程為每個請求執(zhí)行工作的,相反而是它把所有工作添加到一個事件隊(duì)列中,然后有一個單
獨(dú)線程,來循環(huán)提取隊(duì)列中的事件。事件循環(huán)線程抓取事件隊(duì)列中最上面的條目,執(zhí)行它,然后抓取下一個條
目。當(dāng)執(zhí)行長期運(yùn)行或有阻塞I/O的代碼時,注意這里:它不會被阻塞,會繼續(xù)提取下一個事件,而對于被阻塞
的事件Node.js會從線程池中取出一個線程來運(yùn)行這個被阻塞的代碼,同時把當(dāng)前事件本身和它的回調(diào)事件一同
添加到事件隊(duì)列(callback嵌套callback)。
當(dāng)Node.js事件隊(duì)列中的所有事件都被執(zhí)行完成時,Node.js應(yīng)用程序終止。

nodejs的依賴庫,包括大名鼎鼎的V8、libuv。
V8: 我們都知道,是google開發(fā)的一套高效javascript運(yùn)行時,nodejs能夠高效執(zhí)行 js 代碼的很大原因主要在它。
libuv:是用C語言實(shí)現(xiàn)的一套異步功能庫,nodejs高效的異步編程模型很大程度上歸功于libuv的實(shí)現(xiàn),而libuv則是我們今天重點(diǎn)要分析的。
nodejs實(shí)現(xiàn)異步機(jī)制的核心便是libuv,libuv承擔(dān)著nodejs與文件、網(wǎng)絡(luò)等異步任務(wù)的溝通橋梁.
nodejs的網(wǎng)絡(luò)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.網(wǎng)絡(luò)I/O
2.文件I/O
3.一些DNS操作

libuv內(nèi)部還維護(hù)著一個默認(rèn)4個線程的線程池,這些線程負(fù)責(zé)執(zhí)行文件I/O操作、DNS操作、用戶異步代碼。當(dāng) js 層傳遞給
 libuv 一個操作任務(wù)時,libuv 會把這個任務(wù)加到隊(duì)列中。之后分兩種情況:
1、線程池中的線程都被占用的時候,隊(duì)列中任務(wù)就要進(jìn)行排隊(duì)等待空閑線程。
2、線程池中有可用線程時,從隊(duì)列中取出這個任務(wù)執(zhí)行,執(zhí)行完畢后,線程歸還到線程池,等待下個任務(wù)。同時以事件的
方式通知event-loop,event-loop接收到事件執(zhí)行該事件注冊的回調(diào)函數(shù)。

事件循環(huán)原理:
一.node 的初始化
1.1.初始化 node 環(huán)境。
1.2.執(zhí)行輸入代碼。
1.3.執(zhí)行 process.nextTick 回調(diào)。
1.4.執(zhí)行 microtasks。
![事件循環(huán)圖.png](https://upload-images.jianshu.io/upload_images/2668212-61177a5b84011786.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

二.進(jìn)入 event-loop階段 (事件循環(huán))
1.進(jìn)入 timers 階段
1.1.檢查 timer 隊(duì)列是否有到期的 timer 回調(diào),如果有,將到期的 timer 回調(diào)按照 timerId 升序執(zhí)行。
1.2.檢查是否有 process.nextTick 任務(wù),如果有,全部執(zhí)行。
1.3.檢查是否有microtask,如果有,全部執(zhí)行。
1.4.退出該階段。

2.進(jìn)入IO callbacks階段。 ------------- 執(zhí)行你的 fs.read, socket 等 IO 操作的回調(diào)函數(shù), 同時也包括各種 error 的回調(diào)
2.1.檢查是否有 pending(延遲) 的 I/O 回調(diào)。如果有,執(zhí)行回調(diào)。如果沒有,退出該階段。
2.2.檢查是否有 process.nextTick 任務(wù),如果有,全部執(zhí)行。
2.3.檢查是否有microtask,如果有,全部執(zhí)行。
2.4.退出該階段。

3.進(jìn)入 idle,prepare 階段:
3.1.這兩個階段與我們編程關(guān)系不大

4.進(jìn)入 poll 階段 --------作用是等待異步請求和數(shù)據(jù)
4.1.首先檢查是否存在尚未完成的回調(diào),如果存在,那么分兩種情況。
第一種情況:
1.如果有可用回調(diào)(可用回調(diào)包含到期的定時器還有一些IO事件等),執(zhí)行所有可用回調(diào)。
2.檢查是否有 process.nextTick 回調(diào),如果有,全部執(zhí)行。
3.檢查是否有 microtaks(微任務(wù)Promise),如果有,全部執(zhí)行。
4.退出該階段。
第二種情況:
1.如果沒有可用回調(diào)。
2.檢查是否有 setImmediate 回調(diào),如果有,退出 poll 階段。如果沒有,阻塞在此階段,等待新的事件通知。

4.2.如果不存在尚未完成的回調(diào),退出poll階段。

5.進(jìn)入 check 階段。
5.1.如果有setImmediate回調(diào),則執(zhí)行所有setImmediate回調(diào)。
5.2.檢查是否有 process.nextTick 回調(diào),如果有,全部執(zhí)行。
5.3.檢查是否有 microtaks(微任務(wù)Promise),如果有,全部執(zhí)行。
5.4.退出 check 階段

6.進(jìn)入 closing 階段。
6.1.如果有setImmediate回調(diào),則執(zhí)行所有setImmediate回調(diào)。
6.2.檢查是否有 process.nextTick 回調(diào),如果有,全部執(zhí)行。
6.3.檢查是否有 microtaks(微任務(wù)Promise),如果有,全部執(zhí)行。
6.4.退出 closing 階段

7.檢查是否有活躍的 handles(定時器、IO等事件句柄)。
7.1.如果有,繼續(xù)下一輪循環(huán)。
7.2.如果沒有,結(jié)束事件循環(huán),退出程序。

41.node 消息隊(duì)列


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • # 模塊機(jī)制 node采用模塊化結(jié)構(gòu),按照CommonJS規(guī)范定義和使用模塊,模塊與文件是一一對應(yīng)關(guān)系,即加載一個...
    RichRand閱讀 2,744評論 0 3
  • 作為一個在Sun微系統(tǒng)公司Java SE團(tuán)隊(duì)工作了十多年的人,難道不應(yīng)該是體內(nèi)流淌著Java字節(jié)碼的血、只要一息尚...
    Java架構(gòu)學(xué)習(xí)者閱讀 2,627評論 4 17
  • Node.js是目前非常火熱的技術(shù),但是它的誕生經(jīng)歷卻很奇特。 眾所周知,在Netscape設(shè)計出JavaScri...
    Myselfyan閱讀 4,203評論 2 58
  • 筆者前段時間(2018.09-2018.10)參加面試,地點(diǎn)深圳,面試的崗位是NodeJS后臺開發(fā),工作年限是1 ...
    鷗曉栢閱讀 28,636評論 6 46
  • textview也可以接受點(diǎn)擊事件,我們可是使用XML文件產(chǎn)生不同狀態(tài)下的點(diǎn)擊效果,比如點(diǎn)擊,選中,默認(rèn)狀態(tài)。但是...
    UniThan閱讀 896評論 0 0

友情鏈接更多精彩內(nèi)容