標籤:
Node.js基礎知識雜燴
這段時間做項目主要做關於伺服器的功能點,因此,逐漸開始學習node.js寫伺服器,總體下來,覺得node比php更好用,寫服務前器的速度更快,處理效率更高。註:node並不是專門寫伺服器的,其實node的出現,將js從web前端的專用語言,變成了一門通用語言。node中有各樣的基礎模組:比如fs,path,buffer,http,net等模組,此外,node還有各種各樣的三方模組,比如寫伺服器人常用的express,co,koa,seqlize等著名架構,也就是三方模組。那麼,node.js的模組機制究竟是如何的呢?我們應該注意模組機制上的哪些問題呢?
1.關於Nodejs的模組機制的一些疑問
- 什麼是node.js的模組?
Node.js的模組機制遵從CommonJS的規範,CommonJS定義模組的概念如下:
- 一個單獨的檔案就是一個模組。每一個模組都是一個單獨的範圍,也就是說,在一個檔案定義的變數(還包括函數和類),都是私人的,對其他檔案是不可見的。
- 每個檔案的對外介面是module.exports對象。這個對象的所有屬性和方法,都可以被其他檔案匯入。我們可以通過module.exports輸出這個模組定義變數,函數,類,
- 模組可以多次載入,但是只會在第一次載入時運行一次,然後運行結果就被緩衝了,以後再載入,就直接讀取緩衝結果。要想讓模組再次運行,必須清除緩衝。
- require載入同一個模組多次,載入的模組都是一樣的嗎?
下面給出一段代碼,我們根據代碼來看樣本:
module1模組:
var num=1;module.exports.num=num;` a 檔案: `var module1=require("./module1"); var module2=require("./module1"); module1.num=2; console.log(module2.num);
上述代碼運行結果是多少呢?很多人應該都會回答2,答案確實也是2,但是如果下面這種情境呢?
`module1模組:
var num=1;module.exports.num=num
a 檔案:
`var module1=require("./module1");module1.num=2;`
b 檔案:
var module2=require("./module1");console.log(module2.num)
現在,先運行a檔案(注意,a檔案運行完成後,node並未結束),然後在運行b檔案,請問num是多少?這裡很多人都會說,顯然是1啊。這是一種慣性思維,其實答案是2,為什麼呢?因為模組無論在同一個檔案還是在不同的檔案中被載入,如果被載入過,都會保留他們的緩衝,也就是只是被載入一次。所以,這裡在a檔案運行後,module1模組的num數值被載入修改為2,在b檔案啟動並執行時候,module1模組因為被載入過,因此,num的數值在b檔案中,顯示為2。
- 模組中的變數和函數都是靜態嗎?
是的。因為CommonJs的規範,模組都是被載入一次,那麼,每次模組被載入的執行個體都是一樣的,這和static變數或者是函數,都是一樣的。因此,對於node而言,模組之間的變數和函數都是靜態。
-
對比下node的required機制和java的import機制?
對node而言,引入模組是使用require命令,而java是import命令。node的require命令的準系統是,讀入並執行一個JavaScript檔案,然後返回該模組的exports對象。而java的import是引入一個類檔案,然後通過類檔案名稱,來訪問檔案中的方法或者變數。
由此可以看出,node的require和java的import都有相似的設計思路:
- 兩者都是通過檔案(模組)的方式來進行範圍的隔離
- 兩者都進行的訪問的控制:java有priavte,public訪問符號,而node有exports是否輸出決定該檔案中的變數。
兩者不同的之處在於:
- 1.node的require命令引入是一個module對象,而java的import命令引入的是一個類。
- 2.node的required命令引入一個module對象之後,我們可以直接存取module對象中的一些屬性,函數,但是java可能需要通過一個類來執行個體化一個對象,然後訪問這個對象的某些屬性,當然,java中的類上的static變數,方法就可以不執行個體化而進行訪問。
2.關於js的函數的一些疑問
函數是js中一個最關鍵的東東,在java語言中,函數僅僅是傳統意義上的函數,執行一個函數,訪問對象的代碼區,然後為參數指派至的空間,接下來執行函數就可以了,然後傳回值。但是對js而言,函數不單單是函數,函數也可以作為變數,參數,傳回值,函數甚至再js中作為一種基本類型,對!函數在js中是一種物件類型,也是就是說,函數在js中一個對象而存在。其實將函數作為一種基本類型,也就說明了函數的靈活性。那麼,我們有必要分析下js的函數是如何?的
js有兩種常用的定義函數的方法:
function A(){}` `var a=function(params){}
上面方法在記憶體上是怎麼實現的呢?其實也很簡單,看下面記憶體配置圖就會明白了:
其實,上述的方法中,無論是第一種方法還是第二種方法,在js的解譯器中,都會這樣執行:
var x=new Object x.constructor=A; ` `A.prototype=x
這裡的x就是右邊的object。可以看到,js的函數的本質上是指向了一塊堆空間(因為已經有原型了:A.prototype=obejct)。這也很好地解釋了函數為什麼能被作為參數和傳回值,因為一個函數名作為參數的時候,代表的就是一塊堆空間上的一塊地址值了。上述兩種方法裡面,不同的是第一種方法,name=A,而第二種方法的name=undefined。
所以,現在可以明確一個寫node或者js的程式時候的思想了:當我定義一個函數的時候,那麼,隨即會在堆空間上定義一個對象,然後用函數名代表這個對象的地制值,調用這個函數的時候,即訪問這個對象,屆時,再分配棧空間,執行程式碼片段,進而執行函數。
3.js的類型,參數,範圍,閉包
- js是不做類型檢查的一門語言,那麼js的類型有哪些呢?《javascript進階程式設計》大致基本類型分為基礎類型和物件類型,基礎類型包括:布爾類型,整形,string,undefined和null。而物件類型包括Object,Funtion,Array,Date。(這裡再次可以看出,funtion是物件類型)
- js中定義函數的參數時,並不寫型別宣告(這也是js的一個弊端所在),因此,在調用js的函數的時候,程式員務必自己注意類型。關於參數容易出現的問題是將json的string類型和object類型搞混淆,function類型使用錯誤等,都是項目中常見的問題。
-
範圍:關於js的範圍的問題,詳細瞭解可以看這篇部落格:js範圍詳解,關於js傳統的範圍的問題,這裡簡單表述下:
- 1.JavaScript的是有範圍鏈存在的。
- 2.JavaScript沒有塊級範圍。
- 3.JavaScript具有變數提升的特點。
- 4.函數體內部,局部變數的優先順序比同名的全域變數高(局部變數會掩蓋全域變數的帶來的範圍)
我們可以看到,由於js的規定的範圍問題,我們可能會帶來一些困擾,比如下面的這個函數:
var foo = 1; function bar() { if (!foo) { var foo = 10; } console.log(foo); }bar();
執行的結果是多少呢?很多人會說是1,但是,由於js的函數內部定義的變數優先順序比外部的高,加之js的變數提升的原因,因此,上述執行的結果執行為10;
再看這個函數:
function test () { console.log(value); var value = ‘something‘; console.log(value);}
這樣寫法在其他有塊級範圍中顯然是不會編譯通過的,但是在js中可以的,也是因為了js的變數提升,可以編譯通過,輸出結果為undefined和something。
以上的問題,就是js的範圍設計問題帶來的不好的效果,比如沒有塊級範圍以及變數提升等問題,會在編程上帶來一些不理想的效果。這些問題,在ES6的規範中,用let代替var後,得到解決。比如上述問題中第一個,可以使用let來代替var:比如這樣解決:
var foo = 1;function bar() { if (!foo) { let foo = 10; } console.log(foo);}bar();
4.js的有幾種方法如何執行個體化一個對象?
對於js而言,要執行個體化一個對象有以下幾種方法:
1. 書面語句:
var person = { name: "lbq", age: 22, getName: function () { return this.name; }}console.log(person.getName());
可以看到,這樣建立一個object確實很簡單,只要一個花括符就可以,這也是js建立對象最方便的一點。
2. 使用函數建立:
function Person(name,age){ this.name=name; this.age=age; this.getName=function(){ return this.name; }}var person1=new Person("lbq",23);var person2=new Person("lzw",22);console.log(person1.getName());console.log(person2.getName());
大家可以看到,第二中方法是通過function來構造產生的object,這裡可以看到,js在ES6之前,沒有類的概念,我們要構造對象,只有通過function來產生。
- 原型方式產生對象:
function Person2(name, age) { this.name = name; this.age = age;}Person2.prototype.getName = function () { return this.name;}Person2.prototype.getAge = function () { return this.age;}var person3=new Person2("lbq",23);var person4=new Person2("lzw",22);console.log(person3.getAge());console.log(person4.getAge());
在js中,每個函數都是有一個prototype屬性,這個屬性就是一個指標,指向一個對象,這個對象就是原型對象。我們可以對原型對象進行變數定義和方法定義,然後通過new來執行個體化一個對象,執行個體化的對象會有一個——proto—,指向原型對象。關於原型對象的具體可以參考《Javascript進階程式設計》這本書的第六章,有詳細介紹。
- ES6新文法,通過class關鍵字來執行個體化一個對象:
class Person3{ constructor(name,age){ this.name=name; this.age=age; } getName(){ return this.name; } getAge(){ return this.age; }}var person5=new Person3("lbq",23);var person6=new Person3("lzw",22);console.log(person5.getName());console.log(person6.getName());
好的訊息是,在ES6中,加入class關鍵字後,我們可以在js中,像java那樣定義類,然後通過類來執行個體化一個對象了。
5.js的this關鍵字的理解
this關鍵在java中也許很好理解,因為js是物件導向的語言,因此,java中的this就是指向當前的對象,我們可以通過this來訪問當前對象的成員變數或者方法(無論公有還是私人),但是,js是一門函數式編程的語言,js中的this又怎麼理解呢?
關於js的this的理解,可能說的比較複雜,這裡我給出我的自己的理解:出現有this關鍵字的函數,是哪個對象中定義的函數,那麼,該對象就是thi指向的對象:
我們來看下示範:
var obj={};
obj.x=100;
obj.y=function (){console.log(this.y)};
obj.y();
這裡就很好理解了:顯然列印出來的就是100,因為函數y是在obj變數中被調用的,所以thi指向的是obj對象,this.x就是100.
現在我們看一個難一點的示範:
var showthis=function(){
console.log(this.x);
}
var x=”this is a variable of window”;
var obj={};
obj.x=100;
obj.y=function (){console.log(this.x)};
var y2=obj.y;
obj.y();
showthis():
y2():
上述代碼中,一共要列印三個console,按照分析,一個肯定還是100,第二個呢?在JavaScript的變數範圍裡有一條規則“全域變數都是window對象的屬性“。所以,showthis函數變數是在window對象中定義的,因此,showthis列印的是this is a variable of window。那麼,y2對象呢?y2對象也是在window對象中定義的,因此,第三個console的結果,也一定是this is a variable of window。
經過上述兩個案例,我們可以看到,this就是指向函數所在的對象的,如果函數在全域,那麼,this就是指向window對象了。
關於this指向的思考,我是從node構建web項目來理解的,因為在koa中,經常用this.request或者是this.body來指代http的報文請求和應答,所以,有些時候務必要注意程式的中的this指向。如果想改變this的環境,那麼,可以使用call或者是apply。
以上就是關於我在初學node.js中的一些關鍵疑問,比如js的函數如何作為一等公民對待,記憶體配置長啥樣,還有關於初學一門語言都應該掌握的基礎:變數類型,範圍,常會犯錯的關鍵字,依賴體系的建立等,不是學習node應該注意,而是學習任何一門語言都應該注意和掌握的
Node.js基礎知識雜燴