成為自信的node.js開發者(一)

這個博客是我最近整理了過去的文章。

適合閱讀的同學

想更進一步深入理解node的同學,如果你已經稍微了解一點點node, 可以用node做一些小demo,并且想更深一步的了解,希望這篇文章可以幫助到你。

不太適合閱讀的同學

  1. 不太熟悉基本的javascript 語法,比如說回調函數

  2. 對node有深入理解的同學,比如說,可以清晰的說出event-loop

Node 架構——v8、libuv

第一部分,我們先了解一下node的結構,對node先有一個整體上的認識。只有這樣,我們才能編寫出更加高性能的代碼,在遇到問題時,也知道解決的思路。

先來看一張圖表:

最上面是我們編寫的node.js的代碼,當我們執行node index.js的命令時,我們是觸發了一個node的程序,和其他的javascript的項目,比如說前端的h5項目一樣,該node程序需要有其他的依賴,其中最主要的兩個依賴是 v8libuv

  1. v8是 google 開源的引擎,目的是在瀏覽器世界外可以運行javascript的代碼。

  2. libuv 是c++ 開源的項目,最初就是專門為node設計,目的是給node和操作系統交互的能力,比如說網絡, 操作文件。

node在可見的未來仍將繼續使用v8, 但是微軟edge瀏覽器的chakra(讀法:渣坷垃)引擎也是一個強有力的競爭者。github.com/nodejs/node… 這個項目是如何讓node如何跑在chakras 引擎上

v8 引擎

我們現在知道了,node 使用 v8 用來執行javascript 代碼,這意味著,node中所支持的javascript的特性,是由 v8 引擎所決定的。

V8引擎支持的 javascript 特性被劃分為三個不同的group: ShippingStagedIn Progress

默認情況下Shipping group的特性可以直接使用,Staged group的特性需要使用--harmony選項來開啟。如下所示:

? node -v
v7.9.0
? node -p 'process.versions.v8'
5.5.372.43
? node -p "'Node'.padEnd(8, '*')"    // 默認是不支持的
[eval]:1
'Node'.padEnd(8, '*')^TypeError: "Node".padEnd is not a functionat [eval]:1:8at ContextifyScript.Script.runInThisContext (vm.js:23:33)at Object.runInThisContext (vm.js:95:38)at Object. ([eval]-wrapper:6:22)at Module._compile (module.js:571:32)at evalScript (bootstrap_node.js:387:27)at run (bootstrap_node.js:120:11)at run (bootstrap_node.js:423:7)at startup (bootstrap_node.js:119:9)at bootstrap_node.js:538:3
? node --harmony -p "'Node'.padEnd(8, '*')"  // 通過--harmony
Node****
復制代碼

In Progress group的feature不穩定,但你也可以使用特定的flag來開啟,通過 node --v8-options 命令可以查看,通過grep 命令去查找in progress,如下:

? node --v8-options | grep "in progress"--harmony_array_prototype_values (enable "harmony Array.prototype.values" (in progress))--harmony_function_sent (enable "harmony function.sent" (in progress))--harmony_sharedarraybuffer (enable "harmony sharedarraybuffer" (in progress))--harmony_simd (enable "harmony simd" (in progress))--harmony_do_expressions (enable "harmony do-expressions" (in progress))--harmony_restrictive_generators (enable "harmony restrictions on generator declarations" (in progress))--harmony_regexp_named_captures (enable "harmony regexp named captures" (in progress))--harmony_regexp_property (enable "harmony unicode regexp property classes" (in progress))--harmony_for_in (enable "harmony for-in syntax" (in progress))--harmony_trailing_commas (enable "harmony trailing commas in function parameter lists" (in progress))--harmony_class_fields (enable "harmony public fields in class literals" (in progress))
復制代碼

比如說,上面打印出來的倒數第二行-- harmony_trailing_commas 可以支持函數傳參尾逗號:

node -p 'function tc(a,b,) {}'   // 會報錯,因為最后一個逗號
=========================
node --harmony_trailing_commas -p 'function tc(a,b,) {}'   //不會報錯
復制代碼

libuv

  1. libuv 提供了和操作系統交互的能力,比如說操作文件,網絡等等,并且磨平了操作系統的差異。

  2. node還使用libuv來處理異步操作,比如非阻塞IO(file system/TCP socket/child process)。當異步操作完成時,node通常需要調用回調函數,當調用回調函數時,node會把控制權交給V8引擎。 當回調函數執行完畢,控制權從v8引擎重新回到node.

    v8 引擎是單線程的,當v8引擎獲得控制權的時候,node 只能等待v8 引擎操作完成。

    這讓node沒有死鎖,競爭的概念。

  3. libuv 包含一個線程池,從操作系統的層面來做那些不能被異步做的事情

  4. libuv 給node 提供了 event-loop, 會在第二節介紹

