D1.Nodejs 入門篇

分享第一篇,關於 NodeJS —— Javascript 的經常使用知識以及如何從 Javascript 開發者過渡到 NodeJS 開發者(不會介紹具體的框架)。在讀本文前,但願你對 javascript 有一些初步的認識。javascript

Javascript 是一門原型模型的解釋型語言。解釋型將在後面的 NodeJS 裏面討論,原型鏈是 ES6 以前的 Javascript 的面向對象的實現方式之一,在 ES6 中支持的 class 增長了一種新的實現方式。在 Javascript 裏面全部東西都是對象,包括 「類」。接觸過 ruby/python 的元編程的可能會以爲這個很熟悉,Javascript 也很容易是實現出動態的生成類的方法。前端

1. 基於原型鏈實現的簡單的「類」

javascriptvar Person = function(name){
  this.name = name;
};

Person.staticSay = function(name){
  console.log('Hello ' + name);
};

Person.prototype.sayHi = function(){
  Person.staticSay(this.name);
}

提一些常見的規範,例如 Javascript 中全部的方法都是駝峯命名,優先使用單引號,兩個空格等等,更多的規範能夠參考 https://github.com/airbnb/javascriptjava

代碼中的staticSay爲靜態方法,即只能經過 Person.staticSay來調用。 當上面的 Person 生成實例的時候,例如 var vincent = new Person('vincent');的時候,vincent會自動繼承 Person.prototype 的全部方法(代碼中的 this 指代的是當前上下文,即上文中的 vincent)。node

同時也能夠動態的爲對象 vincent 添加方法,例如以下代碼:python

javascriptvar vincent = new Person('vincent')
vincent.tellName = function(){
  console.log('Hi, i\'m am' + this.name)
};

而後當你須要模擬繼承的時候,就須要在 prototype 上下功夫。例以下面使用 Worker.prototype = new Person() 來實現,new Person() 返回的實例對象帶着的全部方法、屬性都被賦給了 prototype,變相模擬了繼承。這種方式最終一層層的往上找 prototype 裏面的內容(由於每一個實例具備的方法都在 prototype 裏面,往上直到 Object)。固然也能夠經過遍從來進行對 prototype 賦值來模擬繼承。git

2. 上下文切換

上下文最直觀的表現就是代碼塊中的 this,一般在面向對象的編程中用到,來指代當前「類」生成的對應實例,與其餘語言的 self一致。github

繼續用上文中的例子,上文中已經實現了一個 Person.prototype.sayHi方法,如今我有一個新的對象,代碼以下:web

javascriptvar Cat = function(name){
  this.name = name;
}

var c = new Cat('tomcat');

若是某天忽然異想天開但願這隻貓像人同樣介紹他本身怎麼辦,他本身沒有 sayHi 這個方法。可是能夠經過 console.log(Person.prototype.sayHi)是能夠拿到人類的 sayHi 方法的,怎麼讓貓也可使用呢?編程

Javascript 有兩個方法,callapply,他們的區別就是參數不一樣(自行谷歌),做用是用來切換上下文。簡單說就是能夠把 Person.prototype.sayHi這個函數中的 this 變成其餘對象。使用方式: Person.prototype.sayHi.call(c)緩存

這個實用嘛?例如以下場景:

javascriptvar doSomething = function(){
  var persons = arguments;
};

上面的函數中,經過關鍵字 arguments獲取全部的參數來支持不定數量的參數。如今咱們但願對 persons用一些原屬於 Array 類型的方法,如何實現呢?這裏就能夠用上下文切換來實現:

javascriptvar doSomething = function(){
  var persons = arguments;
  // 使用 Array 的 slice 方法,將 arguments 對象轉變爲 Array 實例
  var persons_arr = Array.prototype.slice.call(arguments);
};

3. 閉包

先來段常見的代碼

javascriptfor (var i = 0; i < 3; i ++){
  setTimeout(function(){
    console.log(i);
  }, i)
}

這個會輸出什麼結果呢?依次輸出 0 1 2 ?實際狀況是,當 setTimeout第一次執行回調的時候,for 循環已經結束了,也就是說此時的 i 已是 3 了,致使最終的輸出結果是 3 3 3。

