原文: github.com/jimuyouyou/…javascript
Node是搞後端的,不該該被被歸爲前端,更不該該用前端的觀點去理解,去面試node開發人員。因此這份面試題大全,更側重後端應用與對Node核心的理解。css
參考答案:類的支持,模塊化,箭頭操做符,let/const塊做用域,字符串模板,解構,參數默認值/不定參數/拓展參數, for-of遍歷, generator, Map/Set, Promisehtml
參考答案:ES6必火!從軟件工程角度來看,之前真的很弱,不適合作大型應用,很容易致使爛尾工程。ES6就至關於當年的Java5,是歷史性的發展,今後咱們能夠用js作大型項目了。事實上,各大主流瀏覽器如今已經支持大部分新特性了,後端的Node.js更是能夠直接使用ES6的絕大多數語法。前端
推薦ES6入門好書: 阮一峯 ECMAScript 6入門html5
參考答案:主要有構造函數原型和對象建立兩種方法。原型法是通用老方法,對象建立是ES5推薦使用的方法.目前來看,原型法更廣泛.java
代碼演示node
function Person(){
this.name = 'michaelqin';
}
Person.prototype.sayName = function(){
alert(this.name);
}
var person = new Person();
person.sayName();
複製代碼
var Person = {
name: 'michaelqin',
sayName: function(){ alert(this.name); }
};
var person = Object.create(Person);
person.sayName();
複製代碼
參考答案:原型鏈法,屬性複製法和構造器應用法. 另外,因爲每一個對象能夠是一個類,這些方法也能夠用於對象類的繼承.jquery
代碼演示linux
function Animal() {
this.name = 'animal';
}
Animal.prototype.sayName = function(){
alert(this.name);
};
function Person() {}
Person.prototype = Animal.prototype; // 人繼承自動物
Person.prototype.constructor = 'Person'; // 更新構造函數爲人
複製代碼
function Animal() {
this.name = 'animal';
}
Animal.prototype.sayName = function() {
alert(this.name);
};
function Person() {}
for(prop in Animal.prototype) {
Person.prototype[prop] = Animal.prototype[prop];
} // 複製動物的全部屬性到人量邊
Person.prototype.constructor = 'Person'; // 更新構造函數爲人
複製代碼
function Animal() {
this.name = 'animal';
}
Animal.prototype.sayName = function() {
alert(this.name);
};
function Person() {
Animal.call(this); // apply, call, bind方法均可以.細微區別,後面會提到.
}
複製代碼
參考答案:就是類繼承裏邊的屬性複製法來實現.由於當全部父類的prototype屬性被複制後,子類天然擁有相似行爲和屬性.css3
參考答案:大多數語言裏邊都是塊做做用域,以{}進行限定,js裏邊不是.js裏邊叫函數做用域,就是一個變量在全函數裏有效.好比有個變量p1在函數最後一行定義,第一行也有效,可是值是undefined.
代碼演示
var globalVar = 'global var';
function test() {
alert(globalVar); // undefined, 由於globalVar在本函數內被重定義了,致使全局失效,這裏使用函數內的變量值,但是此時還沒定義
var globalVar = 'overrided var'; // globalVar在本函數內被重定義
alert(globalVar); // overrided var
}
alert(globalVar); // global var,使用全局變量
複製代碼
參考答案: this指的是對象自己,而不是構造函數.
代碼演示
function Person() {
}
Person.prototype.sayName() { alert(this.name); }
var person1 = new Person();
person1.name = 'michaelqin';
person1.sayName(); // michaelqin
複製代碼
參考答案:三者均可以把一個函數應用到其餘對象上,注意不是自身對象.apply,call是直接執行函數調用,bind是綁定,執行須要再次調用.apply和call的區別是apply接受數組做爲參數,而call是接受逗號分隔的無限多個參數列表,
代碼演示
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須要先綁定,再執行
複製代碼
參考答案: caller,callee之間的關係就像是employer和employee之間的關係,就是調用與被調用的關係,兩者返回的都是函數對象引用.arguments是函數的全部參數列表,它是一個類數組的變量.
代碼演示
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');
複製代碼
參考答案: 閉包這個術語,不管中文翻譯仍是英文解釋都太2B了,我必須罵人,由於它什麼其實都不是.非要講它是什麼的話,兩個字函數,更多字嵌套函數的父子自我引用關係.全部函數都是閉包.通俗的說,閉包就是做用域範圍,由於js是函數做用域,因此函數就是閉包.全局函數的做用域範圍就是全局,因此無須討論.更多的應用實際上是在內嵌函數,這就會涉及到內嵌做用域,或者叫做用域鏈.說到內嵌,其實就是父子引用關係(父函數包含子函數,子函數由於函數做用域又引用父函數,這它媽不是死結嗎?因此叫閉包),這就會帶來另一個問題,何時引用結束?若是不結束,就會一直佔用內存,引發內存泄漏.好吧,不用的時候就引用設爲空,死結就解開了.
參考答案:Object.defineProperty(obj, prop, descriptor)用來給對象定義屬性,有value,writable,configurable,enumerable,set/get等.hasOwnProerty用於檢查某一屬性是否是存在於對象自己,繼承來的父親的屬性不算.propertyIsEnumerable用來檢測某一屬性是否可遍歷,也就是能不能用for..in循環來取到.
參考答案:
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
3) 代理: 就是新建個類調用老類的接口,包一下
function Person() { }
Person.prototype.sayName = function() { console.log('michaelqin'); }
Person.prototype.sayAge = function() { console.log(30); }
function PersonProxy() {
this.person = new Person();
var that = this;
this.callMethod = function(functionName) {
console.log('before proxy:', functionName);
that.person[functionName](); // 代理
console.log('after proxy:', functionName);
}
}
var pp = new PersonProxy();
pp.callMethod('sayName'); // 代理調用Person的方法sayName()
pp.callMethod('sayAge'); // 代理調用Person的方法sayAge()
4) 觀察者: 就是事件模式,好比按鈕的onclick這樣的應用.
function Publisher() {
this.listeners = [];
}
Publisher.prototype = {
'addListener': function(listener) {
this.listeners.push(listener);
},
'removeListener': function(listener) {
delete this.listeners[this.listeners.indexOf(listener)];
},
'notify': function(obj) {
for(var i = 0; i < this.listeners.length; i++) {
var listener = this.listeners[i];
if (typeof listener !== 'undefined') {
listener.process(obj);
}
}
}
}; // 發佈者
function Subscriber() {
}
Subscriber.prototype = {
'process': function(obj) {
console.log(obj);
}
}; // 訂閱者
var publisher = new Publisher();
publisher.addListener(new Subscriber());
publisher.addListener(new Subscriber());
publisher.notify({name: 'michaelqin', ageo: 30}); // 發佈一個對象到全部訂閱者
publisher.notify('2 subscribers will both perform process'); // 發佈一個字符串到全部訂閱者
複製代碼
參考答案: push/pop, shift/unshift, split/join, slice/splice/concat, sort/reverse, map/reduce, forEach, filter
參考答案: indexOf/lastIndexOf/charAt, split/match/test, slice/substring/substr, toLowerCase/toUpperCase
參考答案: 總結起來node有如下幾個特色:簡單強大,輕量可擴展.簡單體如今node使用的是javascript,json來進行編碼,人人都會;強大致如今非阻塞IO,能夠適應分塊傳輸數據,較慢的網絡環境,尤爲擅長高併發訪問;輕量體如今node自己既是代碼,又是服務器,先後端使用統一語言;可擴展體如今能夠輕鬆應對多實例,多服務器架構,同時有海量的第三方應用組件.
參考答案: 主要分爲三層,應用app >> V8及node內置架構 >> 操做系統. V8是node運行的環境,能夠理解爲node虛擬機.node內置架構又可分爲三層: 核心模塊(javascript實現) >> c++綁定 >> libuv + CAes + http.
參考答案: EventEmitter, Stream, FS, Net和全局對象
參考答案: process, console, Buffer
參考答案: process.stdin, process.stdout, process.stderr, process.on, process.env, process.argv, process.arch, process.platform, process.exit
參考答案: console.log/console.info, console.error/console.warning, console.time/console.timeEnd, console.trace, console.table
參考答案: setTimeout/clearTimeout, setInterval/clearInterval, setImmediate/clearImmediate, process.nextTick
參考答案: Buffer是用來處理二進制數據的,好比圖片,mp3,數據庫文件等.Buffer支持各類編碼解碼,二進制字符串互轉.
參考答案: EventEmitter是node中一個實現觀察者模式的類,主要功能是監聽和發射消息,用於處理多模塊交互問題.
參考答案: 主要分三步:定義一個子類,調用構造函數,繼承EventEmitter
代碼演示
var util = require('util');
var EventEmitter = require('events').EventEmitter;
function MyEmitter() {
EventEmitter.call(this);
} // 構造函數
util.inherits(MyEmitter, EventEmitter); // 繼承
var em = new MyEmitter();
em.on('hello', function(data) {
console.log('收到事件hello的數據:', data);
}); // 接收事件,並打印到控制檯
em.emit('hello', 'EventEmitter傳遞消息真方便!');
複製代碼
參考答案: 1) 模塊間傳遞消息 2) 回調函數內外傳遞消息 3) 處理流數據,由於流是在EventEmitter基礎上實現的. 4) 觀察者模式發射觸發機制相關應用
參考答案: 監聽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');
});
複製代碼
參考答案: 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');
});
複製代碼
參考答案: stream是基於事件EventEmitter的數據管理模式.由各類不一樣的抽象接口組成,主要包括可寫,可讀,可讀寫,可轉換等幾種類型.
參考答案: 非阻塞式數據處理提高效率,片段處理節省內存,管道處理方即可擴展等.
參考答案: 文件,網絡,數據轉換,音頻視頻等.
參考答案: 監聽error事件,方法同EventEmitter.
參考答案: Readable爲可被讀流,在做爲輸入數據源時使用;Writable爲可被寫流,在做爲輸出源時使用;Duplex爲可讀寫流,它做爲輸出源接受被寫入,同時又做爲輸入源被後面的流讀出.Transform機制和Duplex同樣,都是雙向流,區別時Transfrom只須要實現一個函數_transfrom(chunk, encoding, callback);而Duplex須要分別實現_read(size)函數和_write(chunk, encoding, callback)函數.
參考答案: 三步走:1)構造函數call Writable 2) 繼承Writable 3) 實現_write(chunk, encoding, callback)函數
代碼演示
var Writable = require('stream').Writable;
var util = require('util');
function MyWritable(options) {
Writable.call(this, options);
} // 構造函數
util.inherits(MyWritable, Writable); // 繼承自Writable
MyWritable.prototype._write = function(chunk, encoding, callback) {
console.log("被寫入的數據是:", chunk.toString()); // 此處可對寫入的數據進行處理
callback();
};
process.stdin.pipe(new MyWritable()); // stdin做爲輸入源,MyWritable做爲輸出源
複製代碼
參考答案: fs模塊主要由下面幾部分組成: 1) POSIX文件Wrapper,對應於操做系統的原生文件操做 2) 文件流 fs.createReadStream和fs.createWriteStream 3) 同步文件讀寫,fs.readFileSync和fs.writeFileSync 4) 異步文件讀寫, fs.readFile和fs.writeFile
參考答案: 整體來講有四種: 1) POSIX式低層讀寫 2) 流式讀寫 3) 同步文件讀寫 4) 異步文件讀寫
參考答案: 主要有兩種方式,第一種是利用node內置的require('data.json')機制,直接獲得js對象; 第二種是讀入文件入內容,而後用JSON.parse(content)轉換成js對象.兩者的區別是require機制狀況下,若是多個模塊都加載了同一個json文件,那麼其中一個改變了js對象,其它跟着改變,這是由node模塊的緩存機制形成的,只有一個js模塊對象; 第二種方式則能夠隨意改變加載後的js變量,並且各模塊互不影響,由於他們都是獨立的,是多個js對象.
參考答案: 兩者主要用來監聽文件變更.fs.watch利用操做系統原生機制來監聽,可能不適用網絡文件系統; fs.watchFile則是按期檢查文件狀態變動,適用於網絡文件系統,可是相比fs.watch有些慢,由於不是實時機制.
參考答案: node全面支持各類網絡服務器和客戶端,包括tcp, http/https, tcp, udp, dns, tls/ssl等.
參考答案: 主要實現如下幾個步驟便可: 1) openssl生成公鑰私鑰 2) 服務器或客戶端使用https替代http 3) 服務器或客戶端加載公鑰私鑰證書
參考答案: 經典又很沒毛意義的一個題目.思路是加載http模塊,建立服務器,監聽端口.
代碼演示
var http = require('http'); // 加載http模塊
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'}); // 200表明狀態成功, 文檔類型是給瀏覽器識別用的
res.write('<meta charset="UTF-8"> <h1>我是標題啊!</h1> <font color="red">這麼原生,初級的服務器,下輩子能用着嗎?!</font>'); // 返回給客戶端的html數據
res.end(); // 結束輸出流
}).listen(3000); // 綁定3ooo, 查看效果請訪問 http://localhost:3000
複製代碼
參考答案: node是異步非阻塞的,這對高併發很是有效.但是咱們還有其它一些經常使用需求,好比和操做系統shell命令交互,調用可執行文件,建立子進程進行阻塞式訪問或高CPU計算等,child-process就是爲知足這些需求而生的.child-process顧名思義,就是把node阻塞的工做交給子進程去作.
參考答案: exec能夠用操做系統原生的方式執行各類命令,如管道 cat ab.txt | grep hello; execFile是執行一個文件; spawn是流式和操做系統進行交互; fork是兩個node程序(javascript)之間時行交互.
參考答案: 那就用spawn吧.
代碼演示
var cp = require('child_process');
var child = cp.spawn('echo', ['你好', "鉤子"]); // 執行命令
child.stdout.pipe(process.stdout); // child.stdout是輸入流,process.stdout是輸出流
// 這句的意思是將子進程的輸出做爲當前程序的輸入流,而後重定向到當前程序的標準輸出,即控制檯
複製代碼
參考答案: 用fork嘛,上面講過了.原理是子程序用process.on, process.send,父程序裏用child.on,child.send進行交互.
代碼演示
1) fork-parent.js
var cp = require('child_process');
var child = cp.fork('./fork-child.js');
child.on('message', function(msg){
console.log('老爸從兒子接受到數據:', msg);
});
child.send('我是你爸爸,送關懷來了!');
2) fork-child.js
process.on('message', function(msg){
console.log("兒子從老爸接收到的數據:", msg);
process.send("我不要關懷,我要銀民幣!");
});
複製代碼
參考答案: 1) 在myCommand.js文件頭部加入 #!/usr/bin/env node 2) chmod命令把js文件改成可執行便可 3) 進入文件目錄,命令行輸入myComand就是至關於node myComand.js了
參考答案: 概念都是同樣的,輸入,輸出,錯誤,都是流.區別是在父程序眼裏,子程序的stdout是輸入流,stdin是輸出流.
參考答案: node是單線程的,異步是經過一次次的循環事件隊列來實現的.同步則是說阻塞式的IO,這在高併發環境會是一個很大的性能問題,因此同步通常只在基礎框架的啓動時使用,用來加載配置文件,初始化程序什麼的.
參考答案: 1) 多層嵌套回調 2) 爲每個回調寫單獨的函數,函數裏邊再回調 3) 用第三方框架比方async, q, promise等
參考答案: 多種方式 1) sudo 2) apache/nginx代理 3) 用操做系統的firewall iptables進行端口重定向
參考答案: 1) runit 2) forever 3) nohup npm start &
參考答案: 一個CPU運行一個node實例
參考答案: 用--max-old-space-size 和 --max-new-space-size 來設置 v8 使用內存的上限
參考答案: 1) node --prof 查看哪些函數調用次數多 2) memwatch和heapdump得到內存快照進行對比,查找內存溢出
參考答案: 1) try-catch-finally 2) EventEmitter/Stream error事件處理 3) domain統一控制 4) jshint靜態檢查 5) jasmine/mocha進行單元測試
參考答案: node --debug app.js 和node-inspector
參考答案: async是一個js類庫,它的目的是解決js中異常流程難以控制的問題.async不只適用在node.js裏,瀏覽器中也可使用.
async.parallel([
function(){ ... },
function(){ ... }
], callback);
複製代碼
async.series([
function(){ ... },
function(){ ... }
]);
複製代碼
async.waterfall([
function(callback) {
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback) {
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
複製代碼
async.map(['file1','file2','file3'], fs.stat, function(err, results){
// results is now an array of stats for each file
});
複製代碼
async.filter(['file1','file2','file3'], fs.exists, function(results){
// results now equals an array of the existing files
});
複製代碼
參考答案: app.js, package.json, bin/www, public, routes, views.
參考答案: express.Router路由組件,app.get路由定向,app.configure配置,app.set設定參數,app.use使用中間件
參考答案: /users/:name使用req.params.name來獲取; req.body.username則是得到表單傳入參數username; express路由支持經常使用通配符 ?, +, *, and ()
參考答案: res.download() 彈出文件下載
res.end() 結束response
res.json() 返回json
res.jsonp() 返回jsonp
res.redirect() 重定向請求
res.render() 渲染模板
res.send() 返回多種形式數據
res.sendFile 返回文件
res.sendStatus() 返回狀態
參考答案: 相似傳統數據庫,索引和分區.
參考答案: mongoose是mongodb的文檔映射模型.主要由Schema, Model和Instance三個方面組成.Schema就是定義數據類型,Model就是把Schema和js類綁定到一塊兒,Instance就是一個對象實例.常見mongoose操做有,save, update, find. findOne, findById, static方法等.
參考答案: set/get, mset/hset/hmset/hmget/hgetall/hkeys, sadd/smembers, publish/subscribe, expire
參考答案:
var redis = require("redis"),
client = redis.createClient();
client.set("foo_rand000000000000", "some fantastic value");
client.get("foo_rand000000000000", function (err, reply) {
console.log(reply.toString());
});
client.end();
複製代碼
參考答案: 兩者都是代理服務器,功能相似.apache應用簡單,至關普遍.nginx在分佈式,靜態轉發方面比較有優點.
參考答案: File API支持本地文件操做; Canvans/SVG支持繪圖; 拖拽功能支持; 本地存儲支持; 表單多屬性驗證支持; 原生音頻視頻支持等
參考答案: id, 元素,屬性, 值,父子兄弟, 序列等
參考答案: 文檔選擇,文檔操做,動畫, ajax, json, js擴展等.