其他依賴

除了v8引擎和 libuv, node 還有其他的一些比較重要的依賴。

http-parser 用來解析http內容的

c-ares 是用來支持異步的DNS 查詢的

openSSL 常用在 tlscrypto 的包中,提供了加密的方法

zlib 是用來壓縮和解壓的

node REPL

你可以在terminal里面執行node來啟動CLI,如下所示,REPL十分方便

例如,你定義一個array,當你arr.然后tab-tab(tab兩次),array自身的方法會顯示出來

? node
> var arr = [];
undefined
> arr.
arr.toString              arr.valueOf
arr.concat                arr.copyWithin         arr.entries               arr.every              arr.fill                  arr.filter
arr.find                  arr.findIndex          arr.forEach               arr.includes           arr.indexOf               arr.join
arr.keys                  arr.lastIndexOf        arr.length                arr.map                arr.pop                   arr.push
arr.reduce                arr.reduceRight        arr.reverse               arr.shift              arr.slice                 arr.some
arr.sort                  arr.splice             arr.unshift
復制代碼

你也可以輸入.help,然后可以看到各種快捷鍵如下:

> .help
.break    Sometimes you get stuck, this gets you out
.clear    Alias for .break
.editor   Enter editor mode
.exit     Exit the repl
.help     Print this help message
.load     Load JS from a file into the REPL session
.save     Save all evaluated commands in this REPL session to a file
復制代碼

你還可以用_(underscore)來得到上次evaluated的值:

> 3 - 2
1
> _
1
> 3 < 2
false
> _
false
復制代碼

你還可以自定義REPL選項,如下,你自定義repl.js并選擇忽視undefined,這樣output里面就不會有undefined輸出,同時你還可以預先加載你需要的library比如lodash

// repl.js
let repl = require('repl');
let r = repl.start({ ignoreUndefined: true  });
r.context.lodash = require('lodash');
復制代碼
? node ~/repl.js
> var i = 2;
> 
>
復制代碼

你可以用下面的command來查看更多的選項 node --help | less

-p, --print     evaluate script and print result-c, --check     syntax check script without executing-r, --require   module to preload (option can be repeated)
復制代碼

例如,node -c bad-syntax.js可以用來檢查語法錯誤, node -p 'os.cpus()'可以用來執行script并輸出結果,你還可以傳入參數,如下所示

? node -p 'process.argv.slice(1)' test 666
[ 'test', '666' ]
復制代碼

node -r babel-core/register可以用來預加載,相當于require('babel-core/register')

global 中的 process 和 buffer

global相當于瀏覽器里面的window,你可以global.a = 1;這樣a就是全局變量,但一般不推薦這樣做

global 對象身上有兩個屬性特別重要: processbuffer

process

processapplicationrunning env之間的橋梁,可以得到運行環境相關信息,如下所示:

> process.
process.arch
process.argv
process.argv0                       process.assert                      process.binding                     
process.chdir
process.config                      process.cpuUsage                    
process.cwd                         process.debugPort
process.dlopen                      process.emitWarning                 
process.env                         process.execArgv
process.execPath                    
process.exit                        process.features                    process.getegid
process.geteuid                     process.getgid                      process.getgroups                   process.getuid
process.hrtime                      process.initgroups                  
process.kill                        process.memoryUsage
process.moduleLoadList              process.nextTick                    process.openStdin                   
process.pid
process.platform                    process.reallyExit                  process.release                     process.setegid
process.seteuid                     process.setgid                      process.setgroups                   process.setuid
process.stderr                      process.stdin                       process.stdout                      
process.title
process.umask                       process.uptime                      process.version                     process.versions
process._events                     process._maxListeners               process.addListener                 process.domain
process.emit                        process.eventNames                  process.getMaxListeners             process.listenerCount
process.listeners                   
process.on                          
process.once                        process.prependListener
process.prependOnceListener         process.removeAllListeners          process.removeListener              process.setMaxListeners\
復制代碼

process.versions 非常有用:

process.env 提供了當前環境的一些信息

建議從 process.env 中只讀,因為改了也沒有用。

同時,process也是一個event emitter,例如:

