Node.js基礎知識雜燴

來源:互聯網
上載者:User

標籤:

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的模組機制的一些疑問
  1. 什麼是node.js的模組?
    Node.js的模組機制遵從CommonJS的規範,CommonJS定義模組的概念如下:
    • 一個單獨的檔案就是一個模組。每一個模組都是一個單獨的範圍,也就是說,在一個檔案定義的變數(還包括函數和類),都是私人的,對其他檔案是不可見的。
    • 每個檔案的對外介面是module.exports對象。這個對象的所有屬性和方法,都可以被其他檔案匯入。我們可以通過module.exports輸出這個模組定義變數,函數,類,
    • 模組可以多次載入,但是只會在第一次載入時運行一次,然後運行結果就被緩衝了,以後再載入,就直接讀取緩衝結果。要想讓模組再次運行,必須清除緩衝。
  2. 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。

  1. 模組中的變數和函數都是靜態嗎?
    是的。因為CommonJs的規範,模組都是被載入一次,那麼,每次模組被載入的執行個體都是一樣的,這和static變數或者是函數,都是一樣的。因此,對於node而言,模組之間的變數和函數都是靜態。
  2. 對比下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的類型,參數,範圍,閉包
  1. js是不做類型檢查的一門語言,那麼js的類型有哪些呢?《javascript進階程式設計》大致基本類型分為基礎類型和物件類型,基礎類型包括:布爾類型,整形,string,undefined和null。而物件類型包括Object,Funtion,Array,Date。(這裡再次可以看出,funtion是物件類型)
  2. js中定義函數的參數時,並不寫型別宣告(這也是js的一個弊端所在),因此,在調用js的函數的時候,程式員務必自己注意類型。關於參數容易出現的問題是將json的string類型和object類型搞混淆,function類型使用錯誤等,都是項目中常見的問題。
  3. 範圍:關於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來產生。

  1. 原型方式產生對象:
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進階程式設計》這本書的第六章,有詳細介紹。

  1. 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基礎知識雜燴

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.