想寫這篇文章已經很久了,作為一個技工,怎麼能免的了一直學習呢! 也換過兩份工作,所以深知每次面試就是一次大考。寫這篇文章的目的是對技術的回顧,也是對自己成長的記錄
。(對於某些知識點如果我理解的不對,大家有更好的理解角度,也希望大家指出來,大家相互討論,學習吧! )。
閒話休提,書歸正傳。面試就是用較短的時間做到雙方相互瞭解
,所以我儘量用簡短語言來描述事物本質,至於對於某些重要知識點的深入理解,我會在後續逐步整理出文,請關注、點贊、收藏!
小編最近已經離職了,也在尋求新的機會,如果剛好你的公司在招聘,給我個機會,聊一下唄!😄😄😄
如果有看到標題號順序沒對上,不要奇怪,那是因為關於某些問題我認為還可以再優化,還在整理中,我這麼重視格式的人,不會忘記的,哈哈哈!!!這份特殊的【新年禮物】
,你可要收好啊!
mermaid
pie title 文章分為多個模組進行介紹
"http" : 11
"html" : 4
"css" : 5
"javascript" : 23
"react" : 9
"業務層面" : 1
"flutter" : 9
"其他" : 3
面試路上互助交流群
如果你也在找工作 或者你準備換工作 ,那麼可以一起交流一下呀,也不介意潛水哈 😄,我只是希望面試的路上不是你我一個人獨立奮鬥 ,在大城市生活久了人都變的孤獨了許多,我不希望面試完想找人分享找不到 。在群裡我們可以共享資源、一同避一下那些踩雷的公司、📣 吐槽生活、互相激勵等等,下面是群二維碼,歡迎加群交流呦!💪 加油 準備衝刺2023!
一、http
相關
http
相關知識,前端必備網路相關知識。
1. 位址列輸入 URL
到頁面展現都經歷了哪些過程?
1. 構建請求
2. 查詢強快取
3. DNS解析
4. 建立TCP連線(3次握手)
5. 傳送HTTP請求(請求行/頭/體)
6. 伺服器處理收到的請求,將資料返回瀏覽器
7. 瀏覽器收到HTTP響應
8. 讀取內容,解析html,瀏覽器渲染,
9. 生成dom樹、解析css樣式、js互動
2. 三次
握手的過程?
從最開始雙方都處於CLOSED
狀態。
客戶端主動請求建立連線,傳送 SYN
到服務端 , 自己變成了SYN-SENT
狀態。
服務端接收到請求,針對客戶端的SYN
的確認應答,返回SYN
和ACK
(對應客戶端發來的SYN
),並請求建立連線,自己變成了SYN-REVD
。
客戶端收到服務端的請求,對服務端SYN
的確認應答,併發送ACK
給服務端,自己變成了ESTABLISHED
狀態;
服務端收到ACK
之後,也變成了ESTABLISHED
狀態。
另外需要提醒你注意的是,SYN
是需要消耗一個序列號的,下次傳送對應的 ACK
序列號要加1
,因為
凡是需要對端確認的,一定消耗TCP
報文的序列號。 。
為什麼是三次
不是四次
?
三次握手的目的是確認雙方傳送
和接收
的能力,那四次握手可以嘛?
當然可以,100
次都可以。但為了解決問題,三次就足夠了,再多用處就不大了。
三次
握手過程中可以攜帶資料麼?
首先說答案:第三次握手的時候,可以攜帶。前兩次握手不能攜帶資料。
如果前兩次握手能夠攜帶資料,那麼一旦有人想攻擊伺服器,那麼他只需要在第一次握手中的 SYN
報文中放大量資料,那麼伺服器勢必會消耗更多的時間和記憶體空間去處理這些資料,增大了伺服器被攻擊的風險。第三次握手的時候,客戶端已經處於ESTABLISHED
狀態,並且已經能夠確認伺服器的接收、傳送能力正常,這個時候相對安全了,可以攜帶資料。
3. 網路分層是 5
層還是 7
層?
OSI 七層參考模型:
物理層 -> 資料鏈路層 -> 網路層(Ip)-> 傳輸層(TCP)- > 會話層-> 表現層- > 應用層(http)頂層
我們所知道的還有 TCP/IP
四層模型和 TCP/IP
五層模型。這又是怎麼出來的,其實所謂的 TCP/IP
四層模型和 TCP/IP
五層模型是以 OSI
七層優化而來,把某些層進行合併了(會話層,表現層,應用層合併成應用層),其實本質上還是相同的,。
網際網路 5層協議模型:
物理層 -> 資料鏈路層 -> 網路層(Ip)-> 傳輸層(TCP)->應用層(http)
物理層 :用物理手段將電腦連線起來,就像我們講到的計算機之間的物理連線。主要用來傳輸0、1
訊號,所有我們用另一層用來規定不同0、1
組合的意義是什麼。
資料鏈路層 :在資料鏈路層規定一套協議,專門的給0、1
訊號進行分組,以及規定不同的組代表什麼意思,從而雙方計算機都能夠進行識別,這個協議就是"乙太網協議"
網路層 :網路層的由來是因為在資料鏈路層中我們說說兩臺計算機之間的通訊是分為同一子網路和不同子網路之間,那麼問題就來了,怎麼判斷兩臺計算機是否在同一子網路(區域網)中?這就是網路層要解決的問題。
傳輸層 :傳輸層的主要功能就是為了能夠實現"埠到埠"的通訊。計算機上執行的不同程式都會分配不同的埠,所以才能使得資料能夠正確的傳送給不同的應用程式。
應用層 :應用層的功能就是規定了應用程式的資料格式。我們經常用得到的電子郵件、HTTP
協議、以及FTP
資料的格式,就是在應用層定義的。
4. https
和http
有什麼區別?
https
不是 http
的對立面,兩者在本質上是相同的,都採用相同的“超文字傳輸協議”,使請求的資料能夠顯示在網站上。
安全性:
http
協議以明文方式傳送內容,不提供任何方式的資料加密。http
協議不適合傳輸一些敏感資訊,比如:信用卡號、密碼等支付資訊。https
則是具有安全性的SSL / TLS
加密傳輸協議,可防止通過 Internet
傳送的資料(使用者名稱,銀行卡密碼等)被第三方攔截和讀取。
連線方式:
http
和https
使用的是完全不同的連線方式,http
連線建立相對簡單, TCP
三次握手之後便可進行 HTTP
的報文傳輸。而 https
在 TCP
三次握手之後,還需進行 SSL/TLS
的握手過程,才可進入加密報文傳輸。用的埠也不一樣,前者是80
,後者是443
。
權威認證:
https
協議需要向 CA
(證書權威機構)申請數字證書,來保證伺服器的身份是可信的;http
則不需要。
總結: HTTPS
相對複雜,擁有權威認證,也更安全,也是以後網站的普遍模式。HTTPS
協議的主要作用可以分為兩種:一種是建立一個資訊保安通道,來保證資料傳輸的安全;另一種就是確認網站的真實性。
5. http
中的Keep-Alive
有了解嗎?
Keep-Alive
是http
的一個頭部欄位Connection
中的一個值,它是保證我們的http
請求能建立一個持久連線。也就是說建立一次TCP
連線即可進行多次請求和響應的互動。它的特點就是隻要有一方沒有明確提出斷開連線,則保持TCP
連線狀態 ,減少TCP
連線和斷開造成的額外開銷。
HTTP/1.0
階段:
在HTTP/1.0
中所有的連線預設都是關閉
的,如果客戶端瀏覽器支援Keep-Alive
,那麼就在HTTP
請求頭中新增一個欄位Connection: Keep-Alive
,當伺服器收到附帶有Connection: Keep-Alive
的請求時,它也會在響應頭中新增一個同樣的欄位來使用Keep-Alive
。這樣一來,客戶端和伺服器之間的HTTP
連線就會被保持,不會斷開(超過Keep-Alive
規定的時間,意外斷電等情況除外),當客戶端傳送另外一個請求時,就使用這條已經建立的連線。
HTTP/1.1
階段
在HTTP/1.1
中所有的連線預設都是持久連線
的。除非在請求頭或響應頭中指明要關閉:Connection: Close
,這也就是為什麼Connection: Keep-Alive
欄位再沒有意義的原因。目前大部分瀏覽器都是用http1.1
協議,也就是說預設都會發起Keep-Alive
的連線請求了。
6. http1
和http2
的區別?
http2
採用二進位制,而http1
使用的是文字格式;
http2
是多路複用的,而且無阻塞,只需一個連線即可實現並行;http1
一個連線只能傳送一個請求;
http1
的header
帶有大量資訊,而且每次都要重複傳送;http2
使用encoder
來減少需要傳輸的header
大小,通訊雙方各自快取 一份header
資訊字典表,既避免重複傳輸,又提升了傳輸速度。
7. 什麼是websocket
?
Websocket
是h5
提供的一種在單個TCP
連線上進行全雙工通訊的協議,只需要伺服器和瀏覽器通過HTTP
協議進行握手之後,兩者之間就直接可以建立永續性的連線,可以雙向傳送或接收資訊,並且允許伺服器主動向客戶端推送資料 ,適合資料需要及時重新整理的場景。
websocket
特性:
效能高,http
是基於文字,websocket
是二進位制
雙向通訊
在建立連線時還需要http
來通訊,
天生支援跨域
天然支援加密,安全
如果斷開會自動重連
websocket
封裝庫 socket.io
我們知道原生的websocket
寫起來是非常費腦細胞的,所以推薦一個庫,socket.io
,使用起來較為方便,並且它支援ie5
,而且是跨域自動解析資料,非常非常推薦!!!!
8. websocket
與http
有什麼區別?
相同點:
都是一樣基於TCP
的,都是可靠性傳輸協議。
都是應用層協議。
區別:
WebSocket
是雙向通訊協議,可以雙向傳送或接收資訊;而HTTP
是單向的,只能由客戶端發起請求到服務端去請求資料,服務端只能作為被動方;
http
是短連線,請求之後都會關閉連線,下次請求需要重新開啟連線;websocket
是長連線,只需要通過一次請求來初始化連線,後面就可以進行雙向資料通訊
WebSocket
連線的過程:
客戶端發起http
請求,經過3次
握手後,建立起TCP
連線;
http
請求裡存放WebSocket
支援的版本號等資訊,如:Upgrade、Connection、WebSocket-Version
等;
伺服器收到客戶端的握手請求後,同樣採用http
協議回饋資料;
客戶端收到連線成功的訊息後,開始藉助於TCP
傳輸通道進行全雙工通訊。
9. 什麼是http
快取?
http
快取指的是: 當客戶端向伺服器請求資源時,會先抵達瀏覽器快取,如果瀏覽器有要請求資源的副本
,就可以直接從瀏覽器快取中提取而不是從原始伺服器中提取這個資源。
常見的http
快取只能快取get
請求響應的資源,對於其他型別的響應則無能為力,所以後續說的請求快取都是指get
請求
分類:
根據是否需要再請求:可分為強制快取和協商快取 ,強制快取如果生效,不需要再和伺服器發生互動,而協商快取不管是否生效,都需要與服務端發生互動;
根據是否可以被單個或者多個使用者使用來分類,可分為私有快取和共享快取
配置:在請求的headers
中新增
cache-control: max-age=30 pragma:no-cache
如不想被快取,則配置:
cache-control: no-store
10. 為什麼要使用 http
快取?
減少了冗餘的資料傳輸,節省了網費。
緩解了伺服器的壓力, 大大提高了網站的效能
加快了客戶端載入網頁的速度
11. 都有哪些本地儲存的方案?
前端快取一般使用較多的有:localStorage、 sessionStorage、 cookie、indexdb
區別:
- localStorage : HTML5
新特性,只要在相同的協議、相同的主機名、相同的埠下,就能讀取/修改同一份localStorage
資料,是永久儲存,除非手動刪除,大小5M
左右,IE8
以上,不能被爬蟲抓取到
- sessionStorage : HTML5
新特性,比localStorage
更嚴苛一點,除了協議、主機名、埠外,還要求在同一視窗,也就是瀏覽器的標籤,僅僅是會話級別的儲存 ,這些資料只有在同一個會話中的頁面才能訪問並且當會話結束後資料也隨之銷燬。
- cookie : 儲存使用者登入狀態的資料會在每一次傳送http
請求的時候,同時傳送給伺服器 ,而localStoage
和sessionStorage
不會,最小,最大4KB
,可設定過期時間 ,預設當瀏覽器關閉程序的時候自動銷燬。
- indexdb : iE10
以上,比較適合鍵值對較多的資料,如果資料結構比較複雜,同時對瀏覽器相容性沒什麼要求,儲存量最大50M
。
二、HTML
相關
1. 淺談前端工程化介紹:模組化、元件化、規範化、自動化
前端工程化:指使用軟體工程的技術與方法對前端開發的技術、工具、流程、經驗、方案等指標標準化
,它具備模組化 、元件化 、規範化 、自動化 四大特性,主要目的是降低成本
與增加效率
。
模組化:是指在檔案層面上對程式碼與資源實現拆分與組裝,將一個大檔案拆分為互相依賴的小檔案,再統一拼裝與載入。各個模組功能獨立,分模組來維護,組合方式更靈活,多人協作也互不干擾。例如:介面模組、資源模組、路由模組等。
元件化:是指在功能開發場景中,將具備通用功能的互動設計劃分為模板、樣式和邏輯組成的功能單元,是具體某個功能的封裝,實現了程式碼更高層次的複用性,提升開發效率。元件的封裝也是物件的封裝,同樣要做到高內聚低耦合 ,例如分頁器、table
表格、form
表單等。
規範化:將一系列預設規範接入工程各個階段,通過各項指標標準化開發者的工作流程,為每個開發者指明一個方向,引領著成員往該方向走。例如:eslint、stylelint、pre-commit
等,拉齊程式碼標準,形成規範底線,方便不同人員等交叉維護。
自動化:指將一系列繁瑣重複的工作流程交由程式根據預設指令碼自動處理,常見自動化
場景包括但不限於自動化構建
、自動化測試
、自動化打包
、自動化釋出
和自動化部署
等。在保證效率的同時,又解放了雙手。
總結:前端工程化
不是某個具體的工具,而是對專案的整體架構與整體規劃 ,使開發者能在未來可判斷時間內動態規劃發展走向,以提升整個專案對使用者的服務週期。最終的目的是從手動處理流程全部替換為自動處理流程 ,以解放團隊雙手,讓其他成員更專注於自身業務需求 。
參考掘金小冊:《從 0 到 1 落地前端工程化》
關於git
程式碼提交規範
關於程式碼提交規範,我認為是有必要去統一的,在日常開發中經常遇到一些千奇百怪的提交說明,例如中英文混合使用、各種不規範的英文單詞等。這讓Review
程式碼的人會經常搞不清它們到底是幹嘛的,導致後續程式碼維護成本巨大。
我們不能只注重編碼而不考慮程式碼質量與提交質量。Angular團隊 制定的提交規範
是目前市面上公認為最合理、最系統、最流行的提交規範
。
Angular提交規範
的格式包括Header
、Body
和Footer
三個內容。Header
為必填項,Body
與Footer
為可預設項,這些內容通過以下結構組成一個完整的提交格式。
```
():
空一行
# 空一行
```
##### Header
該部分僅書寫一行,包括三個欄位,分別是`type`、`scope`和`subject`。
- **type**:用於說明`commit`的提交型別,`必選`
- **scope**:用於說明`commit`的影響範圍,可選
- **subject**:用於說明`commit`的細節描述,可選
`type`用於說明`commit`的提交型別,包括以下選項,相信這些選項已滿足日常`95%`的應用場景。當然這些選項無需刻意記憶,我會引入命令自動完成這些提交工作。
型別 | 功能 | 描述 |
| :----------: | -- | ----------------- |
| **feat** | 功能 | 新增功能,迭代專案需求 |
| **fix** | 修復 | 修復缺陷,修復上一版本存在問題 |
| **docs** | 文件 | 更新文件,僅修改文件不修改程式碼 |
| **style** | 樣式 | 變動格式,不影響程式碼邏輯 |
| **refactor** | 重構 | 重構程式碼,非新增功能也非修復缺陷 |
| **perf** | 效能 | 優化效能,提高程式碼執行效能 |
| **test** | 測試 | 新增測試,追加測試用例驗證程式碼 |
| **build** | 構建 | 更新構建,改動構建工具或外部依賴 |
| **ci** | 指令碼 | 更新指令碼,改動CI或執行指令碼配置 |
| **chore** | 事務 | 變動事務,改動其他不影響程式碼的事務 |
| **revert** | 回滾 | 回滾版本,撤銷某次程式碼提交 |
| **merge** | 合併 | 合併分支,合併分支程式碼到其他分支 |
| **sync** | 同步 | 同步分支,同步分支程式碼到其他分支 |
| **impr** | 改進 | 改進功能,升級當前功能模組
`scope`用於說明`commit`的影響範圍。簡要說明本次改動的影響範圍,例如根據功能可劃分為`資料層`、`檢視層`和`控制層`,根據互動可劃分為`元件`、`佈局`、`流程`、`檢視`和`頁面`。從`Angular`以往的提交說明來看,還是建議你在提交時對`scope`補全。
`subject`用於說明`commit`的細節描述。文字一定要精簡精煉,無需太多備註,因為`Body`部分可備註更多細節,同時儘量遵循以下規則。
- 以動詞開頭
- 使用第一人稱現在時
- 首個字母不能大寫
- 結尾不能存在句號(`.`)
##### Body
該部分可書寫多行,對`subject`做更詳盡的描述,內容應包括`改動動機`與`改動前後對比`。
##### Footer
該部分只適用兩種情況,分別是`不相容變動`與`問題關閉`。
- **不相容變動**:當前程式碼與上一版本不相容,則以`BREAKING CHANGE`開頭,關聯`變動描述`、`變動理由`和`遷移方法`
- **問題關閉**:當前程式碼已修復某些`Issue`,則以`Closes`開頭,關聯目標`Issue`
示例一個常規提交:
```
// 型別:修復問題,影響範圍:qa 環境,處理細節:全域性切換主題開關無效 關聯bug: #87697
fix(qa):invalid global theme color switch (#87679)
```
[參考掘金小冊:《從 0 到 1 落地前端工程化》 ](https://juejin.cn/book/7034689774719860739?enter_from=course_center)
---
#### 2. `html5` 的新特性
語義化標籤: ` `
新增的表單屬性: ` placehoder 、 required 、 pattern、 min/max 、 autofocus、 mulitiple`
音視訊標籤: `、 `
繪圖標籤:``
儲存: `sessionStorage 、 localStorage`
通訊: `webSocket`
---
**語義化標籤有什麼作用?**
- 比較利於開發人員閱讀,結構清晰明瞭;
- 利於`SEO`搜尋引擎優化,搜尋引擎也要分析我們的網頁,可以很方便的尋找出網頁的重點部分,排名靠前;
- 有利於特殊終端的閱讀(盲人閱讀器)。
---
#### 3. 關於頁面白屏如何排查原因 ?
- 檢查控制檯是否有報錯;
- 檢查網路是否正常,看`network`資源載入情況,傳輸資料量是否過大;
- 在其他電腦開啟是否一樣白屏;
- 看`dom`結構,是否正常載入,是否是靜態資源載入失敗;
- 更換瀏覽器,檢查是否是瀏覽器相容問題。
---
#### 4. 前端優化常用的方法有哪些 ?
- 路由、元件、圖片等懶載入,雪碧圖;
- 減少`http`請求 :節流、防抖、快取(`keep-alive`);
- `webpack` :打包壓縮、`Loader` 、外掛;
- 使用常量,避免全域性變數;
- 減少重繪和迴流,減少`table` 表格佈局,減少`dom` 操作,`html` 層級巢狀不要太深;
- 及時消除物件引用,清除定時器,清除事件監聽器;
- 使用`ssr` 預渲染。
---
### 三、`CSS` 相關
#### 1. `css` 盒模型:
`W3C`盒模型,又名標準盒模型,元素的寬高大小表現為內容的大小:
```
box-sizing:content-box
```
`IE` 盒模型,又名怪異盒模型,元素的寬高表現為內容 + 內邊距 + 邊框:
```
box-sizing:border-box
```
---
#### 2. 什麼是`BFC`?
> `BFC`全稱 `Block formatting context`(塊級格式化上下文)。是web 頁面中盒模型佈局的css 渲染模式,是一個獨立的渲染區域或一個隔離的獨立容器。
**特性:**
- 塊級元素,內部一個一個垂直排列
- 垂直方向的距離由兩個元素中`margin` 的較大值決定
- `bfc` 區域不會與浮動的容器發生重疊
- 計算元素的高度時,浮動元素也會參與計算
- `bfc` 容器中,子元素不會影響外邊元素
- 屬於同一個`bfc`的兩個相鄰元素的外邊距發生重疊,
**怎樣使一個元素變成`BFC`區域**
> 可以通過設定css 屬性來實現
- 設定浮動`float`但不包括`none`
- 設定定位,`absoulte`或者`fixed`
- 行內塊顯示模式,設定`display`為`inline-block`
- 設定`overflow`為`hidden`、`auto`、`scroll`
- 彈性佈局,`flex`
**`BFC`解決了哪些問題:**
- 阻止元素被浮動元素覆蓋
- 可以利用`BFC`解決兩個相鄰元素的上下`margin`重疊問題;
- 可以利用`BFC`解決高度塌陷問題;
- 可以利用`BFC`實現多欄佈局(兩欄、三欄、聖盃、雙飛翼等)。
---
#### 3. `CSS3` 新特性總結
- 選擇器: `E:last-child 、E:nth-child(n)、E:nth-last-child(n)`
- 邊框特性:支援圓角、多層邊框、彩色和圖片邊框
- 背景圖增加多個屬性:`background-image、background-repeat、background-size、background-position、background-origin和background-clip`
- 支援多種顏色模式和不透明度:加了`HSL、HSLA、RGBA` 和不透明度 `opacity`
- 支援過渡與動畫: `transition` 、`animation`
- 引入媒體查詢:`mediaqueries` ,支援為不同解析度的裝置設定不同的樣式
- 增加陰影:文字陰影:`text-shadow`,盒子陰影:`box-shadow`
- 彈性盒模型佈局:新的佈局方式,用於建立具有多行和多列的靈活介面佈局。
---
#### 4. 怎麼用`css`實現 `品` 字形佈局?
**滿屏品字形**
html:
```
1
2
3
```
css:
```
.div1{
width:100%;
height:200px;
margin:auto;
background:red;
}
.div2{
width:50%;
height:200px;
float:left;
background:green;
}
.div3{
width:50%;
height:200px;
float:left;
background:blue;
}
```

**固定寬高品字形:**
html:
```
1
```
css:
```
.div1{
width:200px;
height:200px;
background:red;
margin:0 auto;
}
.div4{
border:1px solid red;
display: flex;
justify-content: center;
}
.div2{
width:200px;
height: 200px;
background:green;
}
.div3{
width:200px;
height: 200px;
background:blue;
}
```

---
#### 5. `flex`怎麼實現一部分固定高度,一部分自適應?
> 下面就列舉幾個常用場景
**左側固定,右側自適應:**
html:
```
```
css:
```
*{
margin:0;
padding:0;
}
.box{
display: flex;
width: 100%;
height: 500px;
}
.div1{
width: 200px;
height:100%;
background:red;
}
.div2{
height:100%;
flex:1;
background:green;
}
```

**左右固定,中間自適應:**
html:
```
```
css:
```
*{
margin:0;
padding:0;
}
.box{
display: flex;
height: 200px;
width: 100%;
}
.div1{
width: 200px;
height: 100%;
background:red;
}
.div2{
height:100%;
flex:1;
background:green;
}
.div3{
width: 200px;
height: 100%;
background:blue;
}
```

**頂部固定,底部自適應:**
html:
```
```
css:
```
*{
margin:0;
padding:0;
}
.box{
display: flex;
min-height: 100vh;
width: 100%;
flex-direction: column;
}
.div1{
flex:0 0 100px;
background:red;
}
.div2{
height: 100px;
flex: auto;
background:green;
}
```

**頂部和底部固定高度,中間自適應**
html:
```
```
css:
```
*{
margin:0;
padding:0;
}
.box{
display: flex;
min-height: 100vh;
width: 100%;
flex-direction: column;
}
.div1{
flex:0 0 100px;
background:red;
}
.div2{
height: 100px;
flex: auto;
background:green;
}
.div3{
flex:0 0 200px;
background:blue;
}
```

#### 6. 居中的佈局
> 在`css`面試題中,關於 `水平 + 垂直居中` 佈局這個問題,可能是我在前端面試中被問到最多的,哈哈 那我們就來實現一下吧!
**html**
```
```
**第一種:(`flex`最方便,有相容性問題:ie 8 以下不支援)**
```
.box{
width:500px;
height: 500px;
background-color: yellow;
display: flex;
justify-content: center;
align-items: center;
}
.center{
width:200px;
height: 200px;
background-color: blue;
}
```
**第二種:(四個方向拉扯)**
```
.box{
width:500px;
height: 500px;
background-color: yellow;
position: relative;
}
.center{
width:200px;
height: 200px;
background-color: blue;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
```
**第三種:(使用計算屬性`calc`)**
```
.box{
width:500px;
height: 500px;
background-color: yellow;
position: relative;
}
.center{
width:200px;
height: 200px;
background-color: blue;
position: absolute;
left: calc(50% - 100px);
top: calc(50% - 100px);
}
```
**第四種:(使用轉換屬性`transform`)**
```
.box{
width:500px;
height: 500px;
background-color: yellow;
position: relative;
}
.center{
width:200px;
height: 200px;
background-color: blue;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
```
**第五種:(定位 + `margin-left:盒子的一半`)**
```
.box{
width:500px;
height: 500px;
background-color: yellow;
position: relative;
}
.center{
width:200px;
height: 200px;
background-color: blue;
position: absolute;
left: 50%;
top: 50%;
margin-left: -100px;
margin-top: -100px;
}
```
統一效果如下:
---
### 四、`JS` 相關
#### 1. 節流和防抖實現及其區別
- 防抖,是指觸發事件後在 `n` 秒內函式只能執行一次,如果在 `n` 秒內再次觸發,則會重新計算函式執行時間,即重新計時,每次觸發事件時都取消之前的延時呼叫方法,這樣一來,只有最後一次操作能被觸發。
某段時間內只執行一次
例如:輸入搜尋,在`400`毫秒只能執行一次,如果又輸入的搜尋內容,則重新開始計算`400`秒
- 節流,就是指連續觸發事件但是在 `n`秒中只執行一次函式。節流會稀釋函式的執行頻率。每次觸發事件時都判斷當前是否有等待執行的延時函式,若沒到規定時間則使用計時器延後,而下一次事件則會重新設定計時器。
間隔時間執行,不管事件觸發有多頻繁,都會保證在規定時間內一定會執行一次真正的事件處理函式
例如:點選事件連續點選了`n`次,但在`400`毫秒內只能執行一次
---
#### 2. `dom` 事件流是什麼,如何阻止事件捕獲
[見連結](https://juejin.cn/post/6844903518902583310)
---
#### 3. `js`的`es`系列的新語法有哪些?
> 包括但不限於`es6、7、8、9`....,它最大的特性就是:方便開發,提高工程性,開發效率高,更不容易犯錯誤
關於`es6`以上新特性相容性: 大部分的新特性`ie`瀏覽器不支援,如需支援,可使用`babel`線上編譯
配置:
```
script type='text/babel'
```
---
- **Number.isSafeInteger(): 是否在安全數內**
> 檢測數字是否在安全數內,返回布林值
```
Number.isSafeInteger(234334343434343); // true
Number.isSafeInteger(999999999999999999); // false
```
- **Number.isNaN(): 檢查是否為 NaN**
> 結果返回布林值。與`isNaN`的區別是,`Number.isNaN`會先檢測引數是否為`Number` 型別,如果不是直接返回`false`,只有當引數為`Number`型別,才會再去判斷是不是`NaN`。
```
Number.isNaN(NaN) // true
Number.isNaN(1) // false
Number.isNaN(true) // false
Number.isNaN(undefined) // false
Number.isNaN({}) // false
Number.isNaN("abc") // false
Number.isNaN("") // false
```
- **Math.sign(): 返回一個數字的符號,指示數字是正數、負數還是零**
> 一共有 5 種返回值,分別是 **1, -1, 0, -0, NaN.** 代表的各是**正數,負數,正零,負零,NaN**。
```
Math.sign(3); // 1
Math.sign(-3); // -1
Math.sign("-3"); // -1
Math.sign(0); // 0
Math.sign(-0); // -0
Math.sign(NaN); // NaN
Math.sign("foo"); // NaN
Math.sign(); // NaN
```
- **Math.imul(): 兩數相乘**
> 返回兩個引數的類`C` 的`32` 位整數乘法運算的結果
```
Math.imul(2, 4); // 8
Math.imul(-1, 8); // -8
Math.imul(-2, -2); // 4
Math.imul(0xffffffff, 5); // -5
```
- **允許頂層 await**
> 之前我們在寫`await` 的時候,一定要在前面加一個`async`,現在我們支援在頂層直接使用。`Top await` 本身就是`esModule` 裡的規範,所以需要在`script` 里加上 `type=“module”`
- **at(-1)可以直接拿到陣列或者字串的最後一位元素**
>支援陣列和字串,再也不用使用這種冗餘的寫法了 `arr[arr.length-1]`
```
[2,3,5].at(-1); // 5
'aazvxcvher'.at(-1); // r
```
- **Object.hasOwn**
> 代替 Object.prototype.hasOwnProperty.call
```
// 原來
Object.prototype.hasOwnProperty.call(obj, 'name');
// 現在
Object.hasOwn(obj, 'name');
```
- **允許 JavaScript 的數值使用下劃線(`_`)作為分隔符**
> 可以間隔3位,也可以間隔兩位,看起來比較好讀
```
let budget = 1_000_000_000_000;
budget === 10 ** 12 // true
```
[其他新特性見連結](https://juejin.cn/post/7026696335575220238#heading-9)
---
#### 4. 關於 `ES6` 的模組化
**發展歷史:**
沒有模組 -> CMD(按需載入,載入數量並沒有減少) -> AMD(非同步載入模組) -> ES6 語言提供的模組化支援
**`export`:匯出的幾種方式**
```
// 匯出普通變數(邊定義邊匯出)
export let a=5;
// 匯出常量
export const a=6;
// 匯出函式
export function xx(){};
// 匯出類
export class xxx(){} ;
// 匯出預設成員
export default 'xxx';
// 從另一個模組匯出
export * from './xx';
// 匯出模組中的部分內容
export {xxx,xx,xx} from './xxx';
// 匯出模組中的default
export {default} from './xx';
```
**`import`:引入的幾種方式**
```
// 全部引入並重命名
import * as mod from '/xxx';
// 引入指定成員
import {a,b,c} from './xxx';
// 引入預設成員,配合 export default xxx;
import xxx from './xxx';
// 將模組的程式碼引入,不引入內部成員
import './x.jpg';
import './x.css';
// 非同步引入
let promise = import ('./xx');
```
---
#### 5. `Object.prototype.toString.call()` 、 `instanceof` 以及 `Array.isArray()`判斷陣列的方法,區別?
> 如果只是用來判斷陣列, `Array.isArray` 優於`instanceof`。
- **Object.prototype.toString.call():**
這種方法對於所有基本的資料型別都能進行判斷,即使是 `null` 和 `undefined` 。
- **instanceof**
`instanceof` 的內部機制是通過判斷物件的原型鏈中是不是能找到型別的 `prototype`。使用`instanceof`判斷一個物件是否為陣列,`instanceof` 會判斷這個物件的原型鏈上是否會找到對應的 `Array` 的原型,找到返回 `true`,否則返回 `false`。
- **Array.isArray():**
是`es6`新增的方法,可以檢測出 `iframes`,執行效率比較高,所以當真正需要檢測一個變數是不是陣列時,先會檢測瀏覽器是否支援`Array.isArray()`, 之後再用`Obejct.prototype.toString.call()`方法。
---
#### 5. `typescript` 中的`type` 和 `interface` 的區別 ?
> 一般情況下定義介面用`interface`,定義常量用`type`
**關於區別:**
- 都可以來描述物件和函式,實現繼承的方式不同,`interface` 是通過 `extends`, `type`是通過`&`;
- 可以定義相同名稱的`interface`,且會合並宣告成員,`type`不可以宣告同名的,會報錯;
- `type` 可以定義基本型別 / 聯合型別 / 元祖型別。
例:
```
type PartialPointX = { x: number };
type PartialPointY = { y: number };
// union(聯合)
type PartialPoint = PartialPointX | PartialPointY;
// tuple(元祖)
type Data = [PartialPointX, PartialPointY];
// primitive(原始值)
type Name = Number;
```
---
#### 6. `Object.assign()` 、 `Object.create()` 、 `Object.defineProperty` 分別的用處 ?
- **`Object.assign`**
> 方法用於將自身所有可列舉屬性的值從一個或多個源物件複製到目標物件,並且會返回一個新的目標物件。[參考連結](https://juejin.cn/post/6844903586451685389#heading-1)
- **`Object.create`**
>建立一個新物件,是把現有物件的屬性,掛到新建物件的原型上,第一個引數新增到原型上,第二個引數,為新增的可列舉屬性(即新增自身屬性,不是原型上的)
例:
**複製物件:**
```
var a = {name:'a'};
a.__proto__.lastName="b";
// 想要建立一個一模一樣的 a
b = Object.create(a.__proto__,Object.getOwnPropertyDescriptors(a))
console.log(b); // { name:'a' }
```
**`Object.create()`方法建立的物件時,屬性是在原型下面的**
```
var a={ name:'hh'};
var b = Object.create(a);
console.log(b); // {}
console.log(b.name); // 'hh'
```

> 當建立一個以另一個空物件為原型,第二個引數是新增到新建立的物件的可列舉屬性:
```
var a = Object.create({}, { p: { value: 19 } })
console.log(a); // {p: 19}
```
- **`Object.defineProperty`**
> 直接在一個物件上定義一個新屬性,或者修改一個物件現有的屬性,並返回這個物件。
```
const object1 = {};
Object.defineProperty(object1, 'property1', {
value: 42,
writable: false
});
object1.property1 = 77; // throws an error in strict mode 不允許被修改
console.log(object1.property1); // 42
```
**區別:**
- `Object.defineProperty` 用於給物件新增新屬性
- `Object.create` 用於將自身所有可列舉屬性的值從一個或多個源物件複製到目標物件,並且會返回一個新的目標物件
- `Object.create()`建立一個新物件,是把現有物件的屬性,掛到新建物件的原型上
---
#### 7. 什麼是`Object`屬性的可列舉性 ?
- 可列舉屬性是指那些內部 `可列舉`(`enumerable`)標誌設定為 `true` 的屬性
- 對於通過直接的賦值和屬性初始化的屬性,該標識值預設為 `true`
- 對於通過`Object.defineProperty`等定義的屬性,該標識值預設為 `false`
- 可列舉的屬性可以通過`for...in`迴圈進行遍歷。
---
#### 8. 淺拷貝和深拷貝 ?
- **淺拷貝** 如果屬性是基本型別,拷貝的就是基本型別的值,如果屬性是引用型別,拷貝的就是記憶體地址(新舊物件共享同一塊記憶體),所以如果其中一個物件改變了這個地址,就會影響到另一個物件。
- **深拷貝** 將一個物件從記憶體中完整的拷貝一份出來,從堆記憶體中開闢一個新的區域存放新物件(新舊物件不共享同一塊記憶體),且修改新物件不會影響原物件。
---
#### 9. 深拷貝的方法 ?
>深拷貝:將一個物件從記憶體中完整的拷貝一份出來,從堆記憶體中開闢一個新的區域存放新物件(新舊物件不共享同一塊記憶體),且修改新物件不會影響原物件
- **JSON.parse(JSON.stringify())**
```
let obj1 = {
name: 'Li',
age:18
}
let obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj1 === obj2); // false
```
**關於`JSON.parse(JSON.stringify())`的缺點**
1. `obj`裡面有`new Date()`,深拷貝後,時間會變成字串的形式。而不是時間物件;
2. `obj`裡有`RegExp`、`Error`物件,則序列化的結果會變成空物件`{}`;
3. `obj`裡有`function`,`undefined`,則序列化的結果會把`function`或 `undefined`丟失;
4. `obj`裡有`NaN`、`Infinity`和`-Infinity`,則序列化的結果會變成`null`;
5. `JSON.stringify()`只能序列化物件的可列舉的自有屬性,如果obj中的物件是由建構函式生成的例項物件, 深拷貝後,會丟棄物件的`constructor`;
[參考文章](https://blog.csdn.net/weixin_40016215/article/details/117700135)
---
- **社群的手寫方案:**
```
//函式拷貝
const copyObj = (obj = {}) => {
//變數先置空
let newobj = null;
//判斷是否需要繼續進行遞迴
if (typeof (obj) == 'object' && obj !== null) {
newobj = obj instanceof Array ? [] : {};
//進行下一層遞迴克隆
for (var i in obj) {
newobj[i] = copyObj(obj[i])
}
//如果不是物件直接賦值
} else newobj = obj;
return newobj;
}
```
- **lodash中的cloneDeep()**
```
import lodash from 'lodash';
let obj = {
a: {
c: 2,
d: [1, 9, 9, 2],
e:'張三'
},
b: 99
}
const newObj = lodash.cloneDeep(obj);
obj.b = 5;
console.log(newObj.b); // 99 不會被影響
```
---
#### 10. `var` 如何實現 `let` ?
**`var` 存在的問題**
- 可以重複宣告
- 沒有塊級作用域
- 不能限制修改
---
**普通的`var`使用:**
```
for(var i=1;i<4;i++){
setTimeout(function(){
console.log(i);
},1000);
}
```

**改造後的實現:**
```
for (var i = 1; i < 4; i++) {
(function f(a) {
setTimeout(function () {
console.log(a);
}, 1000);
})(i);
}
```

---
#### 11. 什麼是作用域 ?
> 作用域指程式中定義變數的區域,它決定了當前執行程式碼對變數的訪問許可權
在`js`中大部分情況下,分為以下三種作用域:
**全域性作用域**: 全域性作用域為程式的最外層作用域,一直存在。
**函式作用域**: 函式作用域只有函式被定義時才會建立,包含在父級作用域內。
**塊級作用域** : 所有語法塊`{}`都會形成獨立的塊級作用域,例如:`if 、for`。
>由於作用域的限制,每段獨立的執行程式碼塊只能訪問自己作用域和外層作用域中的變數,無法訪問到內層作用域的變數。`ES6` 標準提出了使用 `let` 和 `const` 代替 `var` 關鍵字,來“建立塊級作用域”。
```
/* 全域性作用域開始 */
var a = 1;
function func () {
/* func 函式作用域開始 */
var a = 2;
console.log(a);
}
/* func 函式作用域結束 */
func(); // => 2
console.log(a); // => 1
/* 全域性作用域結束 */
```
**除了全域性作用域,我們是不能在一個作用域中訪問其他作用域中的內容的。** 那麼如果我們需要獲取其他作用域中的變數,那怎麼辦?**閉包就這樣誕生來了**!
[參考文章](https://juejin.cn/post/6844904165672484871)
---
#### 12. 什麼是閉包?什麼場景使用?有什麼缺點?
> 官方解釋:閉包是指有權訪問另一個函式作用域中的變數的函式。例如:函式`a`可以拿到函式`b`內部作用域的變數。
**`閉包除了和作用域有關,也和垃圾回收的機制有關,正常的垃圾回收過程是:當一個函式執行時會給它分配空間,而當執行結束後會把空間收回。`**
##### **垃圾回收機制?**
>垃圾回收有兩種方法:標記清除、引用計數。引用計數不太常用,標記清除較為常用。
**標記清除**是`javascript`中最常用的垃圾回收方式。當變數進入執行環境時,就標記這個變數為`進入環境`。從邏輯上講,永遠不能釋放進入環境的變數所佔用的記憶體,因為只要執行流進入相應的環境,就可能會用到他們。當變數離開環境時,則將其標記為`離開環境`。
```
var m = 0,n = 19 // 把 m,n 標記為進入環境。
var a= m + n // 把 a 標記為進入環境。
console.log(n) // n 標記為離開環境,等待垃圾回收。
```
---
> 但是當一個區域性變數被另一個函式正在使用,那麼它就不會被回收,就是形成了閉包環境。
例如:
```
function foo() {
var a = 2;
document.onclick=()=>{
alert(a); // a 不會被回收
}
}
show();
```
>我們瞭解作用域之間是可以巢狀的,我們把這種巢狀關係稱為 **作用域鏈**。閉包的執行看起來像是開發者使用的一個小小的 “作弊手段” ——**繞過了作用域的監管機制,從外部也能獲取到內部作用域的資訊**。閉包的這一特性極大地豐富了開發人員的編碼方式,也提供了很多有效的運用場景。
##### **用途:**
1. 能快取作用域變數,私有化資料,避免全域性變數的汙染;
2. 利於程式碼封裝(防抖、節流,選項卡),呼叫方法後內部值不會互相影響
##### **缺點:**
一直存在記憶體中,有可能導致記憶體洩露(**不是一定**),所以在不需要用到的時候及時把變數設定為`null`。
---
#### 13. 什麼情況下會導致記憶體洩露 ?
**記憶體一般有三個階段的生命週期: 分配期 -> 使用期 -> 釋放期**
>程式的執行需要佔用記憶體,當這些程式沒有用到時,還不釋放記憶體,就會引起記憶體洩漏。而記憶體洩漏,會讓系統佔用極高的記憶體,讓系統變卡甚至崩潰。
- 意外的全域性變數
- 被遺忘的定時器和回撥函式(例如:未使用的閉包)
- 被遺忘的事件監聽器
- 無限迴圈的引用函式(死迴圈)
- `console`儲存大量資料在記憶體中
---
#### 14. 如何避免記憶體洩露 ?
> 其實總結一句話就是:儘早釋放無用物件的引用,下面是具體方法:
- 減少不必要的全域性變數,使用嚴格模式避免意外建立全域性變數(現在編輯器都會提示)。
- 在你使用完資料後,及時解除引用(閉包中的變數,`dom`引用,定時器清除)。
- 梳理清楚邏輯,避免死迴圈等造成瀏覽器卡頓,崩潰的問題。
- 避免過度使用閉包(儘量減少邏輯巢狀)。
---
#### 15. 陣列中各遍歷方法的返回值
- `filter` 返回一個判斷結果為`true`組成的陣列;
- `forEach` 沒有返回值;
- `map` 返回每次函式呼叫的結果組成的新陣列;
- `reduce` 迭代陣列所有項,然後構建一個最終返回的值;
- `some` 如果該函式任意一項返回`true`,則返回`true`;
- `every` 如果該函式對每一項都返回`true`,則返回`true`;
---
#### 16. `map` 和 `filter` 使用區別 ?
**`map`:**
作用是生成一個新陣列,遍歷原陣列,將每個元素拿出來做一些變換然後放入到新的陣列中。
```
[1, 2, 3].map(v => v + 1) // -> [2, 3, 4]
```
**`filter`**
作用也是生成一個新陣列,在遍歷陣列的時候將返回值為 `true` 的元素放入新陣列,我們可以利用這個函式刪除一些不需要的元素
```
let array = [1, 2, 4, 6];
let newArray = array.filter(item => item !== 6) ;
console.log(newArray) // [1, 2, 4]
```
---
#### 17. `Event loop`事件的迴圈機制
>在事件任務中分為微任務和巨集任務兩種
- 巨集任務:包括整體程式碼`script`,定時器:`setTimeout,setInterval`
- 微任務:`Promise.then`(非`new Promise`),`process.nextTick`(`node`中)
---
**`Event Loop` 執行順序如下所示:**
1. 首先執行同步程式碼,這屬於巨集任務
2. 當執行完所有同步程式碼後,執行棧為空,查詢是否有非同步程式碼需要執行,例如`promise.then`裡的回撥函式
3. 執行所有微任務
4. 當執行完所有微任務後,如有必要會渲染頁面
5. 然後開始下一輪 `Event Loop`,執行巨集任務中的非同步程式碼,也就是 `setTimeout` 中的回撥函式
---
#### 18. `setTimeout、Promise、Async/Await` 的區別
> 事件迴圈中分為巨集任務佇列和微任務佇列。
- `promise.then`裡的回撥函式會放到相應巨集任務的微任務佇列裡,等巨集任務裡面的同步程式碼執行完再執行
- `async`函式表示函式裡面可能會有非同步方法,`await`後面跟一個表示式,`async`方法執行時,遇到`await`會立即執行表示式,然後把表示式後面的程式碼放到微任務佇列裡,讓出執行棧讓同步程式碼先執行。
- `settimeout`的回撥函式放到巨集任務佇列裡,等到執行棧清空以後執行;(最後執行)
---
#### 24. `js` 非同步解決方案的發展歷程?
> **非同步操作**:多個操作可以一起進行,而互不干擾,**同步操作**:一次只能處理一個請求
1. 回撥函式:當巢狀層級過多,容易產生回撥地獄,強耦合,一旦有所改動,牽一髮而動全身,錯誤管理困難。
2. `Promise` 就是為了解決`callback`的問題而產生的,`Promise` 實現了鏈式呼叫,也就是說每次 `then `後返回的都是一個全新 `Promise`,如果我們在 `then` 中 `return` ,`return` 的結果會被 `Promise.resolve()` 包裝。讓`Promise` 大放異彩的是`Promise.all`。缺點:無法取消`promise`,如果有邏輯,不能解決,如果有錯誤需要通過回撥函式來捕獲。
3. `Generator` 可以控制函式的執行,可以有順序,可以中途停止,停止後再執行,寫起來相對麻煩。
4. `async`、`await` 是非同步的終極解決方案,當然也是語法糖,內部實現仍然是回撥巢狀回撥。優點是:程式碼清晰,不用像 `Promise` 寫一大堆 `then` 鏈,處理了回撥地獄的問題,用同步的書寫方式寫非同步。缺點:`await` 將非同步程式碼改造成同步程式碼,如果多個非同步操作沒有依賴性而使用 `await` 會導致效能上的降低。
---
#### 21. 什麼是`Promise`? 都有哪幾個狀態?
>`Promise` 是非同步程式設計的一種解決方案,是一種語法糖,可以將非同步操作以同步操作的流程表達出來,避免了層層巢狀的回撥函式,解決回撥地獄難題,一旦新建它就會立即執行,無法中途取消`Promise`。
>`Promise` 是一個物件,它代表了一個非同步操作的最終完成或者失敗。由於它的`then`方法和`catch`、`finally`方法會返回一個新的`Promise`所以可以允許我們鏈式呼叫,解決了傳統的回撥地獄問題。
**`Promise`有三種狀態:**
- `pending`,進行中。
- `resolved` (也可以叫`fulfilled`),已成功。
- `rejected`,已失敗。
**`Promise`物件的狀態改變,只有兩種可能:從`pending`變為`fulfilled`和從`pending`變為`rejected`,`then` 和 `catch` 都會返回一個新的`promise`**
**`then()`**
方法可以接收兩個回撥函式作為引數。第一個回撥函式是`Promise`物件的狀態變為`resolved`時呼叫,處理操作成功後的業務,第二個回撥函式是`Promise`物件的狀態變為`rejected`時呼叫,處理操作異常後的業務。
**`catch()`**
只接受一個引數,用於處理操作異常後的業務, 一般來說,不要在`then`方法裡面定義 `Reject` 狀態的回撥函式(即`then`的第二個引數),總是使用`catch`方法。這樣可以處理 `Promise` 內部發生的錯誤。`catch`方法返回的還是一個 `Promise` 物件,因此後面還可以接著呼叫`then`方法。
**`finally()`**
方法用於指定不管 `Promise` 物件最後狀態如何,都會執行的操作, 方法的回撥函式不接受任何引數, 這表明,`finally`方法裡面的操作,應該是與狀態無關的,不依賴於 `Promise` 的執行結果,最終也是返回一個`promise` 函式
**`all()`**
方法用於將多個 `Promise` 例項,包裝成一個新的 `Promise` 例項。`Promise.all`方法接收一個`陣列`作為引數,然後並行執行非同步任務,並且在所有非同步操作執行完後才執行回撥,一旦丟擲異常,也只有第一個丟擲的錯誤會被捕獲,但不影響其他非同步任務。
舉例:
`p1、p2、p3`都是` Promise` 例項
```
const p = Promise.all([p1, p2, p3]);
```
**`p`的狀態由`p1、p2、p3`決定,分成兩種情況。**
- 只有`p1、p2、p3`的狀態都變成`fulfilled`,`p`的狀態才會變成`fulfilled`,此時`p1、p2、p3`的返回值組成一個數組,傳遞給`p`的回撥函式。
- 只要`p1、p2、p3`之中有一個被`rejected`,`p`的狀態就變成`rejected`,此時第一個被`reject`的例項的返回值,會傳遞給`p`的回撥函式。
**`race()`**
方法返回一個 `promise`,一旦迭代器中的某個 `promise` 解決或拒絕,就會返回結果。`race`含有競速的意思,將多個`Promise`放在一個數組中,陣列中有一個`promise`最先得到結果,不管是" 完成(`resolved`)"還是" 失敗(`resolved`)" , 那麼這個 `race` 方法就會返回這個結果。
**`race()`跟`all()`的區別:**
`race()`根據第一個請求來返回結果,若第一個成功,全域性都成功,第一個失敗,全域性都失敗,返回結果的順序,時間快的先返回。`all()` 是全部成功,才會成功,只有一個失敗,就是失敗,成功返回的結果根據請求的引數的順序,無關請求的快慢。
---
#### 22. 事件委託/事件代理?
>事件委託是利用事件的`冒泡原理`來實現的
**何為事件冒泡呢?**
就是事件從最深的節點開始,然後逐步`向上傳播`事件
只指定一個事件處理程式,就可以管理某一型別的所有事件。註冊事件的話應該註冊在父節點上。假如我們要給`100`個`li`同時新增一樣的事件,那麼我們就需要和瀏覽器互動`100`次,如果要用事件委託,就會將所有的操作放到js程式裡面,只對它的父級(如果只有一個父級)這一個物件進行操作,與`dom`的操作就只需要互動一次,這樣就能大大的減少與`dom`的互動次數,效率高,不用`for`迴圈去新增事件,提高效能。
---
#### 23. 什麼是跨域?
> 跨域的產生來源於現代瀏覽器所通用的同源策略限制,當一個請求`url`的協議、域名、埠三者均一樣的情況下,才允許訪問相同的`cookie、localStrage`,以及訪問頁面`dom`或者傳送`ajax`請求。原因是為了保證使用者資訊保安,防止惡意網站竊取資料。
同源:
```
http://www.example.com:8080/index.html
http://www.example.com:8080/home.html
```
跨域:
```
http://www.example.com:8080/index.html
http://www3.example.com:8080/index.html
```
但是還有兩種情況,http預設的埠為80,https預設的埠號為443,所以以下不是跨域:
```
http://www.example.com:80 === http://www.example.com
https://www.example.com:443 === https://www.example.com
```
---
**如何解決跨域 ?**
>其實跨域請求產生時,請求是發出去了,也是有響應的,只是因為瀏覽器同源策略,它認為此時是不安全的,攔截了我們的返回結果,不將資料傳遞我們使用罷了。
---
- **CORS 全稱是"跨域資源共享"(Cross-origin resource sharing)**
> `CORS`支援所有型別的`HTTP`請求,是跨域http請求的根本解決方案。
後端來處理,修改響應頭,允許任何網站訪問:
```
'Access-Control-Allow-Origin': '*',
```
- **jsonp**
>jsonp是利用`
```
實現步驟:
1. 宣告一個回撥函式,用於接收資料;
2. 建立一個`script` 標籤,把`script`的`scr` 賦值為跨域的`api` 介面地址;
3. 將回調函式的名字當引數掛在scr上,告訴對方我的回撥函式的名稱,用於執行完成後來呼叫我的函式
配合`jquery`來實現:
```
...
$.ajax({
url:"http://sp0.baidu.com.23d8s8dfj923kjs7823jshdsdf/su",
data:{wd:'qq'},
dataType:"jsonp",
jsonp:"cb", // 告訴它回撥函式的名字,以便於它生成一個函式來給我們用
}).then(({s})=>{
console.log(s);
},res=>{
console.log('失敗')
);
...
```
---
- **websocket**
>跨域問題的產生無非就是去伺服器上獲取資料;而`websocket`是和伺服器建立了雙工連線,連線只要建立了,就能通訊了,既然能通訊,那我想要什麼資料跟後臺說,後臺再發送給前端資料,這樣利用通訊就不會有跨域的問題啦。這種方式是通過建立長連結來請求,相比普通請求會比較耗費效能!
`Websocket` 支援跨域,因`websocket`原生`api`用起來不太方便,推薦使用`socket.io`的庫,使用過`node.js`配合前端。
---
- **webpack**
> 通過代理的方式,一般用於開發環境
```
module.exports = {
devServer: {
proxy: {
"/api": {
target: "http://localhost:3000",
pathRewrite: {"/api": ""} // 將/api替換掉
}
}
}
}
```
訪問 `http://localhost:8080/api/test `就會被代理到 `http://localhost:3000/test` 上,而如果`JSESSIONID`設定了`httpOnly`不能重寫`cookie`到`target`,故無效
[參考文章](https://juejin.cn/post/6844903767226351623)
---
#### 24. 關於重繪和重排 ?
- 重繪:某些元素的外觀被改變,例如:元素的填充顏色
- 重排:區域性或者整體佈局發生改變,需要重新生成佈局,重新排列元素。
**重繪不一定導致重排,但重排一定會導致重繪**。重排的開銷要遠遠大於重繪,會破壞使用者體驗,並且讓UI展示非常遲。所以我們要儘量減少頁面重排次數。
**如何減少重排:**
- 脫離文件流,使用`position:absolute 、fixed`
- 避免頻繁操作樣式
- 動畫樣式啟用GPU加速
- 減少重排範圍,儘可能將樣式加在具體元素上,而不是它的父級
[參考文章](https://juejin.cn/post/6844904083212468238)
---
#### 25. 程序和執行緒的區別 ?
> 一個程式至少有一個程序,第一個叫主程序,其餘都是子程序,一個程序可以包含多個執行緒。他們是包含的關係。
```mermaid
stateDiagram-v2
程式 --> 程序1
程式 --> 程序2
程序1 --> 執行緒1
程序1 --> 執行緒2
程序2 --> 執行緒3
程序2 --> 執行緒4
```
1. 執行緒的效能高,但安全性低,同一個程序之內的執行緒之間共享記憶體,共享計數器。
2. 進的效能低,但安全性高,每個程序有自己獨立的記憶體,獨立的計數器,程序和程序之間一般是不共享資料的;
3. 程序要分配一大部分的記憶體,而執行緒只需要分配一部分棧就可以了。
4. 程序是資源分配的最小單位,執行緒是程式執行的最小單位。
5. 一個執行緒可以建立和撤銷另一個執行緒,同一個程序中的多個執行緒之間可以併發執行
`nodejs`和`javascript`是多程序語言
建立程序:
```
js: new Worker()
node:cluster.for()
```
[參考文章](https://juejin.cn/post/6844903801321685000)
---
### 六、`React` 相關
#### 1. `vue` 和 `react` 的區別
[見連結](https://juejin.cn/post/7026696335575220238#heading-40)
---
#### 2. 如何理解受控元件和非受控元件?
>`React` 中受控和非受控的概念通常跟 `form` 表單元件相關,比如 `input` ,通過區分與 `input` 進行資料互動的方式,元件被分成兩個不同的派系,受控與非受控。
- 受控元件:
`React` 中受控元件的是指表單元素的控制是交給 `React` ,表單元素的值是完全交由元件的 `state` 控制。
- 非受控元件
非受控元件指表單元素的狀態並不受 `React` 元件狀態的影響,表單元素的值儲存於 `DOM` 元素中。如果要 `React` 元件要獲取 `DOM` 元素的值,需要通過繫結 `ref` 的方式去獲取。
---
#### 3. `React` 解決了前端開發中的哪些痛點?
- 元件化開發,一切皆元件,提升程式碼重用率;
- `jsx`的書寫方式,更符合趨勢,提高開發效率;
- 虛擬`DOM`解決了以往需要大量操作`DOM`帶來的效能問題 。
---
#### 4. `react`的自定義`hooks`怎麼實現?
[自定義hooks](https://juejin.cn/post/6844904038668976142#heading-35)
---
#### 5. `useMemo`的使用方式?
[詳見連結](https://juejin.cn/post/6844904038668976142#heading-34)
---
#### 6. `React`中`Fiber` 機制怎麼理解
[詳見連結](https://juejin.cn/post/7026696335575220238#heading-59)
---
#### 7. `React-hooks` 原理
[詳見連結](https://juejin.cn/post/7026696335575220238#heading-78)
---
#### 8. `UseState` 和 `this.setState` 對比有什麼區別?
>這兩個是`react` 兩種元件書寫方式中的狀態管理。`useState` 是使用在函式元件中的,`setState` 是用在類元件中
在更新之前有區別,`useState`有優化策略,如果你上次的`value`和下一次的一樣,會觸發`eagerState`策略,不會走到更新。`this.setState`則不會處理,會走到`scheduleUpdateOnFiber`。這也是他們在觸發時最大的不同。
---
### 七、其他業務層面
#### 1. 建立一個元件庫,除基本元件外,應該關注的點有哪些?元件釋出時,關注的點有?
- 文件:保持和程式碼同步的更新頻率,同步最新更新的記錄到文件中,設定一個主要負責人,定期審查;
- 更新規則:非`bug fix`的問題,提前收集整理多方需求,確認上線時間要求,合理安排排期,最多一迭代一更新;
- 測試:在測試資源充足的情況下,安排測試人員對元件進行測試;如無測試資源,保證元件功能說明文件詳盡,待業務提測後一同進行測試。
#### 2. 按鈕級別的許可權怎麼做 ?
**首先將許可權分層來管理**
例如:選單許可權、模組許可權、按鈕許可權。再將其進行分層判斷,如果沒有選單許可權,即不會再進行模組和按鈕許可權判斷。一定程度上避免一些bug 產生。
**關於具體的按鈕許可權:**
可以通過配置來實現,將按鈕做成字典表,然後實現一個鑑權的函式,通過判斷當前角色是否有許可權來顯示按鈕,當角色沒有許可權時,不顯示操作許可權。
---
### 演算法
#### 1. 實現找到最大字元並輸出其個數
```
const array1 =[1,3,5,6,8,8,8];
function foo(arr) {
const maximum = Math.max(...arr);
const maximumArr = arr.filter(function(item,index){
return item === maximum;
});
return {maximum, "length":maximumArr.length}
}
```
列印結果:

---
#### 2. 陣列去重
去重的方法不止一種,下面推薦幾個比較常用的方法:
使用`es6`新增的方法:[new Set](https://juejin.cn/post/6844903555682271245#heading-19)
使用filter 方法: [filter](https://juejin.cn/post/6844903555682271245#heading-20)
使用 includes : [includes](https://juejin.cn/post/6844903555682271245#heading-21)
---
#### 3. 氣泡排序
第一種:
```
let arr = [10, 9, 7, 5, 7, 8, 9, 3, 2, 0];
let len = arr.length;
for (let j = 0; j < len - 1; j++){
for (let i = 0; i < len - 1 - j; i++){
if (arr[i]>arr[i+1]) {
[arr[i + 1], arr[i]] = [arr[i], arr[i + 1]];
}
}
}
// [0, 2, 3, 5, 7, 7, 8, 9, 9, 10]
```
第二種:
```
let arr = [10, 9, 7, 5, 7, 8, 9, 3, 2, 0];
let len = arr.length;
for (var i = 0; i < len; i++){
for (var j = i + 1; j < len; j++){
if (arr[i] > arr[j]) {
[arr[i],arr[j]]=[arr[j],arr[i]]
}
}
}
// [0, 2, 3, 5, 7, 7, 8, 9, 9, 10]
```
#### 4. 展開多維陣列
**最簡潔:**
```
let arr = [1, 2, 4, 6,[5, 4, 5,[98, 3], [34], [7]]];
arr.toString().split(',').map(Number);
```
步驟解析:

---
**使用遞迴來處理:**
```
let arr = [1, 2, 4, 6,[5, 4, 5,[98, 3], [34], [7]]];
function f(arr=[],newArr=[]) {
arr.forEach((item) => {
if (!Array.isArray(item)) {
newArr.push(item);
} else {
f(item, newArr);
}
});
return newArr;
}
f(arr);
```
列印結果:

---
### 八、`flutter` 相關
> 關於`flutter`相關面試題,網上資源是比較少的,而且偏向極端化,就是上來就問各種實現原理,哈哈哈,參考的資料比較少,所以下面這些題一些是源於社群,另一部分是我自己的猜測。另一方面就是面試`flutter `的有一半以上是做客戶端的,有安卓或者`ios` 開發經驗,所以我也只能從前端角度去選取一些題作為參考,後面如果面試中有問題,我會更新到文件中。
[Dart語法知識點看這裡](https://juejin.cn/post/7098649277827645448)
#### 1. 什麼是`flutter` ?
>`Flutter` 是 `Google` 推出並開源的移動應用開發框架,主打跨平臺、高保真、高效能。開發者可以通過 `Dart` 語言開發 `App`,一套程式碼同時執行在 `iOS` 和 `Android`平臺。在`2022年5月12日`中,`Flutter 3.0`正式釋出。當前最新的`Flutter3.0`提供了對`macOS`和`Linux`桌面應用程式支援,改進`Firebase`整合、新的生產力和效能提升、新增`Apple Silicon`支援。截至目前,`Flutter`終於實現了對`iOS、Android、Web、Windows、macOS、Linux`六大平臺的穩定支援,完成了其對跨平臺的願景。
[詳細介紹](https://flutter.cn/docs)
---
#### 2. `dart` 中 `..` 的用法
[參考連結](https://juejin.cn/post/7098649277827645448#heading-38)
---
#### 3. `Dart`中 `??` 與 `??=` 的區別
> A??B
> 左邊如果為空返回右邊的值,否則不處理。
> A??=B
> 左邊如果為空把B的值賦值給A
[參考連結](https://juejin.cn/post/7098649277827645448#heading-17)
---
#### 4. `const`和`final`的區別
**相同點**
1、必須初始化
2、只能賦值一次,且不可被修改
**不同點**
1、`final`可修飾例項變數、`const`不可以修飾例項變數
2、訪問類中`const`修飾的變數需要`static`修飾
3、`const`修飾的`List`集合任意索引不可修改,`final`修飾的可以修改
4、`const` 用來修飾變數 只能被賦值一次,在編譯時賦值,`final` 用來修飾變數 只能被賦值一次,在執行時賦值
5、`final` 只可用來修飾變數, `const` 關鍵字即可修飾變數也可用來修飾 常量建構函式
當`const`修飾類的建構函式時,它要求該類的所有成員都必須是`final`的。
---
#### 5. `Minxins` `extends` `implement` 之前的關係
- `extends`: 如果是複用已有類的實現,使用繼承 `extends`
- `Minxins`: 類似於多繼承,是在多類繼承中重用一個類程式碼的方式 可以使用`with` 來連線繼承多個類
示例:
```
void main() {
var d = D();
d.a(); // 這裡D就可以繼承A B C 的所有方法
}
// 假設有A B C 三個類,類D需要繼承前面的三個,但是又沒有多繼承的方法
class A {
void a() {
print("A.a()...");
}
}
class B {
void b() {
print("B.b()...");
}
}
class C {
void c() {
print("C.c()...");
}
}
class D extends A with B, C {}
// 當繼承的A B C 中都有同名的方法,最後呼叫的順序和這個繼承順序相關,最後繼承的誰,呼叫的就是誰
```
- `implement`: 如果只是使用已有類的外在行為,使用介面 `implements`
示例:
```
void main() {
var student = Student();
}
abstract class Person {
void run();
}
class Student implements Person {
@override
void run() {
print('Student...');
}
}
```
---
#### 6. `await for`
大概意思就是`await for`是不斷獲取`stream`流中的資料,然後執行迴圈體中的操作。
```
Stream stream = new Stream.fromIterable(['不開心', '面試', '沒', '過']);
main() async{
print('上午被開水燙了腳');
await for(String s in stream){
print(s);
}
print('晚上還沒吃飯');
}複製程式碼
```
輸出為
```
上午被開水燙了腳
不開心
面試
沒
過
晚上還沒吃飯
```
`wait for` 和 `listen`的作用很相似,都是獲取流中資料然後輸出,但是正如`await for`中的`await`所示,如果`stream`沒有傳遞完成,就會一直阻塞在這個位置,上面沒吃飯是最後輸出的,下面給個`listen`的例項,一看就懂。
```
Stream stream = new Stream.fromIterable(['不開心', '面試', '沒', '過']);
main(){
print('上午被開水燙了腳');
stream.listen((s) { print(s); });
print('晚上還沒吃飯');
}
```
---
#### 7. `Flutter` 如何與 `Android、iOS` 通訊 ?
`Flutter` 通過 `PlatformChannel` 與原生進行互動,其中 `PlatformChannel` 分為三種:
1. BasicMessageChannel:用於傳遞字串和半結構化的資訊。
1. MethodChannel:用於傳遞方法呼叫。Flutter主動呼叫Native的方法,並獲取相應的返回值。
1. EventChannel:用於資料流(event streams)的通訊。
---
#### 8. `main()` 和`runApp()` 函式在`flutter`的作用分別是什麼?有什麼關係嗎?
> main函式是類似於java語言的程式執行入口函式
>
> runApp函式是渲染根widget樹的函式
>
> 一般情況下runApp函式會在main函式裡執行
---
#### 9. 什麼是`widget` 在`flutter`裡有幾種型別的`widget`,分別有什麼區別?
> widget在flutter裡基本是一些UI元件
>
> 有兩種型別的widget,分別是statefulWidget 和 statelessWidget兩種
>
> statelessWidget不會自己重新構建自己,但是statefulWidget會
---
#### 10. `StatefulWidget` 和`statelessWidget`的生命週期都有哪些?
**StatefulWidget**
- `initState()`:`Widget` 初始化當前 `State`,在當前方法中是不能獲取到 `Context` 的,如想獲取,可以試試 `Future.delayed()`
- `didChangeDependencies()`:在 `initState()` 後呼叫,`State`物件依賴關係發生變化的時候也會呼叫。
- `deactivate()`:當 `State` 被暫時從檢視樹中移除時會呼叫這個方法,頁面切換時也會呼叫該方法,和Android裡的 `onPause` 差不多。
- `dispose()`:`Widget` 銷燬時呼叫。
- `didUpdateWidget`:`Widget` 狀態發生變化的時候呼叫。
**statelessWidget**:
- 建構函式
- `build`方法
[參考文章](https://juejin.cn/post/6844903777879736327)
---
### 九、其他隨機問題
> 這項記錄的是一些開放性問題,沒有標準答案,但也不能全靠隨機應變,我也只是給大家提供一些思路和小的方法,大家僅供參考!
#### 1. 自我介紹
- 首先自報家門,然後告訴面試官自己從事本項工作時長,分別在哪幾家公司,分別負責的專案和具體使用到的技術棧。
- 加分項:如果有部落格或者開源專案可以介紹一下,或者最近自己學習了哪些新的技能知識
- 加分項:自己平常工作中的一些好的習慣,例如:溝通能力、管理能力、學習能力
#### 2. 你的優缺點?
> 這個問題是個開放性的問題,也是面試官讓你自己**充分介紹他所不瞭解的你**。在平常的生活中如果有人問到這個問題,我們可能不太重視,想到哪點說哪點,也不會做整理和優化,**當然我也不是教給大家說謊話,我們要從實際出發,真實闡述自己的(與當前應聘職位相關的)優點和缺點**。下面我給大家分享一篇我認為可以很好闡述這個問題的文章:[連結地址](https://zhuanlan.zhihu.com/p/139952856)
#### 3. 以後的職業規劃
>這個問題同樣開放性比較大,大概是可以分為兩個方向: 技術的深度or廣度、管理層
兩個方向,有不同的重點,在技術領域,無論是深度還是廣度,都需要的是耐心、自律能力、持續學習能力等硬技能,而在管理方向,則需要精進自己的溝通能力、共情能力、協調能力等軟技能,根據自己的想法應答即可。
#### 4. 你平常的學習途徑 ?
> 這個問題因人而異,不同的人有不同的學習習慣,進而就會選擇不同的學習途徑,下面就列出我的學習途徑給大家參考。
- 掘金社群 / 掘金小冊 / csdn / sf
- b站 學習資料
- 某專項技術的官網
- 微信公眾號
- 犀牛書(抄書)
---