process.on('exit', code => {// 并不能阻止node進程退出console.log(code)
})process.on('uncaughtException', err => {console.error(err)process.exit(1)
})
復制代碼
  1. 在process 的事件處理函數中,我們只能執行同步的方法,而不能使用event_loop,

  2. exituncaughtException 的區別。如果uncaughtException 注冊了事件,則node遇到錯誤并不會退出,也就是說,不會觸發exit 事件。這會讓node的執行變的不可預測。證明如下:

    process.on('exit', (code) => {console.log('ssss')    
    })
    process.on('uncaughtException', (err) => {console.error(err);
    })
    // keep the event loop busy
    process.stdin.resume()// 在這里觸發了bug
    console.logg()
    復制代碼

    上面的代碼即使遇到了錯誤也不會退出執行,exit 事件處理函數并不會觸發。所以需要我們手動觸發 process.exit(1) 才可以。

buffer

buffer 也是 global 對象中的一個屬性,主要用來處理二進制流buffer 本質上是一段內存片段,是放在v8引擎的堆的外面。

我們可以在buffer 這個內存中存放數據。

buffer讀取數據時,我們必須指定encoding, 因此從 filessockets 中讀取數據時,如果不指定encoding, 我們會得到一個 buffer 對象。

一旦buffer 被創建,就不能修改大小

buffer 在處理讀取文件,網絡數據流的時候非常有用

創建buffer的三種方式:

  1. Buffer.alloc(2)

    在內存中劃分出固定的大小

  2. Buffer.allocUnsafe(8)

    沒有指定具體的數據,可能會包含老的數據和敏感的數據,需要被正確的『填充』

  3. Buffer.from()

buffer的方法

和數組類似,但是不同。比如說 slice 方法截取出來的新buffer 和 老的buffer是共享同一個內存。

stringDecode

當轉變二進制數據流的時候,toString() 不如使用 stringDecode 模塊,因為該模塊可以處理不完整的數據呢。

Require() 的背后

如果想深入了解node, 必須要深入了解 require 方法。

涉及到兩個核心模塊——require 方法(在grobal對象上,但是每一個模塊都有自己的require 方法) 和 Module 模塊 (同樣在grobal對象上,用來管理模塊的)

require 分為幾步

當我們require一個module時,整個過程有五個步驟:

Resolving 找到module的絕對文件路徑

Loading 將文件內容加載到內存

Wrapping 給每個module創造一個private scope并確保require對每個module來說是local變量

Evaluating VM執行module代碼

Caching 緩存module以備下次使用

module 對象

Module {id: '.',exports: {},parent: undefined,filename: '/Users/xxx/lib/find.js',loaded: false,children: [],paths: [ '/Users/xxx/lib/node_modules','/Users/xxx/node_modules','/Users/node_modules','/node_modules' ] }
復制代碼

在Module對象里面,id 是module的identity,通常它的值是module文件的全路徑,除非是root,這時它的值是.(dot)

filename 是文件的路徑

paths 從當前路徑開始,往上一直到根路徑

require.resolve 和require一樣,但是它不會加載文件,只是resolve

模塊不一定是文件

  1. 可以是文件,比如說 node_module/find-me.js

  2. 可以是目錄帶index.js,比如說 node_module/find-me/index.js

  3. 可以是目錄帶package.json, 比如說node_module/find-me/main.js

    {"name": "find-me","main": "start.js"
    }
    復制代碼

exports 屬性

exportsmodule 上一個特殊的屬性,我們放入它的任何變量都可以在require時得到 。

loaded

Module對象的loaded屬性會保持false,直到所有content都被加載

因此,exports 不能放在的異步的setImmediate

循環引用

例如A require B,B require A

JSON 文件 和 c++ Addon 文件

Node會首先查找.js文件,再查找.json文件,最后.node文件 比如說,在主文件中,引入.json 文件

// 在主文件中
let mock = require('mockData.json')
console.log(mock)
復制代碼

mockData.json 文件中,不需要導出什么,直接寫json格式的即可

{"a": "abc","b": "abc",
}
復制代碼

如果node找不到 .js , .json 文件,就會找.node 文件,會把.node 文件作為一個編譯好的addon(插件) module。那么 .node 文件是從哪里來的呢?

  1. 先有一個 hello.cc 文件,是用 c++ 代碼寫的

  2. 再有一個 binding.gyp, 相當于的編譯的配置文件,里面是json 格式的配置項, 如下面所示:

    {"targets": [{"target_name": "addon","sources": [ "hello.cc" ]}]
    }
    復制代碼
  3. 安裝 npm install node-gyp -g , node 和 npm 自帶的那個不是給開發者用的,而是需要重新安裝一個

  4. node-gyp configure 根據平臺生成項目,再執行node-gyp build 生成 .node 文件,可以在 js的代碼中直接引用使用了。

    你可以通過require.extensions來查看Node支持的文件擴展名:

