摘要:打開一個(gè)網(wǎng)頁,看到服務(wù)器返回給客戶端瀏覽器的各種文件類型圖片構(gòu)建瀏覽器會(huì)遵守一套步驟將文件轉(zhuǎn)換為樹。因?yàn)闉g覽器有渲染線程與引擎線程,為了防止渲染出現(xiàn)不可預(yù)期的結(jié)果,這兩個(gè)線程是互斥的關(guān)系。
1. 瀏覽器架構(gòu)
用戶界面
主進(jìn)程
內(nèi)核
渲染引擎
JS 引擎
執(zhí)行棧
事件觸發(fā)線程
消息隊(duì)列
微任務(wù)
宏任務(wù)
網(wǎng)絡(luò)異步線程
定時(shí)器線程
2. 從輸入 url 到頁面展示的過程 2.1 流程跳轉(zhuǎn)
是否有緩存
DNS查找,域名解析ip
創(chuàng)建TCP鏈接,之后才有HTTP三次握手(HTTP尋在TCP之上)
發(fā)送請(qǐng)求(Request)
接收響應(yīng)(Response),返回請(qǐng)求的文件 (html)
瀏覽器渲染(1,2并行,后面是串行)
解析HTML --> DOM ree
標(biāo)記化算法,進(jìn)行元素狀態(tài)的標(biāo)記
生成DOM
解析CSS --> CSS tree
生成CSSOM
結(jié)合 --> Render tree
結(jié)合DOM與CSSOM,生成渲染樹(Render tree)
layout: 布局(布局渲染樹)
painting: 繪制(繪制渲染樹)
2.2 參考https://juejin.im/post/5c64d1...
https://juejin.im/book/5b9365...
Cookie 的本職工作并非本地存儲(chǔ),而是“維持狀態(tài)”。
因?yàn)镠TTP協(xié)議是無狀態(tài)的,HTTP協(xié)議自身不對(duì)請(qǐng)求和響應(yīng)之間的通信狀態(tài)進(jìn)行保存,通俗來說,服務(wù)器不知道用戶上一次做了什么,這嚴(yán)重阻礙了交互式Web應(yīng)用程序的實(shí)現(xiàn)。
在典型的網(wǎng)上購(gòu)物場(chǎng)景中,用戶瀏覽了幾個(gè)頁面,買了一盒餅干和兩瓶飲料。最后結(jié)帳時(shí),由于HTTP的無狀態(tài)性,不通過額外的手段,服務(wù)器并不知道用戶到底買了什么,于是就誕生了Cookie
我們可以把Cookie 理解為一個(gè)存儲(chǔ)在瀏覽器里的一個(gè)小小的文本文件,它附著在 HTTP 請(qǐng)求上,在瀏覽器和服務(wù)器之間“飛來飛去”。它可以攜帶用戶信息,當(dāng)服務(wù)器檢查 Cookie 的時(shí)候,便可以獲取到客戶端的狀態(tài)
在剛才的購(gòu)物場(chǎng)景中,當(dāng)用戶選購(gòu)了第一項(xiàng)商品,服務(wù)器在向用戶發(fā)送網(wǎng)頁的同時(shí),還發(fā)送了一段Cookie,記錄著那項(xiàng)商品的信息。當(dāng)用戶訪問另一個(gè)頁面,瀏覽器會(huì)把Cookie發(fā)送給服務(wù)器,于是服務(wù)器知道他之前選購(gòu)了什么。用戶繼續(xù)選購(gòu)飲料,服務(wù)器就在原來那段Cookie里追加新的商品信息。結(jié)帳時(shí),服務(wù)器讀取發(fā)送來的Cookie就行了。
3.1.2 什么是cookieCookie指某些網(wǎng)站為了辨別用戶身份而儲(chǔ)存在用戶本地終端上的數(shù)據(jù)(通常經(jīng)過加密)。 cookie是服務(wù)端生成,客戶端進(jìn)行維護(hù)和存儲(chǔ)
通過cookie,可以讓服務(wù)器知道請(qǐng)求是來源哪個(gè)客戶端,就可以進(jìn)行客戶端狀態(tài)的維護(hù),比如登陸后刷新,請(qǐng)求頭就會(huì)攜帶登陸時(shí)response header中的set-cookie,Web服務(wù)器接到請(qǐng)求時(shí)也能讀出cookie的值,根據(jù)cookie值的內(nèi)容就可以判斷和恢復(fù)一些用戶的信息狀態(tài)。
記住密碼,下次自動(dòng)登錄。
購(gòu)物車功能
記錄用戶瀏覽數(shù)據(jù),進(jìn)行商品(廣告)推薦。
3.1.4 Cookie的原理第一次訪問網(wǎng)站的時(shí)候,瀏覽器發(fā)出請(qǐng)求,服務(wù)器響應(yīng)請(qǐng)求后,會(huì)在響應(yīng)頭里面添加一個(gè)Set-Cookie選項(xiàng),將cookie放入到響應(yīng)請(qǐng)求中,在瀏覽器第二次發(fā)請(qǐng)求的時(shí)候,會(huì)通過Cookie請(qǐng)求頭部將Cookie信息發(fā)送給服務(wù)器,服務(wù)端會(huì)辨別用戶身份,另外,Cookie的過期時(shí)間、域、路徑、有效期、適用站點(diǎn)都可以根據(jù)需要來指定。
3.1.5 Cookie生成方式服務(wù)端
http response header中的set-cookie
客戶端
js中可以通過document.cookie可以讀寫cookie
document.cookie="userName=hello"3.1.6 Cookie的缺陷
Cookie 不夠大
各瀏覽器的cookie每一個(gè)name=value的value值大概在4k,所以4k并不是一個(gè)域名下所有的cookie共享的,而是一個(gè)name的大小。
過多的 Cookie 會(huì)帶來巨大的性能浪費(fèi)
Cookie 是緊跟域名的。同一個(gè)域名下的所有請(qǐng)求,都會(huì)攜帶 Cookie
由于在HTTP請(qǐng)求中的Cookie是明文傳遞的,所以安全性成問題,除非用HTTPS。
3.1.7 Cookie與安全屬性 | 作用 |
---|---|
value | 如果用于保存用戶登錄狀態(tài),應(yīng)該將該值加密,不能使用明文的用戶標(biāo)識(shí)(例如可以使用md5加密) |
http-only | 不能通過js訪問cookie,減少xss攻擊 |
secure | 只能在協(xié)議為https的請(qǐng)求中攜帶 |
same-site | 規(guī)定瀏覽器不能在跨域的請(qǐng)求中攜帶cookie,減少csrf攻擊 |
保存的數(shù)據(jù)長(zhǎng)期存在,下一次訪問該網(wǎng)站的時(shí)候,網(wǎng)頁可以直接讀取以前保存的數(shù)據(jù)。
大小為5M左右
僅在客戶端使用,不和服務(wù)端進(jìn)行通信
LocalStorage可以作為瀏覽器本地緩存方案,用來提升網(wǎng)頁首屏渲染速度(根據(jù)第一請(qǐng)求返回時(shí),將一些不變信息直接存儲(chǔ)在本地)。
3.2.2存入/讀取數(shù)據(jù)localStorage.setItem("key","value"); var valueLocal = localStorage.getItem("key");3.3sessionStorage 3.3.1 sessionStorage的特點(diǎn)
會(huì)話級(jí)別的瀏覽器存儲(chǔ)
大小為5M左右
僅在客戶端使用,不和服務(wù)端進(jìn)行通信
3.3.2 窗口共享即便是相同域名下的兩個(gè)頁面,只要它們不在同一個(gè)瀏覽器窗口(瀏覽器的標(biāo)簽頁)中打開,那么它們的 sessionStorage 內(nèi)容便無法共享
localStorage 在所有同源窗口中都是共享的
cookie也是在所有同源窗口中都是共享的
3.3.3存入/讀取數(shù)據(jù)localStorage.setItem("key","value"); var valueLocal = localStorage.getItem("key");3.4 總結(jié)
特性 | cookie | localStorage | sessionStorage | indexDB |
---|---|---|---|---|
數(shù)據(jù)生命周期 | 一般由服務(wù)器生成,可以設(shè)置過期時(shí)間 | 除非被手動(dòng)清理,否則一直存在 | 頁面關(guān)閉就清理 | 除非被手動(dòng)清理,否則一直存在 |
數(shù)據(jù)存儲(chǔ)大小 | 4k | 5M | 5M | 無限 |
與服務(wù)端通信 | 每次都會(huì)攜帶在header中,對(duì)于請(qǐng)求有性能影響 | 不參與 | 不參與 | 不參與 |
cookie 已經(jīng)不建議用于存儲(chǔ)。如果沒有大量數(shù)據(jù)存儲(chǔ)需求的話,可以使用 localStorage 和 sessionStorage 。對(duì)于不怎么改變的數(shù)據(jù)盡量使用 localStorage 存儲(chǔ),否則可以用 sessionStorage 存儲(chǔ)。
Cookie 的本職工作并非本地存儲(chǔ),而是“維持狀態(tài)”
Web Storage 是 HTML5 專門為瀏覽器存儲(chǔ)而提供的數(shù)據(jù)存儲(chǔ)機(jī)制,不與服務(wù)端發(fā)生通信
IndexedDB 用于客戶端存儲(chǔ)大量結(jié)構(gòu)化數(shù)據(jù)
3.5 參考https://github.com/ljianshu/B...
4. 瀏覽器渲染原理 4.1 什么是渲染過程簡(jiǎn)單來說,渲染引擎根據(jù) HTML 文件描述構(gòu)建相應(yīng)的數(shù)學(xué)模型,調(diào)用瀏覽器各個(gè)零部件,從而將網(wǎng)頁資源代碼轉(zhuǎn)換為圖像結(jié)果,這個(gè)過程就是渲染過程(如下圖)
從這個(gè)流程來看,瀏覽器呈現(xiàn)網(wǎng)頁這個(gè)過程,宛如一個(gè)黑盒。在這個(gè)神秘的黑盒中,有許多功能模塊,內(nèi)核內(nèi)部的實(shí)現(xiàn)正是這些功能模塊相互配合協(xié)同工作進(jìn)行的。其中我們最需要關(guān)注的,就是HTML 解釋器、CSS 解釋器、圖層布局計(jì)算模塊、視圖繪制模塊與JavaScript 引擎這幾大模塊:
HTML 解釋器:將 HTML 文檔經(jīng)過詞法分析輸出 DOM 樹。
CSS 解釋器:解析 CSS 文檔, 生成樣式規(guī)則。
圖層布局計(jì)算模塊:布局計(jì)算每個(gè)對(duì)象的精確位置和大小。
視圖繪制模塊:進(jìn)行具體節(jié)點(diǎn)的圖像繪制,將像素渲染到屏幕上。
JavaScript引擎:編譯執(zhí)行 Javascript 代碼。
打開一個(gè)網(wǎng)頁,看到服務(wù)器返回給客戶端(瀏覽器)的各種文件類型
document --> html
stylesheet --> css
script --> js
jpeg --> 圖片
4.2 構(gòu)建DOM瀏覽器會(huì)遵守一套步驟將HTML 文件轉(zhuǎn)換為 DOM 樹。宏觀上,可以分為幾個(gè)步驟
瀏覽器從磁盤或網(wǎng)絡(luò)讀取HTML的原始字節(jié),并根據(jù)文件的指定編碼(例如 UTF-8)將它們轉(zhuǎn)換成字符串。
在網(wǎng)絡(luò)中傳輸?shù)膬?nèi)容其實(shí)都是 0 和 1 這些字節(jié)數(shù)據(jù)。當(dāng)瀏覽器接收到這些字節(jié)數(shù)據(jù)以后,它會(huì)將這些字節(jié)數(shù)據(jù)轉(zhuǎn)換為字符串,也就是我們寫的代碼。
將字符串轉(zhuǎn)換成Token,例如:、
等。Token中會(huì)標(biāo)識(shí)出當(dāng)前Token是“開始標(biāo)簽”或是“結(jié)束標(biāo)簽”亦或是“文本”等信息。
上圖給出了節(jié)點(diǎn)之間的關(guān)系,例如:“Hello”Token位于“title”開始標(biāo)簽與“title”結(jié)束標(biāo)簽之間,表明“Hello”Token是“title”Token的子節(jié)點(diǎn)。同理“title”Token是“head”Token的子節(jié)點(diǎn)。
生成節(jié)點(diǎn)對(duì)象并構(gòu)建DOM
事實(shí)上,構(gòu)建DOM的過程中,不是等所有Token都轉(zhuǎn)換完成后再去生成節(jié)點(diǎn)對(duì)象,而是一邊生成Token一邊消耗Token來生成節(jié)點(diǎn)對(duì)象。換句話說,每個(gè)Token被生成后,會(huì)立刻消耗這個(gè)Token創(chuàng)建出節(jié)點(diǎn)對(duì)象。注意:帶有結(jié)束標(biāo)簽標(biāo)識(shí)的Token不會(huì)創(chuàng)建節(jié)點(diǎn)對(duì)象。
實(shí)例
Web page parsing Web page parsing
This is an example Web page.
上面這段HTML會(huì)解析成這樣
4.3 構(gòu)建CSSOMDOM會(huì)捕獲頁面的內(nèi)容,但瀏覽器還需要知道頁面如何展示,所以需要構(gòu)建CSSOM
構(gòu)建CSSOM的過程與構(gòu)建DOM的過程非常相似,當(dāng)瀏覽器接收到一段CSS,瀏覽器首先要做的是識(shí)別出Token,然后構(gòu)建節(jié)點(diǎn)并生成CSSOM。
在這一過程中,瀏覽器會(huì)確定下每一個(gè)節(jié)點(diǎn)的樣式到底是什么,并且這一過程其實(shí)是很消耗資源的。因?yàn)闃邮侥憧梢宰孕性O(shè)置給某個(gè)節(jié)點(diǎn),也可以通過繼承獲得。在這一過程中,瀏覽器得遞歸 CSSOM 樹,然后確定具體的元素到底是什么樣式
注意:CSS匹配HTML元素是一個(gè)相當(dāng)復(fù)雜和有性能問題的事情。所以,DOM樹要小,CSS盡量用id和class,千萬不要過渡層疊下去。
4.3 構(gòu)建渲染樹當(dāng)我們生成 DOM 樹和 CSSOM 樹以后,就需要將這兩棵樹組合為渲染樹
在這一過程中,不是簡(jiǎn)單的將兩者合并就行了。渲染樹只會(huì)包括需要顯示的節(jié)點(diǎn)和這些節(jié)點(diǎn)的樣式信息,如果某個(gè)節(jié)點(diǎn)是 display: none 的,那么就不會(huì)在渲染樹中顯示。
瀏覽器如果渲染過程中遇到JS文件怎么處理
渲染過程中,如果遇到
4.4 布局與繪制當(dāng)瀏覽器生成渲染樹以后,就會(huì)根據(jù)渲染樹來進(jìn)行布局(也可以叫做回流)。這一階段瀏覽器要做的事情是要弄清楚各個(gè)節(jié)點(diǎn)在頁面中的確切位置和大小。通常這一行為也被稱為“自動(dòng)重排”。
布局流程的輸出是一個(gè)“盒模型”,它會(huì)精確地捕獲每個(gè)元素在視口內(nèi)的確切位置和尺寸,所有相對(duì)測(cè)量值都將轉(zhuǎn)換為屏幕上的絕對(duì)像素。
布局完成后,瀏覽器會(huì)立即發(fā)出“Paint Setup”和“Paint”事件,將渲染樹轉(zhuǎn)換成屏幕上的像素。
4.5 async和defer的作用是什么?有什么區(qū)別?async 異步
defer 延緩
其中藍(lán)色線代表JavaScript加載;紅色線代表JavaScript執(zhí)行;綠色線代表 HTML 解析。
情況1
沒有 defer 或 async,瀏覽器會(huì)立即加載并執(zhí)行指定的腳本,也就是說不等待后續(xù)載入的文檔元素,讀到就加載并執(zhí)行。
情況2 (異步下載)
async 屬性表示異步執(zhí)行引入的 JavaScript,與 defer 的區(qū)別在于,如果已經(jīng)加載好(符合異步),就會(huì)開始執(zhí)行——無論此刻是 HTML 解析階段還是 DOMContentLoaded 觸發(fā)之后。需要注意的是,這種方式加載的 JavaScript 依然會(huì)阻塞 load 事件。換句話說,async-script 可能在 DOMContentLoaded 觸發(fā)之前或之后執(zhí)行,但一定在 load 觸發(fā)之前執(zhí)行
情況3 (延遲執(zhí)行)
defer 屬性表示延遲執(zhí)行引入的 JavaScript,即這段 JavaScript 加載時(shí) HTML 并未停止解析,這兩個(gè)過程是并行的。整個(gè) document 解析完畢且 defer-script 也加載完成之后(這兩件事情的順序無關(guān)),會(huì)執(zhí)行所有由 defer-script 加載的 JavaScript 代碼,然后觸發(fā) DOMContentLoaded 事件。
defer 與相比普通 script,有兩點(diǎn)區(qū)別:載入 JavaScript 文件時(shí)不阻塞 HTML 的解析,執(zhí)行階段被放到 HTML 標(biāo)簽解析完成之后。
在加載多個(gè)JS腳本的時(shí)候,async是無順序的加載,而defer是有順序的加載。
4.6 為什么操作 DOM 慢把 DOM 和 JavaScript 各自想象成一個(gè)島嶼,它們之間用收費(fèi)橋梁連接?!陡咝阅?JavaScript》
JS 是很快的,在 JS 中修改 DOM 對(duì)象也是很快的。在JS的世界里,一切是簡(jiǎn)單的、迅速的。但 DOM 操作并非 JS 一個(gè)人的獨(dú)舞,而是兩個(gè)模塊之間的協(xié)作。
因?yàn)?DOM 是屬于渲染引擎中的東西,而 JS 又是 JS 引擎中的東西。當(dāng)我們用 JS 去操作 DOM 時(shí),本質(zhì)上是 JS 引擎和渲染引擎之間進(jìn)行了“跨界交流”。這個(gè)“跨界交流”的實(shí)現(xiàn)并不簡(jiǎn)單,它依賴了橋接接口作為“橋梁”(如下圖)。
過“橋”要收費(fèi)——這個(gè)開銷本身就是不可忽略的。我們每操作一次 DOM(不管是為了修改還是僅僅為了訪問其值),都要過一次“橋”。過“橋”的次數(shù)一多,就會(huì)產(chǎn)生比較明顯的性能問題。因此“減少 DOM 操作”的建議,并非空穴來風(fēng)。
4.7 總結(jié)瀏覽器工作流程:構(gòu)建DOM -> 構(gòu)建CSSOM -> 構(gòu)建渲染樹 -> 布局 -> 繪制。
CSSOM會(huì)阻塞渲染,只有當(dāng)CSSOM構(gòu)建完畢后才會(huì)進(jìn)入下一個(gè)階段構(gòu)建渲染樹。
通常情況下DOM和CSSOM是并行構(gòu)建的,但是當(dāng)瀏覽器遇到一個(gè)不帶defer或async屬性的script標(biāo)簽時(shí),DOM構(gòu)建將暫停,如果此時(shí)又恰巧瀏覽器尚未完成CSSOM的下載和構(gòu)建,由于JavaScript可以修改CSSOM,所以需要等CSSOM構(gòu)建完畢后再執(zhí)行JS,最后才重新DOM構(gòu)建。
4.8 參考深入淺出瀏覽器渲染原理
5. 重繪與回流(重排) 5.1 名詞解析通俗理解
重繪: 重新繪制,繪制色彩
回流(重排): 重新排版,排版位置大小
比較官方的理解
重繪:當(dāng)我們對(duì) DOM 的修改導(dǎo)致了樣式的變化、卻并未影響其幾何屬性(比如修改了顏色或背景色)時(shí),瀏覽器不需重新計(jì)算元素的幾何屬性、直接為該元素繪制新的樣式(跳過了上圖所示的回流環(huán)節(jié))。
回流: 當(dāng)我們對(duì) DOM 的修改引發(fā)了 DOM 幾何尺寸的變化(比如修改元素的寬、高或隱藏元素等)時(shí),瀏覽器需要重新計(jì)算元素的幾何屬性(其他元素的幾何屬性和位置也會(huì)因此受到影響),然后再將計(jì)算的結(jié)果繪制出來。這個(gè)過程就是回流(也叫重排)
5.2 瀏覽器渲染的流程中的回流與重繪計(jì)算CSS樣式
構(gòu)建RenderTree
Layout(布局) –-> 定位坐標(biāo)和大小
paint(繪制)-->正式開畫
注意:上圖流程中有很多連接線,這表示了Javascript動(dòng)態(tài)修改了DOM屬性或是CSS屬性會(huì)導(dǎo)致重新Layout,但有些改變不會(huì)重新Layout,就是上圖中那些指到天上的箭頭,比如修改后的CSS rule沒有被匹配到元素。
我們知道,當(dāng)網(wǎng)頁生成的時(shí)候,至少會(huì)渲染一次。在用戶訪問的過程中,還會(huì)不斷重新渲染。重新渲染會(huì)重復(fù)回流+重繪或者只有重繪
回流必定會(huì)發(fā)生重繪,重繪不一定會(huì)引發(fā)回流。重繪和回流會(huì)在我們?cè)O(shè)置節(jié)點(diǎn)樣式時(shí)頻繁出現(xiàn),同時(shí)也會(huì)很大程度上影響性能?;亓魉璧某杀颈戎乩L高的多,改變父節(jié)點(diǎn)里的子節(jié)點(diǎn)很可能會(huì)導(dǎo)致父節(jié)點(diǎn)的一系列回流。
5.3 常見引起回流屬性和方法任何會(huì)改變?cè)貛缀涡畔?元素的位置和尺寸大小)的操作,都會(huì)觸發(fā)回流,
添加或者刪除可見的DOM元素;
元素尺寸改變——邊距、填充、邊框、寬度和高度
內(nèi)容變化,比如用戶在input框中輸入文字
瀏覽器窗口尺寸改變——resize事件發(fā)生時(shí)
計(jì)算 offsetWidth 和 offsetHeight 屬性
設(shè)置 style 屬性的值
5.4 常見引起重繪屬性和方法文字屬性
邊框樣式(非大?。?/p>
色彩
背景色
陰影
5.5如何減少回流、重繪使用 transform 替代 top
使用 visibility 替換 display: none ,因?yàn)榍罢咧粫?huì)引起重繪,后者會(huì)引發(fā)回流(改變了布局)
不要把節(jié)點(diǎn)的屬性值放在一個(gè)循環(huán)里當(dāng)成循環(huán)里的變量。
for(let i = 0; i < 1000; i++) { // 獲取 offsetTop 會(huì)導(dǎo)致回流,因?yàn)樾枰カ@取正確的值 console.log(document.querySelector(".test").style.offsetTop) }
不要使用 table 布局,可能很小的一個(gè)小改動(dòng)會(huì)造成整個(gè) table 的重新布局
動(dòng)畫實(shí)現(xiàn)的速度的選擇,動(dòng)畫速度越快,回流次數(shù)越多,也可以選擇使用 requestAnimationFrame
CSS 選擇符從右往左匹配查找,避免節(jié)點(diǎn)層級(jí)過多
將頻繁重繪或者回流的節(jié)點(diǎn)設(shè)置為圖層,圖層能夠阻止該節(jié)點(diǎn)的渲染行為影響別的節(jié)點(diǎn)。比如對(duì)于 video 標(biāo)簽來說,瀏覽器會(huì)自動(dòng)將該節(jié)點(diǎn)變?yōu)閳D層。
5.6 參考https://github.com/ljianshu/B...
6. 瀏覽器下的Event Loop 6.1 線程與進(jìn)程 6.1.1概念我們經(jīng)常說JS 是單線程執(zhí)行的,指的是一個(gè)進(jìn)程里只有一個(gè)主線程,那到底什么是線程?什么是進(jìn)程?
官方的說法是
進(jìn)程: CPU資源分配的最小單位
線程: CPU調(diào)度的最小單位
進(jìn)程好比圖中的工廠,有多帶帶的專屬自己的工廠資源。
線程好比圖中的工人,多個(gè)工人在一個(gè)工廠中協(xié)作工作,工廠與工人是 1:n的關(guān)系。也就是說一個(gè)進(jìn)程由一個(gè)或多個(gè)線程組成,線程是一個(gè)進(jìn)程中代碼的不同執(zhí)行路線;
廠的空間是工人們共享的,這象征一個(gè)進(jìn)程的內(nèi)存空間是共享的,每個(gè)線程都可用這些共享內(nèi)存。
多個(gè)工廠之間獨(dú)立存在
6.1.2 多進(jìn)程與多線程多進(jìn)程:在同一個(gè)時(shí)間里,同一個(gè)計(jì)算機(jī)系統(tǒng)中如果允許兩個(gè)或兩個(gè)以上的進(jìn)程處于運(yùn)行狀態(tài)。多進(jìn)程帶來的好處是明顯的,比如你可以聽歌的同時(shí),打開編輯器敲代碼,編輯器和聽歌軟件的進(jìn)程之間絲毫不會(huì)相互干擾。
多線程:程序中包含多個(gè)執(zhí)行流,即在一個(gè)程序中可以同時(shí)運(yùn)行多個(gè)不同的線程來執(zhí)行不同的任務(wù),也就是說允許單個(gè)程序創(chuàng)建多個(gè)并行執(zhí)行的線程來完成各自的任務(wù)。
以Chrome瀏覽器中為例,當(dāng)你打開一個(gè) Tab 頁時(shí),其實(shí)就是創(chuàng)建了一個(gè)進(jìn)程,一個(gè)進(jìn)程中可以有多個(gè)線程(下文會(huì)詳細(xì)介紹),比如渲染線程、JS 引擎線程、HTTP 請(qǐng)求線程等等。當(dāng)你發(fā)起一個(gè)請(qǐng)求時(shí),其實(shí)就是創(chuàng)建了一個(gè)線程,當(dāng)請(qǐng)求結(jié)束后,該線程可能就會(huì)被銷毀。
6.1.3js單線程存在的問題js是單線程的,處理任務(wù)是一件接著一件處理,所以如果一個(gè)任務(wù)需要處理很久的話,后面的任務(wù)就會(huì)被阻塞
所以js通過Event Loop事件循環(huán)的方式解決了這個(gè)問題
簡(jiǎn)單來說瀏覽器內(nèi)核是通過取得頁面內(nèi)容、整理信息(應(yīng)用CSS)、計(jì)算和組合最終輸出可視化的圖像結(jié)果,通常也被稱為渲染引擎。
瀏覽器內(nèi)核是多線程,在內(nèi)核控制下各線程相互配合以保持同步,一個(gè)瀏覽器通常由以下常駐線程組成:
GUI 渲染線程
JavaScript引擎線程
定時(shí)觸發(fā)器線程
事件觸發(fā)線程
異步http請(qǐng)求線程
6.2.2GUI渲染線程主要負(fù)責(zé)頁面的渲染,解析HTML、CSS,構(gòu)建DOM樹,布局和繪制等。
當(dāng)界面需要重繪或者由于某種操作引發(fā)回流時(shí),將執(zhí)行該線程。
該線程與JS引擎線程互斥,當(dāng)執(zhí)行JS引擎線程時(shí),GUI渲染會(huì)被掛起,當(dāng)任務(wù)隊(duì)列空閑時(shí),主線程才會(huì)去執(zhí)行GUI渲染。
6.2.3 JS引擎線程該線程當(dāng)然是主要負(fù)責(zé)處理 JavaScript腳本,執(zhí)行代碼。
也是主要負(fù)責(zé)執(zhí)行準(zhǔn)備好待執(zhí)行的事件,即定時(shí)器計(jì)數(shù)結(jié)束,或者異步請(qǐng)求成功并正確返回時(shí),將依次進(jìn)入任務(wù)隊(duì)列,等待 JS引擎線程的執(zhí)行。
當(dāng)然,該線程與 GUI渲染線程互斥,當(dāng) JS引擎線程執(zhí)行 JavaScript腳本時(shí)間過長(zhǎng),將導(dǎo)致頁面渲染的阻塞。
6.2.4 定時(shí)器觸發(fā)線程負(fù)責(zé)執(zhí)行異步定時(shí)器一類的函數(shù)的線程,如: setTimeout,setInterval。
主線程依次執(zhí)行代碼時(shí),遇到定時(shí)器,會(huì)將定時(shí)器交給該線程處理,當(dāng)計(jì)數(shù)完畢后,事件觸發(fā)線程會(huì)將計(jì)數(shù)完畢后的事件加入到任務(wù)隊(duì)列的尾部,等待JS引擎線程執(zhí)行。
6.2.5 事件觸發(fā)線程主要負(fù)責(zé)將準(zhǔn)備好的事件交給 JS引擎線程執(zhí)行
比如 setTimeout定時(shí)器計(jì)數(shù)結(jié)束, ajax等異步請(qǐng)求成功并觸發(fā)回調(diào)函數(shù),或者用戶觸發(fā)點(diǎn)擊事件時(shí),該線程會(huì)將整裝待發(fā)的事件依次加入到任務(wù)隊(duì)列的隊(duì)尾,等待 JS引擎線程的執(zhí)行。
負(fù)責(zé)執(zhí)行異步請(qǐng)求一類的函數(shù)的線程,如: Promise,axios,ajax等。
主線程依次執(zhí)行代碼時(shí),遇到異步請(qǐng)求,會(huì)將函數(shù)交給該線程處理,當(dāng)監(jiān)聽到狀態(tài)碼變更,如果有回調(diào)函數(shù),事件觸發(fā)線程會(huì)將回調(diào)函數(shù)加入到任務(wù)隊(duì)列的尾部,等待JS引擎線程執(zhí)行。
6.3 event loop 6.3.1stack,queue,heapstack(棧),先進(jìn)后出
queue(隊(duì)列),先進(jìn)先出,生活中的排隊(duì)
heap(堆):存儲(chǔ)obj對(duì)象
6.3.2 執(zhí)行棧js引擎運(yùn)行時(shí),當(dāng)代碼開始運(yùn)行的時(shí)候,會(huì)將代碼,壓入執(zhí)行棧進(jìn)行執(zhí)行
實(shí)例
當(dāng)代碼被解析后,函數(shù)會(huì)依次被壓入到棧中
有入棧,就要有出棧,當(dāng)函數(shù)c執(zhí)行完,開始出棧
前面執(zhí)行棧,先入后出,但其實(shí)也是同步的,同步就意味著會(huì)阻塞,所以需要異步,那當(dāng)執(zhí)行棧中出現(xiàn)異步代碼會(huì)怎么樣
當(dāng)瀏覽器在執(zhí)行棧執(zhí)行的時(shí)候,發(fā)現(xiàn)有異步任務(wù)之后,會(huì)交給webapi去維護(hù),而執(zhí)行棧則繼續(xù)執(zhí)行后面的任務(wù)
同樣,setTimeout同樣會(huì)被添加到webapi中
webapi是瀏覽器自己實(shí)現(xiàn)的功能,這里專門維護(hù)事件。
上面setTimeout旁邊有個(gè)進(jìn)度條,這個(gè)進(jìn)度就是設(shè)置的等待時(shí)間
6.3.4 回調(diào)隊(duì)列callback queue當(dāng)setTimeout執(zhí)行結(jié)束的時(shí)候,是不是就應(yīng)該回到執(zhí)行棧,進(jìn)行執(zhí)行輸出呢?
答案:并不是!
此時(shí),倒計(jì)時(shí)結(jié)束后的setTimeout的可執(zhí)行函數(shù),被放入了回調(diào)隊(duì)列
最后,setTimeout的可執(zhí)行函數(shù),被從回調(diào)隊(duì)列中取出,再次放入了執(zhí)行棧
這樣的執(zhí)行過程就叫 event loop事件循環(huán)
6.4 Event Loop的具體流程 6.4.1 執(zhí)行棧任務(wù)清空后,才會(huì)從回調(diào)隊(duì)列頭部取出一個(gè)任務(wù)console.log(1)被壓入執(zhí)行棧
setTimeout在執(zhí)行棧被識(shí)別為異步任務(wù),放入webapis中
console.log(3)被壓入執(zhí)行棧,此時(shí)setTimeout倒計(jì)時(shí)結(jié)束后,把可執(zhí)行代碼console.log(2)放入回調(diào)隊(duì)列里等待
console.log(3)執(zhí)行完成后,從回調(diào)隊(duì)列頭部取出console.log(2),放入執(zhí)行棧
console.log(2)執(zhí)行
6.4.2 回調(diào)隊(duì)列先進(jìn)先出當(dāng)console.log(4)執(zhí)行完成后,從回調(diào)隊(duì)列里取出了console.log(2);
只有console.log(2)執(zhí)行完成,執(zhí)行棧再次清空時(shí),才會(huì)從回調(diào)隊(duì)列取出console.log(3)一個(gè)一個(gè)拿,先拿console.log(2),執(zhí)行完后,再拿console.log(3),執(zhí)行
6.4.3 把代碼從回調(diào)隊(duì)列拿到棧中執(zhí)行,發(fā)現(xiàn)在這段代碼中有異步輸出1,將2push進(jìn)回調(diào)隊(duì)列
將4push進(jìn)回調(diào)隊(duì)列
輸出5
清空了執(zhí)行棧,讀取輸出2(從回調(diào)隊(duì)列中放到棧中執(zhí)行),發(fā)現(xiàn)有3(異步),將3push進(jìn)回調(diào)隊(duì)列
清空了執(zhí)行棧,讀取輸出4
清空了執(zhí)行棧,讀取輸出3
5.4 Macrotask(宏任務(wù))、Microtask(微任務(wù)) 5.4.1 什么是宏任務(wù),微任務(wù)宏任務(wù): setTimeout、setInterval、script(整體代碼)、 I/O 操作、UI 渲染等
微任務(wù): new Promise().then(回調(diào))、MutationObserver(html5新特性)、process.nextTick、Object.observe 等。
Microtask(微任務(wù))同樣是一個(gè)任務(wù)隊(duì)列,這個(gè)隊(duì)列的執(zhí)行順序是在清空?qǐng)?zhí)行棧之后
可以看到Macrotask(宏任務(wù))也就是回調(diào)隊(duì)列上面還有一個(gè)Microtask(微任務(wù))
Microtask(微任務(wù))雖然是隊(duì)列,但并不是一個(gè)一個(gè)放入執(zhí)行棧,而是當(dāng)執(zhí)行棧請(qǐng)空,會(huì)執(zhí)行全部Microtask(微任務(wù))隊(duì)列中的任務(wù),最后才是取回調(diào)隊(duì)列的第一個(gè)Macrotask(宏任務(wù))
將setTimeout給push進(jìn)宏任務(wù)
將then(2)push進(jìn)微任務(wù)
將then(4)push進(jìn)微任務(wù)
任務(wù)隊(duì)列為空,取出微任務(wù)第一個(gè)then(2)壓入執(zhí)行棧
輸出2,將then(3)push進(jìn)微任務(wù)
任務(wù)隊(duì)列為空,取出微任務(wù)第一個(gè)then(4)壓入執(zhí)行棧
輸出4
任務(wù)隊(duì)列為空,取出微任務(wù)第一個(gè)then(3)壓入執(zhí)行棧
輸出3
任務(wù)隊(duì)列為空,微任務(wù)也為空,取出宏任務(wù)中的setTimeout(1)
輸出1
實(shí)例
console.log("1"); setTimeout(()=>{ console.log(2) Promise.resolve().then(()=>{ console.log(3); process.nextTick(function foo() { console.log(4); }); }) }) Promise.resolve().then(()=>{ console.log(5); setTimeout(()=>{ console.log(6) }) Promise.resolve().then(()=>{ console.log(7); }) }) process.nextTick(function foo() { console.log(8); process.nextTick(function foo() { console.log(9); }); }); console.log("10")
1,輸出1
2,將setTimeout(2)push進(jìn)宏任務(wù)
3,將then(5)push進(jìn)微任務(wù)
4,在執(zhí)行棧底部添加nextTick(8)
5,輸出10
6,執(zhí)行nextTick(8)
7,輸出8
8,在執(zhí)行棧底部添加nextTick(9)
9,輸出9
10,執(zhí)行微任務(wù)then(5)
11,輸出5
12,將setTimeout(6)push進(jìn)宏任務(wù)
13,將then(7)push進(jìn)微任務(wù)
14,執(zhí)行微任務(wù)then(7)
15,輸出7
16,取出setTimeout(2)
17,輸出2
18,將then(3)push進(jìn)微任務(wù)
19,執(zhí)行微任務(wù)then(3)
20,輸出3
21,在執(zhí)行棧底部添加nextTick(4)
22,輸出4
23,取出setTimeout(6)
24,輸出6
https://juejin.im/post/5a6309...
https://github.com/ljianshu/B...
程序的運(yùn)行需要內(nèi)存。只要程序提出要求,操作系統(tǒng)或者運(yùn)行時(shí)就必須供給內(nèi)存
所謂的內(nèi)存泄漏簡(jiǎn)單來說是不再用到的內(nèi)存,沒有及時(shí)釋放
Javascript具有自動(dòng)垃圾回收機(jī)制(Garbage Collecation)。
由于字符串、對(duì)象和數(shù)組沒有固定大小,所有當(dāng)他們的大小已知時(shí),才能對(duì)他們進(jìn)行動(dòng)態(tài)的存儲(chǔ)分配。JavaScript程序每次創(chuàng)建字符串、數(shù)組或?qū)ο髸r(shí),解釋器都必須分配內(nèi)存來存儲(chǔ)那個(gè)實(shí)體。只要像這樣動(dòng)態(tài)地分配了內(nèi)存,最終都要釋放這些內(nèi)存以便他們能夠被再用,否則,JavaScript的解釋器將會(huì)消耗完系統(tǒng)中所有可用的內(nèi)存,造成系統(tǒng)崩潰。
最簡(jiǎn)單的垃圾回收
JavaScript垃圾回收的機(jī)制很簡(jiǎn)單:找出不再使用的變量,然后釋放掉其占用的內(nèi)存,但是這個(gè)過程不是時(shí)時(shí)的,因?yàn)槠溟_銷比較大,所以垃圾回收器會(huì)按照固定的時(shí)間間隔周期性的執(zhí)行。
var a = "浪里行舟"; var b = "前端工匠"; var a = b; //重寫a
這段代碼運(yùn)行之后,“浪里行舟”這個(gè)字符串失去了引用(之前是被a引用),系統(tǒng)檢測(cè)到這個(gè)事實(shí)之后,就會(huì)釋放該字符串的存儲(chǔ)空間以便這些空間可以被再利用。
6.3 垃圾回收機(jī)制垃圾回收有兩種方法:標(biāo)記清除、引用計(jì)數(shù)。引用計(jì)數(shù)不太常用,標(biāo)記清除較為常用。
標(biāo)記清除這是javascript中最常用的垃圾回收方式。當(dāng)變量進(jìn)入執(zhí)行環(huán)境是,就標(biāo)記這個(gè)變量為“進(jìn)入環(huán)境”。從邏輯上講,永遠(yuǎn)不能釋放進(jìn)入環(huán)境的變量所占用的內(nèi)存,因?yàn)橹灰獔?zhí)行流進(jìn)入相應(yīng)的環(huán)境,就可能會(huì)用到他們。當(dāng)變量離開環(huán)境時(shí),則將其標(biāo)記為“離開環(huán)境”。
function addTen(num){ var sum += num; //垃圾收集已將這個(gè)變量標(biāo)記為“進(jìn)入環(huán)境”。 return sum; //垃圾收集已將這個(gè)變量標(biāo)記為“離開環(huán)境”。 } addTen(10); //輸出20
var user = {name : "scott", age : "21", gender : "male"}; //在全局中定義變量,標(biāo)記變量為“進(jìn)入環(huán)境” user = null; //最后定義為null,釋放內(nèi)存
var m = 0,n = 19 // 把 m,n,add() 標(biāo)記為進(jìn)入環(huán)境。 add(m, n) // 把 a, b,標(biāo)記為進(jìn)入環(huán)境。 console.log(n) // n標(biāo)記為離開環(huán)境,等待垃圾回收。 function add(a, b) { a++ var c = a + b //c標(biāo)記為進(jìn)入環(huán)境 return c //c標(biāo)記離開環(huán)境 }6.4 哪些情況會(huì)引起內(nèi)存泄漏
雖然JavaScript會(huì)自動(dòng)垃圾收集,但是如果我們的代碼寫法不當(dāng),會(huì)讓變量一直處于“進(jìn)入環(huán)境”的狀態(tài),無法被回收
6.4.1 意外的全局變量function foo(arg) { bar = "this is a hidden global variable"; }
bar沒被聲明,會(huì)變成一個(gè)全局變量,在頁面關(guān)閉之前不會(huì)被釋放。
另一種意外的全局變量可能由 this 創(chuàng)建:
function foo() { this.variable = "potential accidental global"; } // foo 調(diào)用自己,this 指向了全局對(duì)象(window) foo();
在 JavaScript 文件頭部加上 "use strict",可以避免此類錯(cuò)誤發(fā)生。啟用嚴(yán)格模式解析 JavaScript ,避免意外的全局變量。
6.4.2 被遺忘的計(jì)時(shí)器或回調(diào)函數(shù)var someResource = getData(); setInterval(function() { var node = document.getElementById("Node"); if(node) { // 處理 node 和 someResource node.innerHTML = JSON.stringify(someResource)); } }, 1000);
如果id為Node的元素從DOM中移除,該定時(shí)器仍會(huì)存在,同時(shí),因?yàn)榛卣{(diào)函數(shù)中包含對(duì)someResource的引用,定時(shí)器外面的someResource也不會(huì)被釋放。
6.4.3 閉包function bindEvent(){ var obj=document.createElement("xxx") obj.onclick=function(){ // Even if it is a empty function } }
閉包可以維持函數(shù)內(nèi)局部變量,使其得不到釋放。上例定義事件回調(diào)時(shí),由于是函數(shù)內(nèi)定義函數(shù),并且內(nèi)部函數(shù)--事件回調(diào)引用外部函數(shù),形成了閉包。
// 將事件處理函數(shù)定義在外面 function bindEvent() { var obj = document.createElement("xxx") obj.onclick = onclickHandler } // 或者在定義事件處理函數(shù)的外部函數(shù)中,刪除對(duì)dom的引用 function bindEvent() { var obj = document.createElement("xxx") obj.onclick = function() { // Even if it is a empty function } obj = null }6.4.4 沒有清理的DOM元素引用
有時(shí),保存 DOM 節(jié)點(diǎn)內(nèi)部數(shù)據(jù)結(jié)構(gòu)很有用。假如你想快速更新表格的幾行內(nèi)容,把每一行 DOM 存成字典(JSON 鍵值對(duì))或者數(shù)組很有意義。此時(shí),同樣的 DOM 元素存在兩個(gè)引用:一個(gè)在 DOM 樹中,另一個(gè)在字典中。將來你決定刪除這些行時(shí),需要把兩個(gè)引用都清除。
var elements = { button: document.getElementById("button"), image: document.getElementById("image"), text: document.getElementById("text") }; function doStuff() { image.src = "http://some.url/image"; button.click(); console.log(text.innerHTML); } function removeButton() { document.body.removeChild(document.getElementById("button")); // 此時(shí),仍舊存在一個(gè)全局的 #button 的引用 // elements 字典。button 元素仍舊在內(nèi)存中,不能被 GC 回收。 }
雖然我們用removeChild移除了button,但是還在elements對(duì)象里保存著#button的引用,換言之,DOM元素還在內(nèi)存里面。
6.5 內(nèi)存泄漏的識(shí)別方法步驟
打開開發(fā)者工具 Performance
勾選 Screenshots 和 memory
左上角小圓點(diǎn)開始錄制(record)
停止錄制
圖中 Heap 對(duì)應(yīng)的部分就可以看到內(nèi)存在周期性的回落也可以看到垃圾回收的周期,如果垃圾回收之后的最低值(我們稱為min),min在不斷上漲,那么肯定是有較為嚴(yán)重的內(nèi)存泄漏問題。
避免內(nèi)存泄漏的一些方式
減少不必要的全局變量,或者生命周期較長(zhǎng)的對(duì)象,及時(shí)對(duì)無用的數(shù)據(jù)進(jìn)行垃圾回收
注意程序邏輯,避免“死循環(huán)”之類的
避免創(chuàng)建過多的對(duì)象
不用了的東西要及時(shí)歸還
6.6 垃圾回收的使用場(chǎng)景優(yōu)化 6.6.1 數(shù)組array優(yōu)化[]賦值給一個(gè)數(shù)組對(duì)象,是清空數(shù)組的捷徑(例如: arr = [];),但是需要注意的是,這種方式又創(chuàng)建了一個(gè)新的空對(duì)象,并且將原來的數(shù)組對(duì)象變成了一小片內(nèi)存垃圾!實(shí)際上,將數(shù)組長(zhǎng)度賦值為0(arr.length = 0)也能達(dá)到清空數(shù)組的目的,并且同時(shí)能實(shí)現(xiàn)數(shù)組重用,減少內(nèi)存垃圾的產(chǎn)生。
const arr = [1, 2, 3, 4]; console.log("浪里行舟"); arr.length = 0 // 可以直接讓數(shù)字清空,而且數(shù)組類型不變。 // arr = []; 雖然讓a變量成一個(gè)空數(shù)組,但是在堆上重新申請(qǐng)了一個(gè)空數(shù)組對(duì)象。6.6.2 對(duì)象盡量復(fù)用
對(duì)象盡量復(fù)用,尤其是在循環(huán)等地方出現(xiàn)創(chuàng)建新對(duì)象,能復(fù)用就復(fù)用。不用的對(duì)象,盡可能設(shè)置為null,盡快被垃圾回收掉。
var t = {} // 每次循環(huán)都會(huì)創(chuàng)建一個(gè)新對(duì)象。 for (var i = 0; i < 10; i++) { // var t = {};// 每次循環(huán)都會(huì)創(chuàng)建一個(gè)新對(duì)象。 t.age = 19 t.name = "123" t.index = i console.log(t) } t = null //對(duì)象如果已經(jīng)不用了,那就立即設(shè)置為null;等待垃圾回收。6.6.3 在循環(huán)中的函數(shù)表達(dá)式,能復(fù)用最好放到循環(huán)外面
// 在循環(huán)中最好也別使用函數(shù)表達(dá)式。 for (var k = 0; k < 10; k++) { var t = function(a) { // 創(chuàng)建了10次 函數(shù)對(duì)象。 console.log(a) } t(k) }
// 推薦用法 function t(a) { console.log(a) } for (var k = 0; k < 10; k++) { t(k) } t = null6.7 參考
https://github.com/ljianshu/B...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/104189.html
摘要:適當(dāng)引導(dǎo)面試官。如果有機(jī)會(huì)來實(shí)習(xí),如何最有效的快速成長(zhǎng)淘寶技術(shù)部前端內(nèi)部有針對(duì)新同學(xué)的前端夜校,有專門的老師授課。 阿里巴巴2019前端實(shí)習(xí)生招聘還剩最后兩周,面向2019年11月1日至2020年10月31日之間畢業(yè)的同學(xué),在這里分享下阿里前端面試考核的關(guān)鍵點(diǎn): Q:在面試過程中,前端面試官如何考核面試者?A:會(huì)看同學(xué)為什么選擇前端行業(yè)?是因?yàn)樗惴ㄌy?Java、C++太難?還是因?yàn)闊?..
摘要:前言秋招宣告結(jié)束,面試了接近家公司,有幸拿到,感謝這段時(shí)間一起找工作面試的朋友和陪伴我的人。一定要提前準(zhǔn)備好,不然面試官叫你說遇到的難點(diǎn),或者直接問問題時(shí)可能會(huì)懵逼。 前言 秋招宣告結(jié)束,面試了接近20家公司,有幸拿到offer,感謝這段時(shí)間一起找工作面試的朋友和陪伴我的人。這是一段難忘的經(jīng)歷,相信不亞于當(dāng)年的高考吧,也許現(xiàn)在想起來高考不算什么,也許只有經(jīng)歷過秋招的人才懂得找工作的艱辛...
摘要:前言秋招宣告結(jié)束,面試了接近家公司,有幸拿到,感謝這段時(shí)間一起找工作面試的朋友和陪伴我的人。一定要提前準(zhǔn)備好,不然面試官叫你說遇到的難點(diǎn),或者直接問問題時(shí)可能會(huì)懵逼。 前言 秋招宣告結(jié)束,面試了接近20家公司,有幸拿到offer,感謝這段時(shí)間一起找工作面試的朋友和陪伴我的人。這是一段難忘的經(jīng)歷,相信不亞于當(dāng)年的高考吧,也許現(xiàn)在想起來高考不算什么,也許只有經(jīng)歷過秋招的人才懂得找工作的艱辛...
摘要:閉包有多重前端知識(shí)點(diǎn)大百科全書前端掘金,,技巧使你的更加專業(yè)前端掘金一個(gè)幫你提升技巧的收藏集。 Vue全家桶實(shí)現(xiàn)還原豆瓣電影wap版 - 掘金用vue全家桶仿寫豆瓣電影wap版。 最近在公司項(xiàng)目中嘗試使用vue,但奈何自己初學(xué)水平有限,上了vue沒有上vuex,開發(fā)過程特別難受。 于是玩一玩本項(xiàng)目,算是對(duì)相關(guān)技術(shù)更加熟悉了。 原計(jì)劃仿寫完所有頁面,礙于豆瓣的接口API有限,實(shí)現(xiàn)頁面也有...
摘要:計(jì)算數(shù)組的極值微信面試題獲取元素的最終前端掘金一題目用代碼求出頁面上一個(gè)元素的最終的,不考慮瀏覽器,不考慮元素情況。 Excuse me?這個(gè)前端面試在搞事! - 前端 - 掘金金三銀四搞事季,前端這個(gè)近年的熱門領(lǐng)域,搞事氣氛特別強(qiáng)烈,我朋友小偉最近就在瘋狂面試,遇到了許多有趣的面試官,有趣的面試題,我來幫這個(gè)搞事 boy 轉(zhuǎn)述一下。 以下是我一個(gè)朋友的故事,真的不是我。 ... ja...
摘要:知識(shí)點(diǎn)前端面試有很多知識(shí)點(diǎn),因?yàn)榍岸吮揪蜕婕暗蕉鄠€(gè)方面。因?yàn)閷?duì)于這樣的前端框架我還不是很熟練,在這方面不能提供很好的學(xué)習(xí)思路。 關(guān)于這幾次的面試 前幾次的面試,讓我對(duì)于一個(gè)前端工程師需要掌握的知識(shí)體系有了一個(gè)全新的認(rèn)識(shí)。之前自己在學(xué)習(xí)方面一直屬于野路子,沒有一個(gè)很規(guī)范的學(xué)習(xí)路徑,往往都是想到什么就去學(xué)什么。而且基本都是處于會(huì)用的那種水平。并沒有真正的做到知其然且知其所以然。面試基本都沒...
閱讀 3843·2021-11-23 09:51
閱讀 4641·2021-11-15 11:37
閱讀 3606·2021-09-02 15:21
閱讀 2808·2021-09-01 10:31
閱讀 942·2021-08-31 14:19
閱讀 919·2021-08-11 11:20
閱讀 3374·2021-07-30 15:30
閱讀 1762·2019-08-30 15:54