不要問我八股文是什么?
舊時科舉考試的一種文體,也稱制義、制藝、時文、八比文。通俗來說(八股文就是指文章的八個部分)
而計算機技術相關崗位面試中,八股文多指面試中常出現的問題,往往都是一些看起來高大上但是實際工作中沒啥用的東西(科普結束,原文開始)
HTTP
http 和 https 的基本概念
http: 是一個客戶端和服務器端請求和應答的標准(TCP),用於從 WWW 服務器傳輸超文本到本地瀏覽器的超文本傳輸協議。
https:是以安全為目標的 HTTP 通道,即 HTTP 下 加入 SSL 層進行加密。其作用是:建立一個信息安全通道,來確保數據的傳輸,確保網站的真實性。
http 和 https 的區別及優缺點?
- http 是超文本傳輸協議,信息是明文傳輸,HTTPS 協議要比 http 協議
安全
,https 是具有安全性的 ssl 加密傳輸協議,可防止數據在傳輸過程中不被竊取、改變,確保數據的完整性。 - http 協議的
默認端口
為 80,https 的默認端口為 443。 - http 的連接很簡單,是無狀態的。https 握手階段比較
費時
,會使頁面加載時間延長 50%,增加 10%~20%的耗電。 - https
緩存
不如 http 高效,會增加數據開銷。 - Https 協議需要 ca 證書,費用較高,功能越強大的
證書費
用越高。 - SSL 證書需要綁定
IP
,不能再同一個 IP 上綁定多個域名,IPV4 資源支持不了這種消耗。
https 協議的工作原理
客戶端在使用 HTTPS 方式與 Web 服務器通信時有以下幾個步驟:
- 客戶端使用 https url 訪問服務器,則要求 web 服務器
建立 ssl 鏈接
。 - web 服務器接收到客戶端的請求之后,會
將網站的證書(證書中包含了公鑰),傳輸給客戶端
。 - 客戶端和 web 服務器端開始
協商 SSL 鏈接的安全等級
,也就是加密等級。 - 客戶端瀏覽器通過雙方協商一致的安全等級,
建立會話密鑰
,然后通過網站的公鑰來加密會話密鑰,並傳送給網站。 - web 服務器
通過自己的私鑰解密出會話密鑰
。 - web 服務器
通過會話密鑰加密與客戶端之間的通信
。
HTTP 請求跨域問題
-
跨域的原理
跨域,是指瀏覽器不能執行其他網站的腳本。它是由瀏覽器的
同源策略
造成的。
同源策略,是瀏覽器對 JavaScript 實施的安全限制,只要協議、域名、端口
有任何一個不同,都被當作是不同的域。
跨域原理,即是通過各種方式,避開瀏覽器的安全限制
。 -
解決方案
最初做項目的時候,使用的是jsonp,但存在一些問題,使用get請求不安全,攜帶數據較小,后來也用過iframe,但只有主域相同才行,也是存在些問題,后來通過了解和學習發現使用代理和proxy代理配合起來使用比較方便,就引導后台按這種方式做下服務器配置,在開發中使用proxy,在服務器上使用nginx代理,這樣開發過程中彼此都方便,效率也高;現在h5新特性還有 windows.postMessage()
-
JSONP:
ajax 請求受同源策略影響,不允許進行跨域請求,而 script 標簽 src 屬性中的鏈 接卻可以訪問跨域的 js 腳本,利用這個特性,服務端不再返回 JSON 格式的數據,而是 返回一段調用某個函數的 js 代碼,在 src 中進行了調用,這樣實現了跨域。步驟:
- 去創建一個script標簽
- script的src屬性設置接口地址
- 接口參數,必須要帶一個自定義函數名,要不然后台無法返回數據
- 通過定義函數名去接受返回的數據
-
1 //動態創建 script 2 var script = document.createElement('script'); 3 4 // 設置回調函數 5 function getData(data) { 6 console.log(data); 7 } 8 9 //設置 script 的 src 屬性,並設置請求地址 10 script.src = 'http://localhost:3000/?callback=getData'; 11 12 // 讓 script 生效 13 document.body.appendChild(script);
-
JSONP 的缺點:
JSON 只支持 get,因為 script 標簽只能使用 get 請求; JSONP 需要后端配合返回指定格式的數據。 -
document.domain 基礎域名相同 子域名不同
-
window.name 利用在一個瀏覽器窗口內,載入所有的域名都是共享一個window.name
-
CORS CORS(Cross-origin resource sharing)跨域資源共享 服務器設置對CORS的支持原理:服務器設置Access-Control-Allow-Origin HTTP響應頭之后,瀏覽器將會允許跨域請求
-
proxy代理 目前常用方式,通過服務器設置代理
-
window.postMessage() 利用h5新特性window.postMessage()
Cookie、sessionStorage、localStorage 的區別
相同點:
- 存儲在客戶端
不同點:
- cookie數據大小不能超過4k;sessionStorage和localStorage的存儲比cookie大得多,可以達到5M+
- cookie設置的過期時間之前一直有效;localStorage永久存儲,瀏覽器關閉后數據不丟失除非主動刪除數據;sessionStorage數據在當前瀏覽器窗口關閉后自動刪除
- cookie的數據會自動的傳遞到服務器;sessionStorage和localStorage數據保存在本地
TCP
TCP三次握手
- 第一次握手:
建立連接時,客戶端發送syn包(syn=j)到服務器,並進入SYN_SENT狀態,等待服務器確認
;SYN:同步序列編號(Synchronize Sequence Numbers)。 - 第二次握手:
服務器收到syn包並確認客戶的SYN
(ack=j+1),同時也發送一個自己的SYN包
(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態; - 第三次握手:
客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1)
,此包發送完畢,客戶端和服務器進入ESTABLISHED(TCP連接成功)狀態,完成三次握手。
TCP 四次揮手
客戶端進程發出連接釋放報文
,並且停止發送數據。釋放數據報文首部,FIN=1,其序列號為seq=u(等於前面已經傳送過來的數據的最后一個字節的序號加1),此時,客戶端進入FIN-WAIT-1(終止等待1)狀態
。 TCP規定,FIN報文段即使不攜帶數據,也要消耗一個序號。服務器收到連接釋放報文,發出確認報文
,ACK=1,ack=u+1,並且帶上自己的序列號seq=v,此時,服務端就進入了CLOSE-WAIT(關閉等待)狀態
。TCP服務器通知高層的應用進程,客戶端向服務器的方向就釋放了,這時候處於半關閉狀態,即客戶端已經沒有數據要發送了,但是服務器若發送數據,客戶端依然要接受。這個狀態還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。- 客戶端收到服務器的確認請求后,此時,
客戶端就進入FIN-WAIT-2(終止等待2)狀態
,等待服務器發送連接釋放報文(在這之前還需要接受服務器發送的最 后的數據)。 服務器將最后的數據發送完畢后,就向客戶端發送連接釋放報文
,FIN=1,ack=u+1,由於在半關閉狀態,服務器很可能又發送了一些數據,假定此時的序列號為seq=w,此時,服務器就進入了LAST-ACK(最后確認)狀態
,等待客戶端的確認。客戶端收到服務器的連接釋放報文后,必須發出確認
,ACK=1,ack=w+1,而自己的序列號是seq=u+1,此時,客戶端就進入了TIME-WAIT(時間等待)狀態
。注意此時TCP連接還沒有釋放,必須經過2∗∗MSL(最長報文段壽命)的時間后,當客戶端撤銷相應的TCB后,才進入CLOSED狀態
。- 服務器只要收到了客戶端發出的確認,
立即進入CLOSED狀態
。同樣,撤銷TCB后,就結束了這次的TCP連接。可以看到,服務器結束TCP連接的時間要比客戶端早一些
。
TCP/IP / 如何保證數據包傳輸的有序可靠?
對字節流分段並進行編號然后通過 ACK 回復
和超時重發
這兩個機制來保證。
- (1)為了保證數據包的可靠傳遞,發送方必須把已發送的數據包保留在緩沖區;
- 並為每個已發送的數據包啟動一個超時定時器;
- 如在定時器超時之前收到了對方發來的應答信息(可能是對本包的應答,也可以是對本包后續包的應答),則釋放該數據包占用的緩沖區;
- 否則,重傳該數據包,直到收到應答或重傳次數超過規定的最大次數為止。
- 接收方收到數據包后,先進行CRC校驗,如果正確則把數據交給上層協議,然后給發送方發送一個累計應答包,表明該數據已收到,如果接收方正好也有數據要發給發送方,應答包也可方在數據包中捎帶過去。
TCP和UDP的區別
- TCP是面向
鏈接
的,而UDP是面向無連接的。 - TCP僅支持
單播傳輸
,UDP 提供了單播,多播,廣播的功能。 - TCP的三次握手保證了連接的
可靠性
; UDP是無連接的、不可靠的一種數據傳輸協議,首先不可靠性體現在無連接上,通信都不需要建立連接,對接收到的數據也不發送確認信號,發送端不知道數據是否會正確接收。 - UDP的
頭部開銷
比TCP的更小,數據傳輸速率更高
,實時性更好
。
粘包問題分析與對策
CP粘包是指發送方發送的若干包數據到接收方接收時粘成一包,從接收緩沖區看,后一包數據的頭緊接着前一包數據的尾。
粘包出現原因
簡單得說,在流傳輸中出現,UDP不會出現粘包,因為它有消息邊界
粘包情況有兩種,一種是粘在一起的包都是完整的數據包
,另一種情況是粘在一起的包有不完整的包
。
為了避免粘包現象,可采取以下幾種措施:
- 對於發送方引起的粘包現象,用戶可通過編程設置來避免,
TCP提供了強制數據立即傳送的操作指令push
,TCP軟件收到該操作指令后,就立即將本段數據發送出去,而不必等待發送緩沖區滿; - 對於接收方引起的粘包,則可通過優化程序設計、精簡接收進程工作量、
提高接收進程優先級等措施
,使其及時接收數據,從而盡量避免出現粘包現象; - 由接收方控制,將一包數據按結構字段,人為控制分多次接收,然后合並,通過這種手段來避免粘包。
分包多發
。
以上提到的三種措施,都有其不足之處。
- 第一種編程設置方法雖然可以避免發送方引起的粘包,但它關閉了優化算法,降低了網絡發送效率,影響應用程序的性能,一般不建議使用。
- 第二種方法只能減少出現粘包的可能性,但並不能完全避免粘包,當發送頻率較高時,或由於網絡突發可能使某個時間段數據包到達接收方較快,接收方還是有可能來不及接收,從而導致粘包。
- 第三種方法雖然避免了粘包,但應用程序的效率較低,對實時應用的場合不適合。
一種比較周全的對策是:接收方創建一預處理線程,對接收到的數據包進行預處理,將粘連的包分開。實驗證明這種方法是高效可行的。
瀏覽器
從輸入URL到頁面加載的全過程
-
首先在瀏覽器中輸入URL
-
查找緩存:瀏覽器先查看瀏覽器緩存-系統緩存-路由緩存中是否有該地址頁面,如果有則顯示頁面內容。如果沒有則進行下一步。
- 瀏覽器緩存:瀏覽器會記錄DNS一段時間,因此,只是第一個地方解析DNS請求;
- 操作系統緩存:如果在瀏覽器緩存中不包含這個記錄,則會使系統調用操作系統, 獲取操作系統的記錄(保存最近的DNS查詢緩存);
- 路由器緩存:如果上述兩個步驟均不能成功獲取DNS記錄,繼續搜索路由器緩存;
- ISP緩存:若上述均失敗,繼續向ISP搜索。
-
DNS域名解析:瀏覽器向DNS服務器發起請求,解析該URL中的域名對應的IP地址。
DNS服務器是基於UDP的,因此會用到UDP協議。
。 -
建立TCP連接:解析出IP地址后,根據IP地址和默認80端口,和服務器建立TCP連接
-
發起HTTP請求:瀏覽器發起讀取文件的HTTP請求,,該請求報文作為TCP三次握手的第三次數據發送給服務器
-
服務器響應請求並返回結果:服務器對瀏覽器請求做出響應,並把對應的html文件發送給瀏覽器
-
關閉TCP連接:通過四次揮手釋放TCP連接
-
瀏覽器渲染:客戶端(瀏覽器)解析HTML內容並渲染出來,瀏覽器接收到數據包后的解析流程為:
- 構建DOM樹:詞法分析然后解析成DOM樹(dom tree),是由dom元素及屬性節點組成,樹的根是document對象
- 構建CSS規則樹:生成CSS規則樹(CSS Rule Tree)
- 構建render樹:Web瀏覽器將DOM和CSSOM結合,並構建出渲染樹(render tree)
- 布局(Layout):計算出每個節點在屏幕中的位置
- 繪制(Painting):即遍歷render樹,並使用UI后端層繪制每個節點。
JS引擎解析過程:調用JS引擎執行JS代碼(JS的解釋階段,預處理階段,執行階段生成執行上下文,VO,作用域鏈、回收機制等等)
- 創建window對象:window對象也叫全局執行環境,當頁面產生時就被創建,所有的全局變量和函數都屬於window的屬性和方法,而DOM Tree也會映射在window的doucment對象上。當關閉網頁或者關閉瀏覽器時,全局執行環境會被銷毀。
- 加載文件:完成js引擎分析它的語法與詞法是否合法,如果合法進入預編譯
- 預編譯:在預編譯的過程中,瀏覽器會尋找全局變量聲明,把它作為window的屬性加入到window對象中,並給變量賦值為'undefined';尋找全局函數聲明,把它作為window的方法加入到window對象中,並將函數體賦值給他(匿名函數是不參與預編譯的,因為它是變量)。而變量提升作為不合理的地方在ES6中已經解決了,函數提升還存在。
- 解釋執行:執行到變量就賦值,如果變量沒有被定義,也就沒有被預編譯直接賦值,在ES5非嚴格模式下這個變量會成為window的一個屬性,也就是成為全局變量。string、int這樣的值就是直接把值放在變量的存儲空間里,object對象就是把指針指向變量的存儲空間。函數執行,就將函數的環境推入一個環境的棧中,執行完成后再彈出,控制權交還給之前的環境。JS作用域其實就是這樣的執行流機制實現的。
瀏覽器重繪與回流的區別?
重排/回流(Reflow)
:當DOM
的變化影響了元素的幾何信息,瀏覽器需要重新計算元素的幾何屬性,將其安放在界面中的正確位置,這個過程叫做重排。表現為重新生成布局,重新排列元素。重繪(Repaint)
: 當一個元素的外觀發生改變,但沒有改變布局,重新把元素外觀繪制出來的過程,叫做重繪。表現為某些元素的外觀被改變
單單改變元素的外觀,肯定不會引起網頁重新生成布局,但當瀏覽器完成重排之后,將會重新繪制受到此次重排影響的部分
重排和重繪代價是高昂的,它們會破壞用戶體驗,並且讓UI展示非常遲緩,而相比之下重排的性能影響更大,在兩者無法避免的情況下,一般我們寧可選擇代價更小的重繪。
『重繪』不一定會出現『重排』,『重排』必然會出現『重繪』。
如何觸發回流和重繪?
任何改變用來構建渲染樹的信息都會導致一次回流或重繪:
- 添加、刪除、更新DOM節點
- 通過display: none隱藏一個DOM節點-觸發回流和重繪
- 通過visibility: hidden隱藏一個DOM節點-只觸發重繪,因為沒有幾何變化
- 移動或者給頁面中的DOM節點添加動畫
- 添加一個樣式表,調整樣式屬性
- 用戶行為,例如調整窗口大小,改變字號,或者滾動。
如何避免回流和重繪?
-
集中改變樣式
,不要一條一條地修改 DOM 的樣式。 -
不要把 DOM 結點的屬性值放在循環里當成循環里的變量。
-
為動畫的 HTML 元件使用
fixed
或absoult
的position
,那么修改他們的 CSS 是不會 reflow 的。 -
不使用 table 布局。因為可能很小的一個小改動會造成整個 table 的重新布局。
-
盡量只修改
position:absolute
或fixed
元素,對其他元素影響不大 -
動畫開始
GPU
加速,translate
使用3D
變化 -
提升為合成層
將元素提升為合成層有以下優點:
- 合成層的位圖,會交由 GPU 合成,比 CPU 處理要快
- 當需要 repaint 時,只需要 repaint 本身,不會影響到其他的層
- 對於 transform 和 opacity 效果,不會觸發 layout 和 paint
提升合成層的最好方式是使用 CSS 的 will-change 屬性:
#target { will-change: transform; }
HTML && CSS
HTML5 新特性、語義化
-
概念:
HTML5的語義化指的是
合理正確的使用語義化的標簽來創建頁面結構
。【正確的標簽做正確的事】 -
語義化標簽:
header nav main article section aside footer
-
語義化的優點:
- 在
沒CSS樣式的情況下,頁面整體也會呈現很好的結構效果
代碼結構清晰
,易於閱讀,利於開發和維護
方便其他設備解析(如屏幕閱讀器)根據語義渲染網頁。有利於搜索引擎優化(SEO)
,搜索引擎爬蟲會根據不同的標簽來賦予不同的權重
- 在
CSS 選擇器及優先級
選擇器
- id選擇器(#myid)
- 類選擇器(.myclass)
- 屬性選擇器(a[rel="external"])
- 偽類選擇器(a:hover, li:nth-child)
- 標簽選擇器(div, h1,p)
- 相鄰選擇器(h1 + p)
- 子選擇器(ul > li)
- 后代選擇器(li a)
- 通配符選擇器(*)
優先級:
!important
- 內聯樣式(1000)
- ID選擇器(0100)
- 類選擇器/屬性選擇器/偽類選擇器(0010)
- 元素選擇器/偽元素選擇器(0001)
- 關系選擇器/通配符選擇器(0000)
帶!important 標記的樣式屬性優先級最高; 樣式表的來源相同時:!important > 行內樣式>ID選擇器 > 類選擇器 > 標簽 > 通配符 > 繼承 > 瀏覽器默認屬性
position 屬性的值有哪些及其區別
- 固定定位 fixed: 元素的位置相對於瀏覽器窗口是固定位置,即使窗口是滾動的它也不會移動。Fixed 定 位使元素的位置與文檔流無關,因此不占據空間。 Fixed 定位的元素和其他元素重疊。
- 相對定位 relative: 如果對一個元素進行相對定位,它將出現在它所在的位置上。然后,可以通過設置垂直 或水平位置,讓這個元素“相對於”它的起點進行移動。 在使用相對定位時,無論是 否進行移動,元素仍然占據原來的空間。因此,移動元素會導致它覆蓋其它框。
- 絕對定位 absolute: 絕對定位的元素的位置相對於最近的已定位父元素,如果元素沒有已定位的父元素,那 么它的位置相對於。absolute 定位使元素的位置與文檔流無關,因此不占據空間。 absolute 定位的元素和其他元素重疊。
- 粘性定位 sticky: 元素先按照普通文檔流定位,然后相對於該元素在流中的 flow root(BFC)和 containing block(最近的塊級祖先元素)定位。而后,元素定位表現為在跨越特定閾值前為相對定 位,之后為固定定位。
- 默認定位 Static: 默認值。沒有定位,元素出現在正常的流中(忽略 top, bottom, left, right 或者 z-index 聲 明)。 inherit: 規定應該從父元素繼承 position 屬性的值。
box-sizing屬性
- box-sizing 規定兩個並排的帶邊框的框,語法為 box-sizing:content-box/border-box/inherit
- content-box:寬度和高度分別應用到元素的內容框,在寬度和高度之外繪制元素的內邊距和邊框。【標准盒子模型】
- border-box:為元素設定的寬度和高度決定了元素的邊框盒。【IE 盒子模型】
- inherit:繼承父元素的 box-sizing 值。
CSS 盒子模型
CSS 盒模型本質上是一個盒子,它包括:邊距,邊框,填充和實際內容。CSS 中的盒子模型包括 IE 盒子模型和標准的 W3C 盒子模型。
在標准的盒子模型中,width 指 content 部分的寬度
。
在 IE 盒子模型中,width 表示 content+padding+border 這三個部分的寬度
。
故在計算盒子的寬度時存在差異:
標准盒模型: 一個塊的總寬度 = width+margin(左右)+padding(左右)+border(左右)
怪異盒模型: 一個塊的總寬度 = width+margin(左右)(既 width 已經包含了 padding 和 border 值)
讓一個元素水平垂直居中
-
水平居中
-
對於 行內元素 :
text-align: center
; -
對於確定寬度的塊級元素:
(1)width和margin實現。
margin: 0 auto
;(2)絕對定位和margin-left: -width/2, 前提是父元素position: relative
-
對於寬度未知的塊級元素
(1)
table標簽配合margin左右auto實現水平居中
。使用table標簽(或直接將塊級元素設值為 display:table),再通過給該標簽添加左右margin為auto。(2)inline-block實現水平居中方法。display:inline-block和text-align:center實現水平居中。
(3)
絕對定位+transform
,translateX可以移動本身元素的50%。(4)flex布局使用
justify-content:center
-
-
垂直居中
- 利用
line-height
實現居中,這種方法適合純文字類 - 通過設置父容器 相對定位 ,子級設置
絕對定位
,標簽通過margin實現自適應居中 - 彈性布局 flex :父級設置display: flex; 子級設置margin為auto實現自適應居中
- 父級設置相對定位,子級設置絕對定位,並且通過位移 transform 實現
table 布局
,父級通過轉換成表格形式,然后子級設置 vertical-align 實現
。(需要注意的是:vertical-align: middle使用的前提條件是內聯元素以及display值為table-cell的元素)
- 利用
隱藏頁面中某個元素的方法
1.opacity:0
,該元素隱藏起來了,但不會改變頁面布局,並且,如果該元素已經綁定 一些事件,如click 事件,那么點擊該區域,也能觸發點擊事件的
2.visibility:hidden
,該元素隱藏起來了,但不會改變頁面布局,但是不會觸發該元素已 經綁定的事件 ,隱藏對應元素,在文檔布局中仍保留原來的空間(重繪)
3.display:none
,把元素隱藏起來,並且會改變頁面布局,可以理解成在頁面中把該元素。 不顯示對應的元素,在文檔布局中不再分配空間(回流+重繪)
用CSS實現三角符號
/*記憶口訣:盒子寬高均為零,三面邊框皆透明。 */ div:after{ position: absolute; width: 0px; height: 0px; content: " "; border-right: 100px solid transparent; border-top: 100px solid #ff0; border-left: 100px solid transparent; border-bottom: 100px solid transparent; }
頁面布局
1.Flex 布局
布局的傳統解決方案,基於盒狀模型,依賴 display 屬性 + position 屬性 + float 屬性。它對於那些特殊布局非常不方便,比如,垂直居中就不容易實現。
Flex 是 Flexible Box 的縮寫,意為"彈性布局",用來為盒狀模型提供最大的靈活性。指定容器 display: flex 即可。 簡單的分為容器屬性和元素屬性。
容器的屬性:
- flex-direction:決定主軸的方向(即子 item 的排列方法)flex-direction: row | row-reverse | column | column-reverse;
- flex-wrap:決定換行規則 flex-wrap: nowrap | wrap | wrap-reverse;
- flex-flow: .box { flex-flow: || ; }
- justify-content:對其方式,水平主軸對齊方式
- align-items:對齊方式,豎直軸線方向
- align-content
項目的屬性(元素的屬性):
- order 屬性:定義項目的排列順序,順序越小,排列越靠前,默認為 0
- flex-grow 屬性:定義項目的放大比例,即使存在空間,也不會放大
- flex-shrink 屬性:定義了項目的縮小比例,當空間不足的情況下會等比例的縮小,如果 定義個 item 的 flow-shrink 為 0,則為不縮小
- flex-basis 屬性:定義了在分配多余的空間,項目占據的空間。
- flex:是 flex-grow 和 flex-shrink、flex-basis 的簡寫,默認值為 0 1 auto。
- align-self:允許單個項目與其他項目不一樣的對齊方式,可以覆蓋
- align-items,默認屬 性為 auto,表示繼承父元素的 align-items 比如說,用 flex 實現聖杯布局
2.Rem 布局
首先 Rem 相對於根(html)的 font-size 大小來計算。簡單的說它就是一個相對單例 如:font-size:10px;,那么(1rem = 10px)了解計算原理后首先解決怎么在不同設備上設置 html 的 font-size 大小。其實 rem 布局的本質是等比縮放,一般是基於寬度。
優點:可以快速適用移動端布局,字體,圖片高度
缺點:
①目前 ie 不支持,對 pc 頁面來講使用次數不多;
②數據量大:所有的圖片,盒子都需要我們去給一個准確的值;才能保證不同機型的適配;
③在響應式布局中,必須通過 js 來動態控制根元素 font-size 的大小。也就是說 css 樣式和 js 代碼有一定的耦合性。且必須將改變 font-size 的代碼放在 css 樣式之前。
3.百分比布局
通過百分比單位 " % " 來實現響應式的效果。通過百分比單位可以使得瀏覽器中的組件的寬和高隨着瀏覽器的變化而變化,從而實現響應式的效果。 直觀的理解,我們可能會認為子元素的百分比完全相對於直接父元素,height 百分比相 對於 height,width 百分比相對於 width。 padding、border、margin 等等不論是垂直方向還是水平方向,都相對於直接父元素的 width。 除了 border-radius 外,還有比如 translate、background-size 等都是相對於自身的。
缺點:
(1)計算困難
(2)各個屬性中如果使用百分比,相對父元素的屬性並不是唯一的。造成我們使用百分比單位容易使布局問題變得復雜。
4.浮動布局
浮動布局:當元素浮動以后可以向左或向右移動,直到它的外邊緣碰到包含它的框或者另外一個浮動元素的邊框為止。元素浮動以后會脫離正常的文檔流,所以文檔的普通流中的框就變的好像浮動元素不存在一樣。
優點
這樣做的優點就是在圖文混排的時候可以很好的使文字環繞在圖片周圍。另外當元素浮動了起來之后,它有着塊級元素的一些性質例如可以設置寬高等,但它與inline-block還是有一些區別的,第一個就是關於橫向排序的時候,float可以設置方向而inline-block方向是固定的;還有一個就是inline-block在使用時有時會有空白間隙的問題
缺點
最明顯的缺點就是浮動元素一旦脫離了文檔流,就無法撐起父元素,會造成父級元素高度塌陷
。
如何使用rem或viewport進行移動端適配
rem適配原理:
改變了一個元素在不同設備上占據的css像素的個數
rem適配的優缺點
- 優點:沒有破壞完美視口
- 缺點:px值轉換rem太過於復雜(下面我們使用less來解決這個問題)
viewport適配的原理
viewport適配方案中,每一個元素在不同設備上占據的css像素的個數是一樣的。但是css像素和物理像素的比例是不一樣的,等比的
viewport適配的優缺點
- 在我們設計圖上所量取的大小即為我們可以設置的像素大小,即所量即所設
- 缺點破壞完美視口
清除浮動的方式
- 添加額外標簽
<div class="parent"> //添加額外標簽並且添加clear屬性 <div style="clear:both"></div> //也可以加一個br標簽 </div>
- 父級添加overflow屬性,或者設置高度
- 建立偽類選擇器清除浮動
//在css中添加:after偽元素 .parent:after{ /* 設置添加子元素的內容是空 */ content: ''; /* 設置添加子元素為塊級元素 */ display: block; /* 設置添加的子元素的高度0 */ height: 0; /* 設置添加子元素看不見 */ visibility: hidden; /* 設置clear:both */ clear: both; }
JS、TS、ES6
JS中的8種數據類型及區別
包括值類型(基本對象類型)和引用類型(復雜對象類型)
基本類型(值類型): Number(數字),String(字符串),Boolean(布爾),Symbol(符號),null(空),undefined(未定義)在內存中占據固定大小,保存在棧內存中
引用類型(復雜數據類型): Object(對象)、Function(函數)。其他還有Array(數組)、Date(日期)、RegExp(正則表達式)、特殊的基本包裝類型(String、Number、Boolean) 以及單體內置對象(Global、Math)等 引用類型的值是對象 保存在堆內存中,棧內存存儲的是對象的變量標識符以及對象在堆內存中的存儲地址。
JS中的數據類型檢測方案
1.typeof
console.log(typeof 1); // number console.log(typeof true); // boolean console.log(typeof 'mc'); // string console.log(typeof function(){}); // function console.log(typeof console.log()); // function console.log(typeof []); // object console.log(typeof {}); // object console.log(typeof null); // object console.log(typeof undefined); // undefined
優點:能夠快速區分基本數據類型
缺點:不能將Object、Array和Null區分,都返回object
2.instanceof
console.log(1 instanceof Number); // false console.log(true instanceof Boolean); // false console.log('str' instanceof String); // false console.log([] instanceof Array); // true console.log(function(){} instanceof Function); // true console.log({} instanceof Object); // true
優點:能夠區分Array、Object和Function,適合用於判斷自定義的類實例對象
缺點:Number,Boolean,String基本數據類型不能判斷
3.Object.prototype.toString.call()
var toString = Object.prototype.toString; console.log(toString.call(1)); //[object Number] console.log(toString.call(true)); //[object Boolean] console.log(toString.call('mc')); //[object String] console.log(toString.call([])); //[object Array] console.log(toString.call({})); //[object Object] console.log(toString.call(function(){})); //[object Function] console.log(toString.call(undefined)); //[object Undefined] console.log(toString.call(null)); //[object Null]
優點:精准判斷數據類型
缺點:寫法繁瑣不容易記,推薦進行封裝后使用
var && let && const
ES6之前創建變量用的是var,之后創建變量用的是let/const
三者區別:
- var定義的變量,
沒有塊的概念,可以跨塊訪問
, 不能跨函數訪問。
let定義的變量,只能在塊作用域里訪問,不能跨塊訪問,也不能跨函數訪問。
const用來定義常量,使用時必須初始化(即必須賦值),只能在塊作用域里訪問,且不能修改。 - var可以
先使用,后聲明
,因為存在變量提升;let必須先聲明后使用。 - var是允許在相同作用域內
重復聲明同一個變量
的,而let與const不允許這一現象。 - 在全局上下文中,基於let聲明的全局變量和全局對象GO(window)沒有任何關系 ;
var聲明的變量會和GO有映射關系; 會產生暫時性死區
:
暫時性死區是瀏覽器的bug:檢測一個未被聲明的變量類型時,不會報錯,會返回undefined 如:console.log(typeof a) //undefined 而:console.log(typeof a)//未聲明之前不能使用 let a
- let /const/function會把當前所在的大括號(除函數之外)作為一個全新的塊級上下文,應用這個機制,在開發項目的時候,遇到循環事件綁定等類似的需求,無需再自己構建閉包來存儲,只要基於let的塊作用特征即可解決
JS垃圾回收機制
-
項目中,如果存在大量不被釋放的內存(堆/棧/上下文),頁面性能會變得很慢。當某些代碼操作不能被合理釋放,就會造成內存泄漏。我們盡可能減少使用閉包,因為它會消耗內存。
-
瀏覽器垃圾回收機制/內存回收機制:
瀏覽器的Javascript具有自動垃圾回收機制(GC:Garbage Collecation),垃圾收集器會定期(周期性)找出那些不在繼續使用的變量,然后釋放其內存。
-
標記清除:在
js
中,最常用的垃圾回收機制是標記清除:當變量進入執行環境時,被標記為“進入環境”,當變量離開執行環境時,會被標記為“離開環境”。垃圾回收器會銷毀那些帶標記的值並回收它們所占用的內存空間。
谷歌瀏覽器:“查找引用”,瀏覽器不定時去查找當前內存的引用,如果沒有被占用了,瀏覽器會回收它;如果被占用,就不能回收。
IE瀏覽器:“引用計數法”,當前內存被占用一次,計數累加1次,移除占用就減1,減到0時,瀏覽器就回收它。 -
優化手段:內存優化 ; 手動釋放:取消內存的占用即可。
(1)堆內存:fn = null 【null:空指針對象】
(2)棧內存:把上下文中,被外部占用的堆的占用取消即可。
-
內存泄漏
在 JS 中,常見的內存泄露主要有 4 種,全局變量、閉包、DOM 元素的引用、定時器
作用域和作用域鏈
創建函數的時候,已經聲明了當前函數的作用域==>當前創建函數所處的上下文
。如果是在全局下創建的函數就是[[scope]]:EC(G)
,函數執行的時候,形成一個全新的私有上下文EC(FN)
,供字符串代碼執行(進棧執行)
定義:簡單來說作用域就是變量與函數的可訪問范圍,由當前環境與上層環境的一系列變量對象組成
1.全局作用域:代碼在程序的任何地方都能被訪問,window 對象的內置屬性都擁有全局作用域。
2.函數作用域:在固定的代碼片段才能被訪問
作用:作用域最大的用處就是隔離變量
,不同作用域下同名變量不會有沖突。
作用域鏈參考鏈接一般情況下,變量到 創建該變量 的函數的作用域中取值。但是如果在當前作用域中沒有查到,就會向上級作用域去查,直到查到全局作用域,這么一個查找過程形成的鏈條就叫做作用域鏈。
閉包的兩大作用:保存/保護
-
閉包的概念
函數執行時形成的私有上下文EC(FN),正常情況下,代碼執行完會出棧后釋放;但是特殊情況下,如果當前私有上下文中的某個東西被上下文以外的事物占用了,則上下文不會出棧釋放,從而形成不銷毀的上下文。 函數執行函數執行過程中,會形成一個全新的私有上下文,可能會被釋放,可能不會被釋放,不論釋放與否,他的作用是:
(1)保護:划分一個獨立的代碼執行區域,在這個區域中有自己私有變量存儲的空間,保護自己的私有變量不受外界干擾(操作自己的私有變量和外界沒有關系);
(2)保存:如果當前上下文不被釋放【只要上下文中的某個東西被外部占用即可】,則存儲的這些私有變量也不會被釋放,可以供其下級上下文中調取使用,相當於把一些值保存起來了;
我們把函數執行形成私有上下文,來保護和保存私有變量機制稱為閉包
。
閉包是指有權訪問另一個函數作用域中的變量的函數--《JavaScript高級程序設計》
稍全面的回答: 在js中變量的作用域屬於函數作用域, 在函數執行完后,作用域就會被清理,內存也會隨之被回收,但是由於閉包函數是建立在函數內部的子函數, 由於其可訪問上級作用域,即使上級函數執行完, 作用域也不會隨之銷毀, 這時的子函數(也就是閉包),便擁有了訪問上級作用域中變量的權限,即使上級函數執行完后作用域內的值也不會被銷毀。
-
閉包的特性:
-
1、內部函數可以訪問定義他們外部函數的參數和變量。(作用域鏈的向上查找,把外圍的作用域中的變量值存儲在內存中而不是在函數調用完畢后銷毀)設計私有的方法和變量,避免全局變量的污染。
1.1.閉包是密閉的容器,,類似於set、map容器,存儲數據的
1.2.閉包是一個對象,存放數據的格式為 key-value 形式
-
2、函數嵌套函數
-
3、本質是將函數內部和外部連接起來。優點是可以讀取函數內部的變量,讓這些變量的值始終保存在內存中,不會在函數被調用之后自動清除
-
-
閉包形成的條件:
- 函數的嵌套
- 內部函數引用外部函數的局部變量,延長外部函數的變量生命周期
-
閉包的用途:
- 模仿塊級作用域
- 保護外部函數的變量 能夠訪問函數定義時所在的詞法作用域(阻止其被回收)
- 封裝私有化變量
- 創建模塊
-
閉包應用場景
閉包的兩個場景,閉包的兩大作用:
保存/保護
。 在開發中, 其實我們隨處可見閉包的身影, 大部分前端JavaScript 代碼都是“事件驅動”的,即一個事件綁定的回調方法; 發送ajax請求成功|失敗的回調;setTimeout的延時回調;或者一個函數內部返回另一個匿名函數,這些都是閉包的應用。 -
閉包的優點:延長局部變量的生命周期
-
閉包缺點:會導致函數的變量一直保存在內存中,過多的閉包可能會導致內存泄漏
JS 中 this 的五種情況
- 作為普通函數執行時,
this
指向window
。 - 當函數作為對象的方法被調用時,
this
就會指向該對象
。 - 構造器調用,
this
指向返回的這個對象
。 - 箭頭函數 箭頭函數的
this
綁定看的是this所在函數定義在哪個對象下
,就綁定哪個對象。如果有嵌套的情況,則this綁定到最近的一層對象上。 - 基於Function.prototype上的
apply 、 call 和 bind
調用模式,這三個方法都可以顯示的指定調用函數的 this 指向。apply
接收參數的是數組,call
接受參數列表,`` bind方法通過傳入一個對象,返回一個
this綁定了傳入對象的新函數。這個函數的
this指向除了使用
new `時會被改變,其他情況下都不會改變。若為空默認是指向全局對象window。
原型 && 原型鏈
原型關系:
- 每個 class都有顯示原型 prototype
- 每個實例都有隱式原型 _ proto_
- 實例的_ proto_指向對應 class 的 prototype
原型: 在 JS 中,每當定義一個對象(函數也是對象)時,對象中都會包含一些預定義的屬性。其中每個函數對象
都有一個prototype
屬性,這個屬性指向函數的原型對象
。
原型鏈:函數的原型鏈對象constructor默認指向函數本身,原型對象除了有原型屬性外,為了實現繼承,還有一個原型鏈指針__proto__,該指針是指向上一層的原型對象,而上一層的原型對象的結構依然類似。因此可以利用__proto__一直指向Object的原型對象上,而Object原型對象用Object.prototype.__ proto__ = null表示原型鏈頂端。如此形成了js的原型鏈繼承。同時所有的js對象都有Object的基本防范
特點: JavaScript
對象是通過引用來傳遞的,我們創建的每個新對象實體中並沒有一份屬於自己的原型副本。當我們修改原型時,與之相關的對象也會繼承這一改變。
new運算符的實現機制
- 首先創建了一個新的
空對象
設置原型
,將對象的原型設置為函數的prototype
對象。- 讓函數的
this
指向這個對象,執行構造函數的代碼(為這個新對象添加屬性) - 判斷函數的返回值類型,如果是值類型,返回創建的對象。如果是引用類型,就返回這個引用類型的對象。
EventLoop 事件循環
JS
是單線程的,為了防止一個函數執行時間過長阻塞后面的代碼,所以會先將同步代碼壓入執行棧中,依次執行,將異步代碼推入異步隊列,異步隊列又分為宏任務隊列和微任務隊列,因為宏任務隊列的執行時間較長,所以微任務隊列要優先於宏任務隊列。微任務隊列的代表就是,Promise.then
,MutationObserver
,宏任務的話就是setImmediate setTimeout setInterval
JS運行的環境。一般為瀏覽器或者Node。 在瀏覽器環境中,有JS 引擎線程和渲染線程,且兩個線程互斥。 Node環境中,只有JS 線程。 不同環境執行機制有差異,不同任務進入不同Event Queue隊列。 當主程結束,先執行准備好微任務,然后再執行准備好的宏任務,一個輪詢結束。
瀏覽器中的事件環(Event Loop)
事件環的運行機制是,先會執行棧中的內容,棧中的內容執行后執行微任務,微任務清空后再執行宏任務,先取出一個宏任務,再去執行微任務,然后在取宏任務清微任務這樣不停的循環。
-
eventLoop 是由JS的宿主環境(瀏覽器)來實現的;
-
事件循環可以簡單的描述為以下四個步驟:
- 函數入棧,當Stack中執行到異步任務的時候,就將他丟給WebAPIs,接着執行同步任務,直到Stack為空;
- 此期間WebAPIs完成這個事件,把回調函數放入隊列中等待執行(微任務放到微任務隊列,宏任務放到宏任務隊列)
- 執行棧為空時,Event Loop把微任務隊列執行清空;
- 微任務隊列清空后,進入宏任務隊列,取隊列的第一項任務放入Stack(棧)中執行,執行完成后,查看微任務隊列是否有任務,有的話,清空微任務隊列。重復4,繼續從宏任務中取任務執行,執行完成之后,繼續清空微任務,如此反復循環,直至清空所有的任務。
瀏覽器中的任務源(task):
宏任務(macrotask)
:
宿主環境提供的,比如瀏覽器
ajax、setTimeout、setInterval、setTmmediate(只兼容ie)、script、requestAnimationFrame、messageChannel、UI渲染、一些瀏覽器api微任務(microtask)
:
語言本身提供的,比如promise.then
then、queueMicrotask(基於then)、mutationObserver(瀏覽器提供)、messageChannel 、mutationObersve
介紹節流防抖原理、區別以及應用
節流
:事件觸發后,規定時間內,事件處理函數不能再次被調用。也就是說在規定的時間內,函數只能被調用一次,且是最先被觸發調用的那次。
防抖
:多次觸發事件,事件處理函數只能執行一次,並且是在觸發操作結束時執行。也就是說,當一個事件被觸發准備執行事件函數前,會等待一定的時間(這時間是碼農自己去定義的,比如 1 秒),如果沒有再次被觸發,那么就執行,如果被觸發了,那就本次作廢,重新從新觸發的時間開始計算,並再次等待 1 秒,直到能最終執行!
使用場景
:
節流:滾動加載更多、搜索框搜的索聯想功能、高頻點擊、表單重復提交……
防抖:搜索框搜索輸入,並在輸入完以后自動搜索、手機號,郵箱驗證輸入檢測、窗口大小 resize 變化后,再重新渲染。
/** * 節流函數 一個函數執行一次后,只有大於設定的執行周期才會執行第二次。有個需要頻繁觸發的函數,出於優化性能的角度,在規定時間內,只讓函數觸發的第一次生效,后面的不生效。 * @param fn要被節流的函數 * @param delay規定的時間 */ function throttle(fn, delay) { //記錄上一次函數觸發的時間 var lastTime = 0; return function(){ //記錄當前函數觸發的時間 var nowTime = Date.now(); if(nowTime - lastTime > delay){ //修正this指向問題 fn.call(this); //同步執行結束時間 lastTime = nowTime; } } } document.onscroll = throttle(function () { console.log('scllor事件被觸發了' + Date.now()); }, 200); /** * 防抖函數 一個需要頻繁觸發的函數,在規定時間內,只讓最后一次生效,前面的不生效 * @param fn要被節流的函數 * @param delay規定的時間 */ function debounce(fn, delay) { //記錄上一次的延時器 var timer = null; return function () { //清除上一次的演示器 clearTimeout(timer); //重新設置新的延時器 timer = setTimeout(function(){ //修正this指向問題 fn.apply(this); }, delay); } } document.getElementById('btn').onclick = debounce(function () { console.log('按鈕被點擊了' + Date.now()); }, 1000);