能夠理解是每次執行棧執行的代碼就是一個宏任務(包括每次從事件隊列中獲取一個事件回調並放到執行棧中執行)。javascript
瀏覽器爲了可以使得JS內部(macro)task與DOM任務可以有序的執行,會在一個(macro)task執行結束後, 在下一個(macro)task 執行開始前,對頁面進行從新渲染,流程以下:css
(macro)task->渲染->(macro)task->... (macro)task主要包含:script(總體代碼)、setTimeout、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(Node.js 環境)html
能夠理解是在當前 task 執行結束後當即執行的任務。也就是說,在當前task任務後,下一個task以前,在渲染以前。前端
因此它的響應速度相比setTimeout(setTimeout是task)會更快,由於無需等渲染。也就是說,在某一個macrotask執行完後,就會將在它執行期間產生的全部microtask都執行完畢(在渲染前)。html5
microtask主要包含:Promise.then、MutaionObserver、process.nextTick(Node.js 環境)java
在事件循環中,每進行一次循環操做稱爲 tick,每一次 tick 的任務處理模型是比較複雜的,但關鍵步驟以下:android
執行一個宏任務(棧中沒有就從事件隊列中獲取) 執行過程當中若是遇到微任務,就將它添加到微任務的任務隊列中 宏任務執行完畢後,當即執行當前微任務隊列中的全部微任務(依次執行) 當前宏任務執行完畢,開始檢查渲染,而後GUI線程接管渲染 渲染完畢後,JS線程繼續接管,開始下一個宏任務(從事件隊列中獲取)ios
dom tree + css tree = render tree
完了之後迴流 肯定每一個元素的幾何位置 重繪肯定每一個元素的像素nginx
節點的幾何屬性發生改變或者因爲樣式發生改變而不會影響佈局的,稱爲重繪, 例如outline, visibility, color、background-color等,重繪的代價是高昂的,由於瀏覽器必須驗證DOM樹上其餘節點元素的可見性。web
迴流是佈局或者幾何屬性須要改變就稱爲迴流。迴流是影響瀏覽器性能的關鍵因素,由於其變化涉及到部分頁面(或是整個頁面)的佈局更新。 一個元素的迴流可能會致使了其全部子元素以及DOM中緊隨其後的節點、祖先節點元素的隨後的迴流。 迴流一定會發生重繪,重繪不必定會引起迴流。
<meta name='viewport' content='width=device-width,initial-scale=0.5'>
複製代碼
Vue2.x:使用Object.defineProperty實現雙向綁定,其實現的功能是數據劫持,在觀察者訂閱者模式當中是觀察者的角色 此外,當觀察者發現變化時能夠告知manager,manager告知訂閱者,訂閱者對相應的作出反應(compile)。 p.s.:對於某些數組操做失靈,其採用的方法是,改寫某個數組的原型鏈,使其指向改造過的方法原型。 Vue3.x:使用Proxy來代替Object.defineProperty,優勢總結就是,劫持能力比前者強大,能夠監控整個對象,並且不會對 數組的某些操做失靈。
四個關鍵方法:pathch函數分爲兩種狀況初始化時,將全部的虛擬dom實現再插入到container上
第二次當虛擬dom改變時,會使用diff算法找出不一樣,使用新dom替換舊dom(同層比較,不移動
說到渲染,有個h函數,做用是將虛擬dom渲染成爲真實dom,本質是一個遞歸函數,當不是葉子節點時會不斷的
向下調用h函數,參數有標籤名,屬性,子節點;葉子節點的調用是,標籤名,屬性,text
同層遞歸順序比較 只比較同層的dom結構,比較是依次深度遞歸比較的,順序是先看此節點還在不在,再看節點屬性是否改變,再看文本內容,節點是否被替換
能夠配置默認的url、transformRequest、transformResponse對請求體和響應體作對應的處理、headers自定義
timeout自定義、onUploadProgres onDownloadProgress、proxy設置代理、cancelToken用於取消響應
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// executor 函數接收一個 cancel 函數做爲參數
cancel = c;
})
});
// 取消請求
cancel();
複製代碼
事件綁定:w3c事件委託 addEventListener,removeEventListener 第三個參數是false則爲冒泡 true是捕獲
事件捕獲與冒泡:捕獲是先觸發父元素再觸發子元素 阻止冒泡:e.stopPropagation()、e.cancelBubble = true 阻止默認事件:e.preventDefault()、e.returnValue = false; 事件委託:將事件綁定在父元素上能夠減小綁定的數量以及動態增減子元素都無所謂 好比 focus、blur 之類的事件自己沒有事件冒泡機制,因此沒法委託; mousemove、mouseout 這樣的事件,雖然有事件冒泡,可是隻能不斷經過位置去計算定位,對性能消耗高,所以也是不適合於事件委託的;
任何能夠輸入的地方都有可能引發,包括URL XSS 常見的注入方法:
防護:客戶端求情參數:包括用戶輸入,url參數、post參數。
csrf是讓用戶住不知情的狀況下,冒用其身份發起了一個請求
在三方網站中,利用圖片的url來執行get請求,表單執行post請求在後臺接收到請求的時候,能夠經過請求頭中的Referer請求頭來判斷請求來源 一般來講 CSRF 是由 XSS 實現的,CSRF 時常也被稱爲 XSRF(CSRF 實現的方式還能夠是直接經過命令行發起請求等)。 本質上講,XSS 是代碼注入問題,CSRF 是 HTTP 問題。 XSS 是內容沒有過濾致使瀏覽器將攻擊者的輸入當代碼執行。CSRF 則是由於瀏覽器在發送 HTTP 請求時候自動帶上 cookie,而通常網站的 session 都存在 cookie裏面(Token驗證能夠避免)。
——因爲IEEE 754標準的緣由 解決辦法能夠將結果乘1000再除1000進行一個截尾操做 parseFloat((0.1 + 0.2).toFixed(10))
方法僅僅返回匹配指定選擇器的第一個元素。若是你須要返回全部的元素,請使用 querySelectorAll() 方法替代。 因爲querySelector是按css規範來實現的,因此它傳入的字符串中第一個字符不能是數字. 最後再根據查詢的資料總結一下: query選擇符選出來的元素及元素數組是靜態的,而getElement這種方法選出的元素是動態的。 靜態的就是說選出的全部元素的數組,不會隨着文檔操做而改變. 在使用的時候getElement這種方法性能比較好,query選擇符則比較方便.
Boolean([]) //true
Boolean(undefined) // false
Boolean(null) // false
Boolean(0) // false
Boolean(NaN) // false
Boolean('') // false
Number([]) // 0
Number({}) // NaN。
複製代碼
前者支持動態導入,也就是 require(${path}/xx.js),後者目前不支持,可是已有提案
前者是同步導入,由於用於服務端,文件都在本地,同步導入即便卡住主線程影響也不大。然後者是異步導入,由於用於瀏覽器,須要下載文件,若是也採用同步導入會對渲染有很大影響
前者在導出時都是值拷貝,就算導出的值變了,導入的值也不會改變,因此若是想更新值,必須從新導入一次。可是後者採用實時綁定的方式,導入導出的值都指向同一個內存地址,因此導入值會跟隨導出值變化
後者會編譯成 require/exports 來執行的
--瀏覽器搜索自身的DNS緩存: 首先瀏覽器會去搜索自身的DNS緩存,看緩存有沒有過時,過時的話緩存的解析就結束了(chrome緩存的時間只有一分鐘,查看chrome的緩存可打開:chrome:/net-internals/#dns )。 --搜索操做系統自身的DNS緩存: 若是瀏覽器沒有找到緩存或者緩存過時失效,瀏覽器就會搜索操做系統自身的緩存,沒有找到或者失效,解析結束(操做系統的緩存:window系統是一天,mac系統嚴格根DNS協議中的TTL)。 --讀取本地的hosts文件: 若操做系統的緩存也沒有找到或失效,瀏覽器就會去讀取本地的hosts文件(Hosts文件也能夠創建域名到IP地址的綁定關係,能夠經過編輯Hosts文件來達到名稱解析的的。 例如,咱們須要屏蔽某個域名時,就能夠將其地址指向一個不存在IP地址,以達到屏蔽的效果)。 --瀏覽器發起一個DNS的系統調用: hosts中沒有找到對應的配置項的話,瀏覽器發起一個DNS的調用(向本地主控DNS服務,通常來講是你的運營商提供的)。
--經過 DNS 查詢 IP 地址的操做稱爲域名解析,負責執行解析這一操做的就叫解析器。 解析器其實是一段程序,它包含在操做系統的 Socket 庫中 --調用解析器後,解析器會向 DNS 服務器(運營商提供的)發送查詢消息。 --運營商服務會先查找自身緩存找到對應條目,沒有過時,解析成功,若沒找到對應條目,主控服務器會代替瀏覽器發起一個迭代的DNS解析的請求,先查找根域的), 運營商服務器拿到域名的IP,返回給操做系統的內核,同時緩存在了本身的緩存區,操做系統內核從DNS服務商拿來的IP地址返回給瀏覽器。 --瀏覽器再向 Web 服務器發送消息時,只要從該內存地址取出 IP地址,將它與 HTTP 請求消息一塊兒交給操做系統 .
--首先運營商服務從已經配置好的信息中拿到根域名的IP地址(這裏假設根域只有一個,實際是想13個根域發起請求),而後像根域發起請求羣問:"請問http:/www.lab.glasscom.com的IP地址是多少?",根域名查詢記錄數據後沒有找到,回答:"我不知道它的IP地址,不過我知道.com的權威服務器(ns)的地址,它xxx.xxx.xxx.xxx,你去問它吧"。運營商服務運營商服務拿到.com的IP地址,根據IP地址發起另外一個請求去詢問.com服務器問:"請問 http:/www.lab.glasscom.com的ns的IP地址是多少?",.com域服務器查找自身記錄數據後回答:「我不知道,我只知道.http://glasscom.com的IP地址」。 以此類推,只要重複前面的步驟,就能夠順藤摸瓜找到目標DNS服務器,只要向目標DNS 服務器發送查詢消息,就可以獲得咱們須要的答案,也就是 http:/www.lab.glasscom.com 的 IP 地址了。
--而HTTP傳輸時延對web的訪問速度的影響很大,在絕大多數狀況下是起決定性做用的,這是由TCP/IP協議的一些特色決定的。物理層上的緣由是光速有限、信道有限,協議上的緣由有丟包、慢啓動、擁塞控制等。 要提升訪問速度,最簡單的作法固然就是多設置幾個服務器,讓終端用戶離服務器「更近」。典型的例子是各種下載網站在不一樣地域不一樣運營商設置鏡像站,或者是像Google那樣設置多個數據中心。可是多設幾個服務器的問題也很多,一是多地部署時的困難,二是一致性無法保障,三則是管理困難、成本很高。實際上,在排除多地容災等特殊需求的狀況下,對大多數公司這種作法是不太可取的。固然,這種方案真正作好了,甚至是比後續所說的使用CDN要好的。
CDN是一種公共服務,他自己有不少臺位於不一樣地域、接入不一樣運營商的服務器,而所謂的使用CDN實質上就是讓CDN做爲網站的門面,用戶訪問到的是CDN服務器,而不是直接訪問到網站。因爲CDN內部對TCP的優化、對靜態資源的緩存、預取,加上用戶訪問CDN時,會被智能地分配到最近的節點,下降大量延遲,讓訪問速度能夠獲得很大提高。
--原理:CDN作了兩件事,一是讓用戶訪問最近的節點,二是從緩存或者源站獲取資源 CDN有個源站的概念,源站就是提供內容的站點(網站的真實服務器), 從源站取內容的過程叫作回源。 1)、用戶向瀏覽器提供要訪問的域名; 2)、瀏覽器調用域名解析庫對域名進行解析,因爲CDN對域名解析過程進行了調整,因此解析函數庫通常獲得的是該域名對應的CNAME記錄,爲了獲得實際IP地址, 瀏覽器須要再次對得到的CNAME域名進行解析以獲得實際的IP地址;在此過程當中,使用的全局負載均衡DNS解析, 如根據地理位置信息解析對應的IP地址,使得用戶能就近訪問。 3)、這次解析獲得CDN緩存服務器的IP地址,瀏覽器在獲得實際的IP地址之後,向緩存服務器發出訪問請求; 4)、緩存服務器根據瀏覽器提供的要訪問的域名,經過Cache內部專用DNS解析獲得此域名的實際IP地址,再由緩存服務器向此實際IP地址提交訪問請求; 5)、緩存服務器從實際IP地址得獲得內容之後,一方面在本地進行保存,以備之後使用,另外一方面把獲取的數據返回給客戶端,完成數據服務過程; 6)、客戶端獲得由緩存服務器返回的數據之後顯示出來並完成整個瀏覽的數據請求過程。 經過以上的分析咱們能夠獲得,爲了實現既要對普通用戶透明(即加入緩存之後用戶客戶端無需進行任何設置,直接使用被加速網站原有的域名便可訪問, 又要在爲指定的網站提供加速服務的同時下降對ICP的影響,只要修改整個訪問過程當中的域名解析部分,以實現透明的加速服務。
ES6 新增的一種新的數據結構,相似於數組,但成員是惟一且無序的,沒有重複的值。 Set 對象容許你儲存任何類型的惟一值,不管是原始值或者是對象引用。 向 Set 加入值的時候,不會發生類型轉換,因此5和"5"是兩個不一樣的值。Set 內部判斷兩個值是否不一樣,使用的算法叫作「Same-value-zero equality」, 它相似於精確相等運算符(===),主要的區別是NaN等於自身,而精確相等運算符認爲NaN不等於自身。 add(value):新增,至關於 array裏的push
delete(value):存在即刪除集合中value
has(value):判斷集合中是否存在 value
clear():清空集合
Array.from 方法能夠將 Set 結構轉爲數組
遍歷方法(遍歷順序爲插入順序) keys():返回一個包含集合中全部鍵的迭代器
values():返回一個包含集合中全部值得迭代器
entries():返回一個包含Set對象中全部元素得鍵值對迭代器
forEach(callbackFn, thisArg):用於對集合成員執行callbackFn操做,若是提供了 thisArg 參數,回調中的this會是這個參數,沒有返回值
WeakSet 與 Set 的區別:
WeakSet 只能儲存對象引用,不能存放值,而 Set 對象均可以 WeakSet 對象中儲存的對象值都是被弱引用的,即垃圾回收機制不考慮 WeakSet 對該對象的應用,若是沒有其餘的變量或屬性引用這個對象值, 則這個對象將會被垃圾回收掉(不考慮該對象還存在於 WeakSet 中),因此,WeakSet 對象裏有多少個成員元素,取決於垃圾回收機制有沒有運行, 運行先後成員個數可能不一致,遍歷結束以後,有的成員可能取不到了(被垃圾回收了),WeakSet 對象是沒法被遍歷的(ES6 規定 WeakSet 不可遍歷), 也沒有辦法拿到它包含的全部元素
集合 與 字典 的區別:
共同點:集合、字典 能夠儲存不重複的值 不一樣點:集合 是以 [value, value]的形式儲存元素,字典 是以 [key, value] 的形式儲存
const m = new Map()
const o = {p: 'haha'}
m.set(o, 'content')
m.get(o) // content
m.has(o) // true
m.delete(o) // true
m.has(o) // false
複製代碼
set(key, value):向字典中添加新元素 get(key):經過鍵查找特定的數值並返回 has(key):判斷字典中是否存在鍵key delete(key):經過鍵 key 從字典中移除對應的數據 clear():將這個字典中的全部元素刪除
Keys():將字典中包含的全部鍵名以迭代器形式返回 values():將字典中包含的全部數值以迭代器形式返回 entries():返回全部成員的迭代器 forEach():遍歷字典的全部成員
const map = new Map([[1, 1], [2, 2], [3, 3]])
console.log([...map]) // [[1, 1], [2, 2], [3, 3]]
複製代碼
const map = new Map([[1, 1], [2, 2], [3, 3]])
console.log(map) // Map {1 => 1, 2 => 2, 3 => 3}
複製代碼
由於 Object 的鍵名都爲字符串,而Map 的鍵名爲對象,因此轉換的時候會把非字符串鍵名轉換爲字符串鍵名。
function mapToObj(map) {
let obj = Object.create(null)
for (let [key, value] of map) {
obj[key] = value
}
return obj
}
const map = new Map().set('name', 'An').set('des', 'JS')
mapToObj(map)
複製代碼
function objToMap(map) {
let map = new Map()
for (let key of Object.keys(obj)) {
map.set(key, obj[key])
}
return map
}
objToMap({'name': 'An', 'des': 'JS'})
複製代碼
function mapToJson(map) {
return JSON.stringify([...map])
}
let map = new Map().set('name', 'An').set('des', 'JS')
mapToJson(map) // [["name","An"],["des","JS"]]
複製代碼
function jsonToMap(jsonStr) {
return objToMap(JSON.parse(jsonStr));
}
jsonToStrMap('{"name": "An", "des": "JS"}')
複製代碼
WeakMap 對象是一組鍵值對的集合,其中的鍵是弱引用,其中,鍵必須是對象,而值能夠是任意。
注意,WeakMap 弱引用的只是鍵名,而不是鍵值。鍵值依然是正常引用。
WeakSet 中,每一個鍵對本身所引用對象的引用都是弱引用,在沒有其餘引用和該鍵引用同一對象,這個對象將會被垃圾回收(相應的key則變成無效的),因此,WeakSet 的 key 是不可枚舉的。
has(key):判斷是否有 key 關聯對象 get(key):返回key關聯對象(沒有則則返回 undefined) set(key):設置一組key關聯對象 delete(key):移除 key 的關聯對象
成員惟1、無序且不重複 [value, value],鍵值與鍵名是一致的(或者說只有鍵值,沒有鍵名) 能夠遍歷,方法有:add、delete、has
成員都是對象 成員都是弱引用,能夠被垃圾回收機制回收,能夠用來保存DOM節點,不容易形成內存泄漏 不能遍歷,方法有add、delete、has
本質上是鍵值對的集合,相似集合 能夠遍歷,方法不少能夠跟各類數據格式轉換
只接受對象最爲鍵名(null除外),不接受其餘類型的值做爲鍵名 鍵名是弱引用,鍵值能夠是任意的,鍵名所指向的對象能夠被垃圾回收,此時鍵名是無效的 不能遍歷,方法有get、set、has、delete
// ES6
class Super {}
class Sub extends Super {}
const sub = new Sub();
Sub.__proto__ === Super;
// ES5
function Super() {}
function Sub() {}
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
var sub = new Sub();
Sub.__proto__ === Function.prototype;
複製代碼
function MyES5Array() {
Array.call(this, arguments);
}
// it's useless
const arrayES5 = new MyES5Array(3); // arrayES5: MyES5Array {}
class MyES6Array extends Array {}
// it's ok
const arrayES6 = new MyES6Array(3); // arrayES6: MyES6Array(3) []
複製代碼
負責收集並維護由全部聲明的標識符(變量、函數)組成的一系列查詢,並實施一套很是嚴格的規則,肯定當前執行的代碼對這些標識符的訪問權限。
L和R分別表明一個賦值操做的左側和右側,當變量出如今賦值操做的左側時進行LHS查詢,出如今賦值操做的非左側時進行RHS查詢。
LHS查詢(左側):找到變量的容器自己,而後對其賦值 RHS查詢(非左側):查找某個變量的值,能夠理解爲 retrieve his source value,即取到它的源值
ReferenceError和做用域判別失敗相關,TypeError表示做用域判別成功了,可是對結果的操做是非法或不合理的。
RHS查詢在做用域鏈中搜索不到所需的變量,引擎會拋出ReferenceError異常。 非嚴格模式下,LHS查詢在做用域鏈中搜索不到所需的變量,全局做用域中會建立一個具備該名稱的變量並返還給引擎。 嚴格模式下(ES5開始,禁止自動或隱式地建立全局變量),LHS查詢失敗會拋出ReferenceError異常 在RHS查詢成功狀況下,對變量進行不合理的操做,引擎會拋出TypeError異常。(好比對非函數類型的值進行函數調用,或者引用null或undefined類型的值中的屬性)
將內部函數傳遞到所在的詞法做用域之外,它都會持有對原始定義做用域的引用,不管在何處執行這個函數都會使用閉包。
app.use()——掛載中間件 在app.listen()方法中compose函數調用了中間件對其進行處理,以後就是this.handleRequest
存儲:以數組形式存儲中間件。 狀態管理:全部的狀態變動,都交給ctx對象,無需跨中間件傳遞參數。 流程控制:以遞歸的方式進行中間件的執行,將下一個中間件的執行權交給正在執行的中間件,即洋蔥圈模型。 異步方案:用Promise包裹中間件的返回結果,以支持在上一個中間件內部實現Await邏輯。
在 HTTP/1 中,每次請求都會創建一次TCP鏈接,也就是咱們常說的3次握手4次揮手,這在一次請求過程當中佔用了至關長的時間,即便開啓了 Keep-Alive ,解決了屢次鏈接的問題,可是依然有兩個效率上的問題:
第一個:串行的文件傳輸。當請求a文件時,b文件只能等待,等待a鏈接到服務器、服務器處理文件、服務器返回文件,這三個步驟。咱們假設這三步用時都是1秒,那麼a文件用時爲3秒,b文件傳輸完成用時爲6秒,依此類推。(注:此項計算有一個前提條件,就是瀏覽器和服務器是單通道傳輸) 第二個:鏈接數過多。咱們假設Apache設置了最大併發數爲300,由於瀏覽器限制,瀏覽器發起的最大請求數爲6(Chrome),也就是服務器能承載的最高併發爲50,當第51我的訪問時,就須要等待前面某個請求處理完成。
HTTP2採用二進制格式傳輸,取代了HTTP1.x的文本格式,二進制格式解析更高效。 多路複用代替了HTTP1.x的序列和阻塞機制,全部的相同域名請求都經過同一個TCP鏈接併發完成。在HTTP1.x中,併發多個請求須要多個TCP鏈接,瀏覽器爲了控制資源會有6-8個TCP鏈接都限制。 HTTP2中 同域名下全部通訊都在單個鏈接上完成,消除了因多個 TCP 鏈接而帶來的延時和內存消耗。 單個鏈接上能夠並行交錯的請求和響應,之間互不干擾
REST即Representational State Transfer的縮寫,可譯爲"表現層狀態轉化」。REST最大的幾個特色爲:資源、統一接口、URI和無狀態。
即在數據發送前將數據進行哈希或使用公鑰加密。若是數據被中間人獲取,拿到的則再也不是明文。 固然還有其餘一些優勢:例如避免後端等打印日誌直接暴露明文密碼,還能夠避免明文撞庫等.
瀏覽器會先檢查是否有相應的cookie,有則自動添加在request header中的cookie字段中。這些是瀏覽器自動幫咱們作的,並且每一次http請求瀏覽器都會自動幫咱們作。這個特色很重要,由於這關係到「什麼樣的數據適合存儲在cookie中」。 存儲在cookie中的數據,每次都會被瀏覽器自動放在http請求中,若是這些數據並非每一個請求都須要發給服務端的數據,瀏覽器這設置自動處理無疑增長了網絡開銷;但若是這些數據是每一個請求都須要發給服務端的數據(好比身份認證信息),瀏覽器這設置自動處理就大大免去了重複添加操做。因此對於那種設置「每次請求都要攜帶的信息(最典型的就是身份認證信息)」就特別適合放在cookie中,其餘類型的數據就不適合了。
不一樣的瀏覽器存放的cookie位置不同,也是不能通用的。 cookie的存儲是以域名形式進行區分的,不一樣的域下存儲的cookie是獨立的。 咱們能夠設置cookie生效的域(當前設置cookie所在域的子域),也就是說,咱們可以操做的cookie是當前域以及當前域下的全部子域 一個域名下存放的cookie的個數是有限制的,不一樣的瀏覽器存放的個數不同,通常爲20個。 每一個cookie存放的內容大小也是有限制的,不一樣的瀏覽器存放大小不同,通常爲4KB。 cookie也能夠設置過時的時間,默認是會話結束的時候,當時間到期自動銷燬。