> require.extensions
{ '.js': [Function], '.json': [Function], '.node': [Function] }
復制代碼

上面的代碼中,對于 .js 文件,是直接編譯引入,對于.json 文件,是使用了JSON.parse 方法,對于 .node 文件,是使用了 process.dlopen() 方法。

包裹模塊

exports.id = 1;   // 對的exports = {id: 1,        // 錯的
}module.exports = {id: 1        // 對的
}
復制代碼

上面的代碼中,為什么exportsmodule.exports 有區別?

原因是,node 引入一個模塊代碼后,node 會給這些代碼外面包裹上一層方法,這個方法是module 模塊的wrapper 方法:

> require('module').wrapper
>[ '(function (exports, require, module, __filename, __dirname) { ','\n});' ]
復制代碼

這個方法接受5個參數: exports, require, module, __filename, __dirname

這個方法,讓 exports, require, module 看起來是全局變量,但其實是每個文件所獨有的。

exportsmodule 對象的module.exports 方法的引用,相當于 let exports s = module.exports, 如果讓 exports = {} 等于讓 exports 變量改寫了引用

緩存模塊

當第二次引入同一個文件的時候,將會走了緩存。

console.log(require.cache)
delete require.cache['/User/sss/sss/cache.js']
復制代碼

下一期我們再見~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/275857.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/275857.shtml
英文地址,請注明出處:http://en.pswp.cn/news/275857.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mysql讀寫分離和分布式_MySQL主從復制與讀寫分離

MySQL主從復制(Master-Slave)與讀寫分離(MySQL-Proxy)實踐Mysql作為目前世界上使用最廣泛的免費數據庫&#xff0c;相信所有從事系統運維的工程師都一定接觸過。但在實際的生產環境中&#xff0c;由單臺Mysql作為獨立的數據庫是完全不能滿足實際需求的&#xff0c;無論是在安全…

ux和ui_UI和UX設計師的10種軟技能

ux和ui重點 (Top highlight)As designers, whether it be UI, UX, or Product Design, we tend to direct our focus and energy on developing and mastering tangible skills.作為設計師&#xff0c;無論是UI&#xff0c;UX還是產品設計&#xff0c;我們都將重點和精力放在開…

SQLServer中批量插入數據方式的性能對比 (轉)

轉自&#xff1a;http://www.cnblogs.com/wlb/archive/2010/03/02/1676136.html 昨天下午快下班的時候&#xff0c;無意中聽到公司兩位同事在探討批量向數據庫插入數據的性能優化問題&#xff0c;頓時來了興趣&#xff0c;把自己的想法向兩位同事說了一下&#xff0c;于是有了本…

VueConf China 2021 《Vue3生態進展-尤雨溪》 Reaction

大家好&#xff0c;我是若川。今天分享昨天Vueconf的一篇文章&#xff0c;來了解下Vue的生態進展。另外今晚7點&#xff0c;Vuebeijing社區邀請了尤大會在視頻號直播&#xff0c;可以加我微信 ruochuan12&#xff0c;告訴觀看地址提前預約。點擊下方卡片關注我、加個星標&#…

Plsql運行mysql腳本_oracle中PLSQL語句

1.set autot off 禁止使用autotrace命令 set autot on 這個命令包括exp 和 stat(執行語句、生成explain plan、生成統計信息) set autot trace 不執行sql語句&#xff0c;但(生成explain plan、生成統計信息) set autot trace exp stat 與上句同 set autot trace st1.set autot…

2019年,你需要關注這些Node API和Web框架

對于Node.js框架和開源軟件來說&#xff0c;2018年是非常有趣的一年。開發者社區討論了企業贊助對開源項目的作用以及如何維護那些沒有經濟支持卻有數百萬人使用的項目。同樣&#xff0c;安全問題也得到了極大關注&#xff0c;一些流行的Node/JS軟件包被劫持&#xff0c;Github…

ai創造了哪些職業_關于創造職業的思考

ai創造了哪些職業When I was growing up, the idea of a creative career wasn’t an option.當我長大時&#xff0c;創意事業的想法不是一個選擇。 I had enjoyed doodling, arts and crafts as a kid, so as I grew up, it was a natural transition into Photoshop and lat…