當你須要保護某一個變量,使得他不被外圍的代碼所影響的時候,你可能就須要考慮下閉包 —— 一個封閉的做用域的代碼塊。

javascriptfor (var i = 0; i < 3; i ++){
  +function(i){
    setTimeout(function(){
      console.log(i);
    }, i)
  }(i)
}

咦, +是幹嗎的,有沒有其餘方式實現,請自行谷歌。閉包內的 i 的做用域是一個封閉的做用域,因此最終 閉包內的 i 一直沒有被外面的執行改變,因此能夠成功的輸出 0 1 2。


簡單的介紹了 javascript 部分特性,關鍵字 原型鏈、call 和 apply、arguments 關鍵字,更多的建議能夠看看例如權威指南這樣的書,或者快速瞭解下基本的類型以及每一個類型有的方法。有一些比較神奇的代碼,例如得到當前的代碼的字符串,而後進行處理獲得本身想要的內容,使用 getter 和 setter 在用戶對對象屬性獲取或者賦值的時候作一些特殊的操做等等。

4. NodeJS 和 Javascript 的開發區別

這塊主要介紹 require 加載的基礎知識,首先先介紹一些代碼:

javascript// a.js
module.exports = {
  name: "a",
  doSomething: function(){
    return "something";
  }
}

// b.js
var a = require('./a')
global.a_name = a.name;

// c.js
require('./b');
console.log(a_name) // 執行後打印 a

當咱們執行 node c.js的時候發生了什麼?

  1. require是 nodes 關鍵字,雖然 NodeJS 是以異步著稱,可是他的 require都是阻塞的。不然就會出現尚未載入其餘模塊,已經開始執行下面的代碼的狀況。
  2. require.resolve()方法是用來找出你所引用的文件的實際路徑,找出後 Nodejs 會在 require.cache裏面尋找是否有緩存,沒有的話則會讀取文件而後解析,因此一般狀況下,一個 js 文件裏面的執行的代碼只會在第一次被 require 的時候被執行。(tip. require.cache 若是有須要的話是能夠手動刪除一些東西的,而後能夠某種程度上能夠執行屢次)
  3. 當 b.js 開始執行的時候,他須要先載入 a.js,module.exports告訴 Nodejs 這個文件對外暴露寫什麼,例如 a.js 暴露的是一個對象,包含 name 屬性和 doSomething 方法。而後 b.js 中的 a 變量其實就是這個對象。
  4. 執行完獲取 a.js 後,繼續回到 b.js ,global.a_name 至關於聲明瞭一個全局變量,這個和前端中的 window.a_name = a.name 效果相似。
  5. 最終過程完成,c.js 執行輸出值。

5. 異步的底層原理

NodeJS 很容易給人一種使用上的錯覺,就是寫了好久均可能不知道底層的異步是怎麼實現的。(下面的理解主要來自於對 python3.4 中的 asyncio 的理解,若有錯誤歡迎指出)。

NodeJS 底層的 libev 分別在 Window 下使用 IOCP 和 *nix 下使用基於 AIO 的 libeio 來實現異步。經過系統層面的技術,最後達到一個目的,就是應用程序發起一個異步請求,最終在系統執行完後,系統通知應用程序處理完成。在這個過程當中,應用程序能夠將以前的處理掛起/推入線程池中等待執行,而應用程序在此期間能夠執行其餘任務。

整個的運行經過系統層面的事件循環來進行運做。例如 Python 提供了相似於 run_until 以及 run_forever 的這樣的方法,保證在異步執行以前程序不會結束運行。將整個異步想象成一個一直在運做的車間,車間裏面的機器負責查看包裹並蓋章這樣的操做,工人拿到了一個包裹,而後貼上相應的標籤後放進去,等車間處理完後再交還給工人,工人根據包裹上他以前貼上的標籤和被車間貼上的標籤,進行下一步的處理。工人無需等待包裹檢查完畢才能進行下一個,他只須要接受簡單處理,而後放入車間進行檢查。而後等某個時間車間返回給他某個包裹,他再去進行下一步的操做。


目前主要仍是隻介紹了一些語言層面的知識,可是隻有這些距離開發一個完整的 web 還有一些距離,將在後面繼續介紹。包括 Redis,Nginx,測試驅動等等。

原文:https://github.com/vincenting/note/issues/1

相關文章
相關標籤/搜索