本文總結了前端老司機常常問題的一些問題並結合我的總結給出了比較詳盡的答案。網易阿里騰訊校招社招必備知識點。javascript
原理講解參考:前端增加-從新定義大前端css
官方博客:前端學堂 fed123.com前端
readyState
狀態loading、interactive和complete。最後是頁面onload,分別是loadEventStart和loadEventEnd時間節點。
能夠經過這個接口統計前端的頁面性能數據。vue
參考下:瀏覽器工做原理 瀏覽器渲染與阻塞原理html5
第一部分經過performance.time這個api咱們能夠了解瀏覽器加載網頁的流程,瀏覽器邊加載html邊構建DOM樹,固然會有容錯和修正機制。瀏覽器解析到行內css和內聯css會立馬加入到構建渲染樹,解析到外鏈css就開始加載,加載完以後也會合併到渲染樹的構建中,最後將渲染樹和DOM作節點鏈路匹配,也叫layout階段,計算每一個DOM元素最終在屏幕上顯示的大小和位置。 遍歷順序爲從左至右,從上到下,繪製在屏幕上,layout過程當中可能會觸發頁面迴流和重繪,好比某個外鏈css加載完解析以後合併構建到渲染樹中,其中某個css改變了DOM樹種某個元素的定位(改爲絕對定位)或者改變了長寬邊距等位置信息,會觸發從新layout,因此會迴流reflow。重繪是好比css改變了以前的背景圖片顏色,瀏覽器會從新繪製。java
會有渲染阻塞,瀏覽器刷新的頻率大概是60次/秒, 也就是說刷新一次大概時間爲16ms,若是瀏覽器對每一幀的渲染工做超過了這個時間, 頁面的渲染就會出現卡頓的現象。瀏覽器內核中有3個最主要的線程:JS線程,UI渲染線程,事件處理線程。此外還有http網絡線程,定時器任務線程,文件系統處理線程等等。node
總結一下,渲染阻塞有兩個方面:react
js引擎只執行同步任務, 異步任務會有工做線程來執行,當須要進行異步操做(定時器、ajax請求、dom事件註冊等), 主線程會發一個異步任務的請求, 相應的工做線程接受請求; 當工做線程完成工做以後, 通知主線程;主線程接收到通知以後, 會執行必定的操做(回調函數)。主線程和工做線程之間的通知機制叫作事件循環。webpack
當調用棧爲空時, 主線程會從任務隊列裏取一條消息並放入當前的調用棧當中執行, 主線程會一直重複這個動做直到消息隊列爲空。 這個過程就叫作事件循環 (event-loop)。
關於宏任務和微任務,參考 事件流、事件模型、事件循環概念理解? 瀏覽器線程理解與microtask與macrotask
ES6新引入了Promise標準,同時瀏覽器實現上多了一個microtask微任務概念。在ECMAScript中,microtask稱爲jobs
,macrotask可稱爲task
。
task--》渲染 --》宏任務 --》渲染 .....
task--》jobs --》渲染 --》宏任務 --》jobs --》渲染 .....
AST 參考:程序語言進階之DSL與AST實戰解析
將抽象語法樹以前要先了解下NLP中文法的機率。任何一種語言,具體說就是DSL,都有本身的一套文法,用來表示這套語言的邏輯規範。不一樣的文法寫出來的語法表達式也不同。咱們根據語法表達式來解析語言,就能夠造成一個AST抽象語法樹。而後能夠做進一步處理。我經常使用的是PEG解析表達式語法。能夠很輕鬆的寫出語法的每一條產生式規則,來構造生成AST。所謂AST能夠理解成按照必定語法結構組成的詞彙流,每一個詞彙有特定的語法含義,好比說這是一個聲明,這個一個操做符等等。
上面這個圖是蘋果最先作的KHTML渲染引擎中的KJS(javascript引擎),他是基於AST來實現的JavaScript語言解析的,先經過詞法分析獲得JSTokens流,而後通過語法分析獲得抽象語法樹,而後通過字節碼生成器,轉換成字節碼。字節碼通過JavaScript虛擬機JIT編譯成機器碼,而後執行。這是最初的設計架構,後來蘋果公司基於此重構出了webkit渲染引擎,google基於webkit單獨維護,稱爲blink渲染引擎,chrome的JS引擎改造爲V8引擎。參考:簡述Chromium, CEF, Webkit, JavaScriptCore, V8, Blink
舉個例子經常使用的babel插件的原理就是基於babylon詞法語法分析器生成抽象語法樹,將代碼文本轉換成按照特定語法組合的token流集合,而後通過babtlon-traverse這個組件來負責處理遍歷語法樹,訪問每一個token節點,經過對token的處理,能夠生成咱們須要的AST語法樹,而後再經過babylon-generator這個組件來作代碼生成,根據AST生成代碼。好比能夠將 箭頭函數 轉換成 function函數。
瀏覽器中,經過開發者調試工具分析就能看到,下載完js腳本後,首先瀏覽器要先解析代碼=》初始化上下文環境=》執行代碼,整個是evaluate script的過程,解析代碼的過程也是編譯js的過程因此看最前面第一步就是compile script,將js代碼編譯成字節碼(這一塊涉及到瀏覽器js引擎的優化,v8引擎是編譯成字節碼,後面通過JIT解析執行(這個參考 你不知道的LLVM編譯器 能夠提高效率作動態優化), 這個相似於java、C#這些須要將源代碼編譯成中間語言,而後在虛擬機執行,javascript編譯成字節碼後面也是在虛擬機執行),而後就開始執行腳本。
擴展性主要是從功能上考慮,容錯性是從數據上考慮。
我主要考慮的是組件複用,能夠將一類組件歸類,好比商品卡片,基本都是頭圖加標題行動點,價格,按鈕。這就是最基礎的一個組件。擴展性能夠經過數據來作響應式的展現,好比新增一個描述,數據模型新增描述字段,有描述字段卡片上就展現描述,沒有就不展現。像點擊按鈕的加購功能能夠單獨作成功能組件,統一處理,而不放在卡片上。由於這種加購每每附帶的是商業邏輯,有不少業務邏輯要處理,獨立出來反而更利於維護和拓展。
錯誤處理咱們這邊是基於組件的方式來處理,開發一個錯誤處理的功能組件,提供thenable的能力,區分不一樣的錯誤類型,提供統一埋點作監控和記錄。
參考下:HTTP協商緩存VS強緩存原理
前面介紹navigation api時候介紹了瀏覽器加載頁面的各個關鍵時間節點。和緩存相關的主要有兩部分
-》強緩存,判斷依據是expires(http 1.0協議規定)和cache-control(http 1.1協議規定)字段,expires是絕對時間,cache-control有可選值no-cache(不使用本地緩存,走協商緩存),no-store(禁止瀏覽器緩存數據,每次都是從新獲取數據),public(能夠被客戶端和中間商CDN作緩存),private(只能客戶端緩存,CDN不能緩存)
-》協商緩存,用到的響應頭字段是last-modified/if-modified-since, Etag/if-none-match,這是兩對哈,每隊/前面一個是服務端返回的response header中的字段,/後面是請求頭request攜帶的頭部字段,第一次請求資源瀏覽器會返回last-modified(最後修改時間),後面再次請求請求頭會帶上if-modified-since,固然這個值和上次瀏覽器返回的last-modified是同樣的,而後瀏覽器判斷若是文件沒有變化,那麼返回304 Not Modified http code,響應請求頭不會攜帶last-modified字段,瀏覽器從緩存取數據,也不用更新last-modified字段,若是有修改,那麼響應頭返回新的last-modified字段數據,返回響應內容。Etag/if-none-match這一對是一樣的邏輯,不一樣之處是用etag標識來判斷文件是否修改,而不是用時間,由於服務器時間可能會變的,還會收到時區的影響。還有一點是每次請求都會返回etag字段,即便沒有變化。
我目前開發分狀況用不一樣的技術框架。
對於前端領域來講,目前前端框架作掉了不少事情,搭建好項目框架以後,開發的就行就是填功能。所編寫的模塊和組件的模式也比較固定,能夠根據具體狀況來實現。
angular
特色: 數據雙向綁定-》數據驅動開發的思想
html標籤化的模板,模塊化思想
數據綁定,控制器,依賴注入,
服務,指令,過濾器…
優勢: 比較完善規範,文檔、社區比較活躍
模塊清晰,代碼明瞭
缺點: 功能規範太固定,開發發揮空間小。
相對react和vue,不夠輕量化
擴展性不夠靈活
react
特色: 強大的組件化思想,任意封裝、組合
首創JSX語法,virtual dom智能patch,靈活高效
輕量,易擴展,模塊清晰,代碼明瞭
社區生態完善,組件庫、插件庫豐富
缺點: 組件難以在複雜交互場景複用
側重於作組件,作view展現層,對於業務邏輯等封裝治理不如angular強大
JSX中html模板不夠完備和健壯,好比一些屬性變換寫法,綁定事件大小寫
vue
特色: 文檔豐富,容易上手
模板較完備,聲明式渲染,插值表達式與指令系統,
事件處理器,修飾符,計算屬性 ,簡單易用,功能強
社區生態完善,組件庫、插件庫豐富
缺點: 輕量框架使用是要結合生態插件組件使用,項目初始配置比較麻煩,
不過能夠參考各類場景的標準模板配置,不少腳手架
聲明式渲染與命令式渲染: 這個涉及到函數式編程中的一個聲明式編程和命令式編程的概念。
好比命令式編程:
聲明式編程:
聲明式編程隱藏了函數處理細節,命令式編程則須要處理細節。
聲明式編程的好處是簡單化,易於理解,減小勞動量。好比vue中的指令綁定事件,綁定屬性都是這樣。@click,:title等等,用的時候很方便,這正是聲明式編程最直觀的好處。
怎麼可能,我又不是你。ES6中最經常使用的像變量定義這部分用let、const能夠避免一些坑,異步處理能夠用promise,不過我到喜歡用async/await 更簡潔好用。
發展趨勢: 整體來講前端開發更規範,更簡單,語法更完備和成熟。支持的功能加強,開發效率提高,體驗加強。
能夠的,說一下原理,須要將 .less 文件最終解析成CSS,less是一種DSL,咱們能夠現根據less預發,先將其解析成AST,而後解析成CSS便可。 我推薦用PEG.js這種解析表達式語法更簡單些,只須要描述產生式規則便可。也能夠本身根據LESS預發來寫正則表達來匹配規則,而後轉換成css。比較經常使用的是PostCSS,處理流程以下:參考官方文檔
PostCSS的處理流程也是通過詞法解析語法分析,將讀取到的文件字符轉化成詞彙流tokens,根據語法分析,根據less的語法,解析成一個AST。
source string → tokens → AST
核心組件有:
你們以前應該用過gulp,grunt這種代碼打包工具,定義不一樣的打包任務和打包流程。我用的比較多的rollup這個打包工具,配置起來比較簡單些。
webpack也是用來作代碼打包,能夠作代碼分析,拆分,混淆,壓縮等等,基於他的插件擴展機制能夠作不少事情。分析webpack的原理,能夠先從webpack配置文件提及。參考:webpack編譯代碼原理介紹 用webpack4和一些插件提高代碼編譯速度
首先做爲打包工具,要定義打包的輸入entry和輸出output;而後是定義webpack要用到的module,好比babel js loader, cssloader等等。執行編譯具體的流程是:
加載webpack配置文件 --》 根據配置初始化編譯器compiler --》找到入口,根據loader配置開始編譯入口文件以及層層依賴 --》編譯完成以後,能夠獲得全部編譯過的文件和依賴關係結構 --》根據依賴關係將模塊組裝成一個個包含多個模塊的chunk,而後根據配置寫到輸出文件。
webpack構建流程可分爲如下三大階段。
分析依賴是在編譯過程當中完成的,從入口查找依賴,最後造成依賴關係。 爲了提升效率,能夠記錄分析過的依賴,這樣下次遇到一樣的模塊就不用再分析,直接引用編譯過的依賴就能夠了。
tree-shaking的名字原理同樣,就是搖一搖大樹,落下來的葉子都是冗餘的部分。Tree-shaking 較早由 Rich_Harris 的 rollup 實現,後來,webpack2 也增長了tree-shaking 的功能。其實在更早,google closure compiler 也作過相似的事情。三個工具的效果和使用各不相同,使用方法能夠經過官網文檔去了解。
tree shaking的目的是去掉無用代碼,減小代碼體積。其實對於編譯的編程語言對應的編譯器基本都有判斷哪些代碼不會影響輸出,從而在編譯時移除這些代碼的功能,稱爲DCE(dead code elimination)。tree shaking 是DCE的一種實現,傳統的是消除沒有引用不會執行的代碼,tree shaking 主要是要消除沒有用的代碼。
Dead Code 通常具備如下幾個特徵
•代碼不會被執行,不可到達
•代碼執行的結果不會被用到
•代碼只會影響死變量(只寫不讀)
在前端代碼打包處理中,最終都會有個代碼壓縮混淆的環節,這個環節其實會完成DCE的工做,會將這些dead code移除。
可是uglify代碼是隻是單個單個文件處理,並不能分析出這個代碼有沒有被其餘文件用到,固然也不會對這些爲被調用的函數作處理,如上圖uglify就不會去除沒用到的get函數,因此就須要tree shaking。tree shaking是有限制的,只能消除函數和import/export的變量,不會處理import/export的class(由於javascript動態語言特性使得分析比較困難,可能致使之外的錯誤,side effect比較大), 對於純函數處理效果較好。
具體參考https://github.com/wintercn/b...
top
, right
, bottom
, and left
. The offset does not affect the position of any other elements.This value always creates a new stacking context. Note that a sticky element "sticks" to its nearest ancestor that has a "scrolling mechanism" (created when overflow
is hidden
, scroll
, auto
, or overlay
), even if that ancestor isn't the nearest actually scrolling ancestor. This effectively inhibits any "sticky" behavior (see the Github issue on W3C CSSWG).absolute就只能根據祖先類元素(父類以上)進行定位,而這個祖先類還必須是以postion非static方式定位的, 舉個例子,a元素使用absoulte定位,它會從父類開始找起,尋找以position非static方式定位的祖先類元素(注意,必定要是直系祖先纔算哦~),直到<html>標籤爲止,這裏還須要注意的是,relative和static方式在最外層時是以<body>標籤爲定位原點的,而absoulte方式在無父級是position非static定位時是以<html>做爲原點定位。
參考: position屬性
關於Layout and the containing block,看下官方介紹的contain block,另外相關的點是 BFC:如何建立塊級格式化上下文(block formatting context),BFC有什麼用
動畫能夠看作是一個連續的幀序列的組合。咱們把網頁的動畫分紅兩大類 —— 一類是合成器動畫,一類是非合成器動畫(UC 內部也將其稱爲內核動畫或者 Blink Animation,雖然這不是 Chrome 官方的術語)。
合成器動畫又能夠分爲兩類:
Blink 觸發的動畫,若是是 Transform 和 Opacity 屬性的動畫基本上均可以由合成器運行,由於它們沒有改變圖層的內容。不過即便能夠交由合成器運行,它們也須要產生一個新的 Main Frame 提交給合成器來觸發這個動畫,若是這個 Main Frame 包含了大量的圖層變動,也會致使觸發的瞬間卡頓,頁端事先對圖層結構進行優化能夠避免這個問題。
非合成器動畫也能夠分爲兩類:
合成器動畫和非合成器動畫在渲染流水線上有較大的差別,後者更復雜,流水線更長。上面四種動畫的分類,按渲染流水線的複雜程度和理論性能排列(複雜程度由低到高,理論性能由高到低):
開啓硬件加速的方法不少,好比transform: translate3d(0,0,0); 加了以後,在chrome開發者工具中的layer欄目下能夠看到多了一層 composition layer,同時給出了理由描述是開啓了3D transform,這個元素就放入了Composited Layer中託管,其動畫效果都是在單獨一個圖形層上面處理,不會影響其它層。
什麼狀況下能使元素得到本身的層?雖然 Chrome 的啓發式方法(heuristic)隨着時間在不斷髮展進步,可是從目前來講,知足如下任意狀況便會建立層:
使用3D硬件加速提高動畫性能時,最好給元素增長一個z-index屬性,人爲干擾複合層的排序,能夠有效減小chrome建立沒必要要的複合層,提高渲染性能,移動端優化效果尤其明顯。
關於層的介紹:gpu-accelerated-compositing-in-chrome
理解CSS animations 和 transitions的性能問題與動畫調試
參考MDN,最新的 ECMAScript 標準定義了 7 種數據類型:
除 Object 之外的全部類型都是不可變的(值自己沒法被改變)。例如,與 C 語言不一樣,JavaScript 中字符串是不可變的。JavaScript 中對字符串的操做必定返回了一個新字符串,原始字符串並無被改變。
標準的" 對象, 和函數【複雜數據類型】
日期:內建的 Date 對象
數組和類型數組:
數組是一種使用整數做爲鍵(integer-key-ed)屬性和長度(length)屬性之間關聯的常規對象。此外,數組對象還繼承了 Array.prototype 的一些操做數組的便捷方法。例如, indexOf (搜索數組中的一個值) or push (向數組中添加一個元素),等等。 這使得數組是表示列表或集合的最優選擇。
類型數組(Typed Arrays)是ECMAScript Edition 6中新定義的 JavaScript 內建對象,提供了一個基本的二進制數據緩衝區的類數組視圖。
集合對象Map、WeakMap、Set、WeakSet:這些數據結構把對象的引用看成鍵,其在ECMAScript第6版中有介紹。當 Map
和 WeakMap
把一個值和對象關聯起來的時候, Set
和 WeakSet
表示一組對象。 Map和WeakMaps之間的差異在於,在前者中,對象鍵是可枚舉的。
結構化數據JSON:JSON (JavaScript Object Notation) 是一種輕量級的數據交換格式
參考:標準全局內置對象
兩種類型:
1. ECMAScript變量包含兩種不一樣類型的值:基本類型值、引用類型值;
2. 基本類型值:指的是保存在棧內存中的簡單數據段;
3. 引用類型值:指的是那些保存在堆內存中的對象,意思是,變量中保存的實際上只是一個指針,這個指針執行內存中的另外一個位置,由該位置保存對象;
兩種訪問方式:
4. 基本類型值:按值訪問,操做的是他們實際保存的值;
5. 引用類型值:按引用訪問,當查詢時,咱們須要先從棧中讀取內存地址,而後再順藤摸瓜地找到保存在堆內存中的值;
數據複製
三種變量類型檢測
1. Typeof操做符是檢測基本類型的最佳工具;
2. 若是變量值是null或者對象,typeof 將返回「object」;結合null == null 來判斷
3. Instanceof用於檢測引用類型,能夠檢測到具體的,它是什麼類型的實例;
4. 若是變量是給定引用類型的實例,instanceof操做符會返回true;
5. Object.prototype.toString.call(xx) 來打印原型判斷類型
能夠在這裏試一下:在線編程環境
鏈表:
插入鏈表節點:
刪除鏈表節點:
雙向鏈表:
循環鏈表:
下面給一個最簡單的單項鍊表示例:
快速排序:
(1)在數據集之中,選擇一個元素做爲"基準"(pivot)。
(2)全部小於"基準"的元素,都移到"基準"的左邊;全部大於"基準"的元素,都移到"基準"的右邊。
(3)對"基準"左邊和右邊的兩個子集,不斷重複第一步和第二步,直到全部子集只剩下一個元素爲止。
選擇排序:
(1)首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
(2)再從剩餘未排序元素中繼續尋找最小(大)元素,而後放到已排序序列的末尾
(3)直到全部都排序
冒泡排序:
直接插入排序:
(1)將待排序數組取一個數值插入到已排序數組中合適的位置
(2)重複取數據,直到全部數據取完
分治法的設計思想是:將一個難以直接解決的大問題,分割成一些規模較小的相同問題,以便各個擊破,分而治之。
分治策略是:對於一個規模爲n的問題,若該問題能夠容易地解決(好比說規模n較小)則直接解決,不然將其分解爲k個規模較小的子問題,這些子問題互相獨立且與原問題形式相同,遞歸地解這些子問題,而後將各子問題的解合併獲得原問題的解。這種算法設計策略叫作分治法。
分治法所能解決的問題通常具備如下幾個特徵:
1) 該問題的規模縮小到必定的程度就能夠容易地解決
2) 該問題能夠分解爲若干個規模較小的相同問題,即該問題具備最優子結構性質。
3) 利用該問題分解出的子問題的解能夠合併爲該問題的解;
4) 該問題所分解出的各個子問題是相互獨立的,即子問題之間不包含公共的子子問題。
所謂貪心算法是指,在對問題求解時,老是作出在當前看來是最好的選擇。也就是說,不從總體最優上加以考慮,他所作出的僅是在某種意義上的局部最優解。
貪心算法沒有固定的算法框架,算法設計的關鍵是貪心策略的選擇。必須注意的是,貪心算法不是對全部問題都能獲得總體最優解,選擇的貪心策略必須具有無後效性,即某個狀態之後的過程不會影響之前的狀態,只與當前狀態有關。
因此對所採用的貪心策略必定要仔細分析其是否知足無後效性。
貪心算法的基本思路:
1.創建數學模型來描述問題。
2.把求解的問題分紅若干個子問題。
3.對每一子問題求解,獲得子問題的局部最優解。
4.把子問題的解局部最優解合成原來解問題的一個解。
動態規劃過程是:每次決策依賴於當前狀態,又隨即引發狀態的轉移。一個決策序列就是在變化的狀態中產生出來的,因此,這種多階段最優化決策解決問題的過程就稱爲動態規劃。
基本思想與分治法相似,也是將待求解的問題分解爲若干個子問題(階段),按順序求解子階段,前一子問題的解,爲後一子問題的求解提供了有用的信息。在求解任一子問題時,列出各類可能的局部解,經過決策保留那些有可能達到最優的局部解,丟棄其餘局部解。依次解決各子問題,最後一個子問題就是初始問題的解。
因爲動態規劃解決的問題多數有重疊子問題這個特色,爲減小重複計算,對每個子問題只解一次,將其不一樣階段的不一樣狀態保存在一個二維數組中。
與分治法最大的差異是:適合於用動態規劃法求解的問題,經分解後獲得的子問題每每不是互相獨立的(即下一個子階段的求解是創建在上一個子階段的解的基礎上,進行進一步的求解)。
能採用動態規劃求解的問題的通常要具備3個性質:
(1) 最優化原理:若是問題的最優解所包含的子問題的解也是最優的,就稱該問題具備最優子結構,即知足最優化原理。
(2) 無後效性:即某階段狀態一旦肯定,就不受這個狀態之後決策的影響。也就是說,某狀態之後的過程不會影響之前的狀態,只與當前狀態有關。
(3)有重疊子問題:即子問題之間是不獨立的,一個子問題在下一階段決策中可能被屢次使用到。(該性質並非動態規劃適用的必要條件,可是若是沒有這條性質,動態規劃算法同其餘算法相比就不具有優點)
在包含問題的全部解的解空間樹中,按照深度優先搜索的策略,從根結點出發深度探索解空間樹。當探索到某一結點時,要先判斷該結點是否包含問題的解,若是包含,就從該結點出發繼續探索下去,若是該結點不包含問題的解,則逐層向其祖先結點回溯。(其實回溯法就是對隱式圖的深度優先搜索算法)
相似於回溯法,也是一種在問題的解空間樹T上搜索問題解的算法。但在通常狀況下,分支限界法與回溯法的求解目標不一樣。回溯法的求解目標是找出T中知足約束條件的全部解,而分支限界法的求解目標則是找出知足約束條件的一個解,或是在知足約束條件的解中找出使某一目標函數值達到極大或極小的解,即在某種意義下的最優解。
因爲求解目標不一樣,致使分支限界法與回溯法在解空間樹T上的搜索方式也不相同。回溯法以深度優先的方式搜索解空間樹T,而分支限界法則以廣度優先或以最小耗費優先的方式搜索解空間樹T。
架構,我理解主要作:系統分解、服務分層的工做。
持續集成 (Continuous integration,簡稱CI)。項目是一個迭代一個迭代快速開發,每一個迭代開發不一樣的feature,全部的feature合在一塊兒構成完整的功能。
持續集成的目的,就是讓產品能夠快速迭代,同時還能保持高質量。它的核心措施是,代碼集成到主幹以前,必須經過自動化測試。只要有一個測試用例失敗,就不能集成。
Martin Fowler說過,"持續集成並不能消除Bug,而是讓它們很是容易發現和改正。"
與持續集成相關的,還有兩個概念,分別是持續交付和持續部署。
設計模式(Design pattern)表明了最佳的實踐,一般被有經驗的面向對象的軟件開發人員所採用。設計模式是軟件開發人員在軟件開發過程當中面臨的通常問題的解決方案。
單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種類型的設計模式屬於建立型模式,它提供了一種建立對象的最佳方式。
這種模式涉及到一個單一的類,該類負責建立本身的對象,同時確保只有單個對象被建立。這個類提供了一種訪問其惟一的對象的方式,能夠直接訪問,不須要實例化該類的對象。
注意:
在策略模式(Strategy Pattern)中,一個類的行爲或其算法能夠在運行時更改。這種類型的設計模式屬於行爲型模式。
在策略模式中,咱們建立表示各類策略的對象和一個行爲隨着策略對象改變而改變的 context 對象。策略對象改變 context 對象的執行算法。
很好理解,好比上面給的一個異常處理的代碼,寫個簡單的示例。
瀏覽器能夠跑下結果看看:
這就是策略模式,不一樣的狀況,輸出的結果是不同的。
在node中,事件循環表現出的狀態與瀏覽器中大體相同。不一樣的是node中有一套本身的模型。node中事件循環的實現是依靠的libuv引擎。咱們知道node選擇chrome v8引擎做爲js解釋器,v8引擎將js代碼分析後去調用對應的node api,而這些api最後則由libuv引擎驅動,執行對應的任務,並把不一樣的事件放在不一樣的隊列中等待主線程執行。 所以實際上node中的事件循環存在於libuv引擎中。下面是一個libuv引擎中的事件循環的模型:
注:模型中的每個方塊表明事件循環的一個階段
這個模型是node官網上的一篇文章中給出的,我下面的解釋也都來源於這篇文章。我會在文末把文章地址貼出來,有興趣的朋友能夠親自與看看原文。
咱們知道Linux中有個高效多路IO複用的poll/select模型,加強改進有個epoll模型。參考這裏
上面這個node的poll模型中,咱們能夠大體分析出node中的事件循環的順序:
外部輸入數據-->輪詢階段(poll)-->檢查階段(check)-->關閉事件回調階段(close callback)-->定時器檢測階段(timer)-->I/O事件回調階段(I/O callbacks)-->閒置階段(idle, prepare)-->輪詢階段...
以上各階段的名稱是根據我我的理解的翻譯,爲了不錯誤和歧義,下面解釋的時候會用英文來表示這些階段。
這些階段大體的功能以下:
setTimeout()
和 setInterval()
。setImmediate()
的回調。setImmediate()
的回調會在這個階段執行。socket.on('close', ...)
這種close事件的回調。詳情參考:瀏覽器與node環境的事件循環機制
知道connect的時候出於性能優化的考慮作了一層淺比較。