Windows Mobile,用C#更改網絡連接(SSID、IP Address、Subnet Mask、Gatew... (轉)

前幾天在做一個改變PDA無線網絡連接的SSID和IP的功能是發現了一個好東西OpenNETCF Framework使用OpenNETCF.Net包&#xff0c;實現了任意改變PDA無線網絡連接的功能。并且不需要Reset PDA。現在正在做一個IP Manager For Windows Mobile的小程序。實現搜索當前網卡可見的SSID、…

一文讀懂vuex4源碼,原來provide/inject就是妙用了原型鏈?

1. 前言你好&#xff0c;我是若川&#xff0c;歡迎加我微信ruochuan12&#xff0c;加群長期交流學習。這是學習源碼整體架構系列 之 vuex4 源碼&#xff08;第十篇&#xff09;。學習源碼整體架構系列文章(有哪些必看的JS庫)&#xff1a;jQuery、underscore、lodash、sentry、v…

Spring4.3x教程之一IOCDI

SpringIOC也稱為DI&#xff0c;對屬性內容的注入可以通過屬性的setXXX方法進行也可以通過構造方法進行&#xff0c;當然還可以使用工廠模式進行屬性內容的注入。 什么是DI&#xff1f;什么是IOC&#xff1f; DI&#xff1a;Dependency Injection依賴注入 其實一個類中的屬性就是…

戰神4 幕后花絮 概念藝術_幕后花絮:品牌更新的背后

戰神4 幕后花絮 概念藝術Under the Hood gives you an inside look at different parts of Waze — straight from the people working on them every day.在引擎蓋下&#xff0c;您可以深入了解Waze的不同部分-直接來自每天進行工作的人員。 Traffic is the worst. It makes …

C#日期控件(js版)

js 腳本代碼: <script type"text/javascript"> //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // 這是一個日歷 Javascript 頁…

python第三周測試_python第三周小測

1.讀取一個文件&#xff0c;顯示除了井號(#)開頭的行意外的所有行# -*- coding: utf-8 -*-"""Created on Tue May 28 09:37:08 2019author: Omega_Sendoh"""#打開文件f open("install-sh","r")#讀取文件的所有行&#xff0…

「Vueconf」探索 Vue3 中 的 JSX

大家好&#xff0c;我是若川。今天再分享 Vueconf 的一篇文章。另外 Vueconf 主辦方提供的錄播鏈接是&#xff1a;?https://www.bilibili.com/read/mobile?id11408693&#xff0c;感興趣可以復制觀看。點擊下方卡片關注我、加個星標。學習源碼整體架構系列、年度總結、JS基礎…

設計模式--享元模式實現C++

/********************************* *設計模式--享元模式實現 *C語言 *Author&#xff1a;WangYong *Blog:http://www.cnblogs.com/newwy ********************************/ #include <iostream> #include <cassert> #include <vector> #include <strin…

安卓加載asset中的json文件_Android解析Asset目錄下的json文件

在app module中的src/main/assets目錄下我們準備了兩個json文件&#xff1a;destination.json如下&#xff1a;{"main/tabs/sofa": {"isFragment": true,"asStarter": false,"needLogin": false,"pageUrl": "main/tabs…

一文搞懂 Promise、Genarator、 Async 三者的區別和聯系

非985/211大學畢業&#xff0c;軟件工程專業&#xff0c;前端&#xff0c;坐標&#xff1a;北京工作三年多&#xff0c;第一家人數 30 多人的創業公司&#xff0c;1 年多。第二家屬于前端技術不錯的公司&#xff0c;2 年多。01我是一個喜歡在技術領域“折騰”的人&#xff0c;技…

閉包,sync使用細節

代碼 先看代碼如下&#xff1a; func main() {var a []intfor i : 0; i < 100; i {go func() {a append(a, i)}()}time.Sleep(2 * time.Second)fmt.Println(a) } 這段測試代碼是想要一個元素為0到100的切片&#xff0c;但是這一小段代碼隱藏了很多的問題。 閉包函數 先看這…

dynamic 儀表板_儀表板完成百萬美元交易

dynamic 儀表板問題 (The Problem) Anybody dealing with tech products and data-focused services runs into the same fundamental problem: what you do is technical but non-technical people control the budget. In other words:任何處理高科技產品和以數據為中心的服務…

checkStyle -- 代碼風格一致

download page: http://sourceforge.net/project/showfiles.php?group_id80344&package_id107587 轉載于:https://www.cnblogs.com/xuqiang/archive/2010/10/26/1953431.html