面試阿里巴巴前端開發工程師電話面試部分
阿新 • • 發佈:2020-08-24
原文連結: 掘金文章
筆試部分請翻閱上上篇文章
1. 盒模型
- 盒模型屬性有:
margin、padding、border、content
- 標準盒模型:
width/height
=> 指的是content
部分的寬/高 - 怪異盒模型(IE瀏覽器)width/height => 指的是 border + padding + content
2. BFC
- 介紹: 塊格式化上下文
- 特性(功能):
- 內部的盒會在垂直方向一個接一個排列
- 處於同一個
BFC
中的元素相互影響,可能會發生重疊 BFC
就是頁面上的一個隔離的獨立容器,容器裡面的子元素不會影響到外面的元素- 計算
BFC
的高度時,考慮BFC
所包含的所有元素,連浮動元素也參與計算
- 觸發 BFC 特性:
- 浮動元素:
float
除none
以外的值 - 絕對定位元素:
position (absolute、fixed)
overflow
除了visible
以外的值(hidden、auto、scroll)
display
的值為table-cell, table-caption, inline-block, flex
, 或者inline-flex
中的其中一個
- 浮動元素:
BFC
有什麼作用:- 避免外邊距合併
- 防止正常文件流中元素佔據浮動元素位置
- 消除浮動
- 實現自適應佈局
3. 你工作開發中 ES6 及以上常用特性
- 注:記些工作中常用的就好, 以下均為粗略的介紹,詳細介紹請自行百度
- 一總結嚇自己一跳,有好多方法其實我們都在用,但是不知道是Es6 及以上的功能;ES7以上有相容性問題,要注意
- 有點亂, ES6-ES11之間相互穿插了一些功能
ES6
詳情 菜鳥教程很完善的文件https://www.runoob.com/w3cnote/es6-tutorial.html
let
與const
:塊級作用域- 模板字串:
${}
- 解構賦值
Symbol
:新的原始資料型別Symbol
,表示獨一無二的值,最大的用法是用來定義物件的唯一屬性名Map:Map
物件儲存鍵值對。任何值(物件或者原始值) 都可以作為一個鍵或一個值。Set
:物件允許你儲存任何型別的唯一值,無論是原始值或者是物件引用。Proxy
:可以對目標物件的讀取、函式呼叫等操作進行攔截,它不直接操作物件,而是通過代理模式,通過物件的代理物件進行操作;vue3.0重要方法Reflect
:可以用於獲取目標物件的行為,它與Object
類似,但是更易讀,為操作物件提供了一種更優雅的方式。它的方法與Proxy
是對應的- 字串拓展的方法:
includes()
:返回布林值,判斷是否找到引數字串;startsWith()
:返回布林值,判斷引數字串是否在原字串的頭部;endsWith()
:返回布林值,判斷引數字串是否在原字串的尾部。repeat()
:返回新的字串,表示將字串重複指定次數返回。padStart()
:前置填充字串; 經典常用 --> 前置補0。padEnd()
:後置填充字串; 經典常用 --> 後置補0 。
Number
物件新方法Number.isFinite()
: 檢查一個數值是否為有限的(finite
),即不是Infinity
Number.parseInt()
: 將給定字串轉化為指定進位制的整數
Math
物件的擴充套件: ... 一大堆,請檢視文件- 物件的新方法:
Object.assign(target, source_1, ···)
: 將源物件的所有可列舉屬性複製到目標物件中Object.is(value1, value2)
:比較兩個值是否嚴格相等
- 陣列的拓展:
Array.of()
:將引數中所有值作為元素形成陣列。Array.from()
: 將類陣列物件或可迭代物件轉化為陣列。find()
:查詢陣列中符合條件的元素,若有多個符合條件的元素,則返回第一個元素findIndex()
:查詢陣列中符合條件的元素索引,若有多個符合條件的元素,則返回第一個元素索引。fill()
:將一定範圍索引的陣列元素內容填充為單個指定的值。copyWithin()
:將一定範圍索引的陣列元素修改為此陣列另一指定範圍索引的元素。entries()
:遍歷鍵值對。keys()
:遍歷鍵名。values()
:遍歷鍵值。includes()
:陣列是否包含指定值。
- 函式擴充套件
- 箭頭函式
- 預設引數
- 不定引數
for...of
迴圈:迭代常規的資料型別,如Array 、 String 、 Map 和 Set 等等
(有點強大)Class
類的定義extends
繼承- 匯出(
export
)、匯入(import
)兩個模組 Promise
物件非同步操作pending
(進行中)、fulfilled
(已成功)和rejected
(已失敗)。除了非同步操作的結果,任何其他操作都無法改變這個狀態- 從
pending
變為fulfilled
和從pending
變為rejected
的狀態改變。只要處於fulfilled
和rejected
,狀態就不會再變了即resolved
(已定型)
Generator
函式:可以通過yield
關鍵字,把函式的執行流掛起,為改變執行流程提供了可能,從而為非同步程式設計提供解決方案
ES7
Array.prototype.includes
:表示某個陣列是否包含給定的值,與字串的includes
方法類似- 指數運算子(冪運算):
ES8
Async/Await
:非同步函式有效避免回撥地獄Object.values()
:遍歷物件的屬性值,無需使用使用屬性名Object.entries()
:遍歷物件的屬性名和屬性值Object.getOwnPropertyDescriptors()
:獲取一個物件的所有自身屬性的描述符。
ES9
- 非同步迭代:
iterator
(迭代器)除了next()
方法返回一個Promise
。因此await
可以和for...of
迴圈一起使用,以序列的方式執行非同步操作 Promise.finally()
: 返回一個Promise
,無論結果是fulfilled
或者是rejected
,在執行then()
和catch()
後,都會執行finally
指定的回撥函式Rest/Spread
屬性- 正則表示式命名捕獲組
- 正則表示式反向斷言
- 正則表示式Unicode屬性轉義
- 非同步迭代:
ES10
Array.prototype flat, flatMap
: 扁平化陣列Object.fromEntries
:它可以將鍵值對陣列還原成物件結構, 相對應Es6
的Object.entries
方法 遍歷物件的屬性名和屬性值String.protope.{trimstart, trimEnd}
:可以分別去除頭和尾上的空格、換行符Symbol.protoype.description
:獲取Symbol
型別資料的描述資訊- 可選的catch引數
Function.prototype.toString:
之前函式物件呼叫toString
方法,會將它定義過程中的註釋等資訊去掉,現在會原樣進行輸出Array.prototype.sort()
:穩定的陣列排序; V8的先前實現,對包含10個以上項的陣列使用了不穩定的快速排序演算法(快排和插入排序演算法,預設是將陣列元素轉為字串,然後根據Unicode
字符集編號的大小排序)。
ES11
BigInt
資料型別: 表示一個任意精度的整數,可以表示超長資料,可以超出2的53次方- 私有變數: 通過在變數或函式前面新增一個雜湊符號#,可以將它們設為私有屬性,只在類內部可用
Promise.allSettled
: 其引數接受一個Promise
的陣列, 返回一個新的Promise
, 其不會進行短路, 當Promise
全部處理完成後我們可以拿到每個Promise
的狀態, 而不管其是否處理成功;Promise.all\Promise.rae
任何一個失敗都會造成短路- 可選鏈操作符 ?.
- 空值合併運算子 ??
import
支援動態載入模組,載入模組成功以後,這個模組會作為一個物件,當作then回撥的引數。因此,可以使用物件解構賦值的語法,獲取輸出介面globalThis
:一種標準化的方式去訪問全域性物件,這時候可以在任意上下文中獲取全域性物件自身,並且不用擔心環境的問題String.protype.matchAll()
: 返回所有與正則表示式匹配字串的結果的迭代器,包括捕獲組
4. map
和 object
的區別
key
必須是簡單資料型別(整數,字串或者是symbol
),但一個Map
的鍵可以是任意資料型別任意值。Map
中的鍵值是有序的(FIFO 原則),而新增到物件中的鍵則沒有這一特性。Map
的鍵值對個數可以從size
屬性獲取,而Object
的鍵值對個數只能手動計算。new Map()
有set、get
方法Object
都有自己的原型,原型鏈上的鍵名有可能和你自己在物件上的設定的鍵名產生衝突, Map 繼承自 Object 物件。
5. Typescript
和 JavaScript
的不同, 常用型別有哪些
JavaScript
特點、特性:
- 解釋性指令碼語言(不進行預編譯,跟java一樣);
- 在html頁面上提供互動行為、既可以寫成單獨的js檔案,也可以嵌入在html中
- 可跨平臺,因為受到了各種瀏覽器的支援,這使得js可以在各種平臺上執行
- 作為客戶端指令碼語言,獨立運行於使用者的瀏覽器,不需要伺服器的支援,減少對伺服器的負擔
- 不安全性,顯然這個特性和上一個特性相關
- 事件驅動、非同步、動態化,和大部分指令碼語言一樣,型別與值而不是與變數關聯
typescript
是微軟開發的用於開發大型應用的程式語言,其為javascript
的嚴格超集,並添加了可選的靜態型別和基於類的面向物件程式設計
typescript
完全相容javascript
,且最終編譯成javascript
執行typescript
有編譯時型別檢查,這為程式的編寫帶來了極大的方便javascript
是一門動態語言,而typescript
添加了可選的靜態型別typescript
在javascript
的基礎上增加了不少特性(型別批註、編譯時型別檢查、型別推斷、介面、列舉、混入、泛型程式設計、名稱空間、元組、類、可選引數、預設引數 .........)- ++常用型別有++:7大基本資料型別、
any
(任意值 --- 頂級型別)、unknown
(任意值 --- 頂級型別)、never(
永不存在的值的型別)、void
(空值)、Tuple
(元組)、enum
(列舉)、interface
介面型別、泛型
6. vue
和 react
有哪些區別 (自由擴充套件,能擴充套件到很遠)
- 監聽資料變化的實現原理不同
Vue
通過getter/setter
以及一些函式的劫持,每個元件都有自己的渲染watcher
,它掌管了當前元件的檢視更新,但是並不會掌管ChildComponent
的更新, 能精確知道資料變化React
在類似的場景下是 自頂向下的進行遞迴更新的;就是說,React
中假如ChildComponent
裡還有十層巢狀子元素,那麼所有層次都會遞迴的重新render
(在不進行手動優化的情況下),在事務結束時觸發;更新是一個同步的過程,會影響渲染,造成卡頓,這是效能上的災難。React
因為他們遵從Immutable
的設計思想,永遠不在原物件上修改屬性,由於沒有響應式的收集依賴,React
只能遞迴的把所有子元件都重新render
一遍,然後再通過 diff演算法 決定要更新哪部分的檢視,這個遞迴的過程叫做reconciler
;React
在初始化的時候,會把真實的dom
轉化成虛擬的dom,vnode
是fiber
物件節點;(因此,React
創造了Fiber
,創造了非同步渲染,其實本質上是彌補被自己搞砸了的效能)React
預設通過比較引用的方式(diff
)進行的,如果不優化可能導致大量不必要的VDOM
的重新渲染;原因:Vue
使用的是可變資料,而React
更強調資料的不可變,兩者沒有好壞之分,Vue
更加簡單,而React
構建大型應用的時候更加有利Vue
可以做到更小粒度的更新,而react
做不到,這是由資料驅動決定的,vue
的底層diff
演算法參考linux
的檔案比較演算法,在效率上比react
的diff
更好一點,但是react
實現了requsetIdleCallback
,讓計算不影響渲染。更加流暢
- 資料流的不同:
vue
使用v-model
實現雙向資料繫結,react
需手動呼叫setState
方法實現 HoC
和mixins
:vue
使用mixin
實現混合,react
有mixins
轉向了HoC
(高階元件)(覺得mixins
覺得這種方式對元件侵入太強會導致很多問題)- 模板渲染方式的不同:
React
是通過JSX
渲染模板都,通過原生JS
實現模板中的常見語法,比如插值,條件,迴圈等,都是通過JS
語法實現的,更加純粹更加原生;Vue
是通過一種拓展的HTML
語法進行渲染,通過指令來實現各種js
邏輯,有些獨特,但會把HTML
弄得很亂
- 渲染過程不同:
Vue
可以更快地計算出Virtual DOM
的差異,這是由於它在渲染過程中,會跟蹤每一個元件的依賴關係,不需要重新渲染整個元件樹React
在應用的狀態被改變時,全部子元件都會重新渲染。通過shouldComponentUpdate
這個生命週期方法可以進行控制,但Vue
將此視為預設的優化- 如果應用中互動複雜,需要處理大量的
UI
變化,那麼使用Virtual DOM
是一個好主意。如果更新元素並不頻繁,那麼Virtual DOM
並不一定適用,效能很可能還不如直接操控DOM
- 框架本質不同:
Vue
本質是MVVM
框架,由MVC
發展而來;React
是前端元件化框架,由後端元件化發展而來 - 狀態管理
Vuex
和Redux
的區別:
Redux
使用的是不可變資料;每一個元件都需要顯示的用connect
把需要的props
和dispatch
連線起來;只能進行dispatch
,不能直接呼叫reducer
進行修改Vuex
的資料是可變的;$store
被直接注入到了元件例項中,因此可以比較靈活的使用dispatch、commit
提交更新,可通過mapState、mapActions、mapGetters、mapMutations
功能函式或者直接通過this.$store
來讀取資料和操作函式
7. vue3.0
新特性,和vue2.0
的主要區別
- 開發構建工具的重構
vite
Vue團隊也推出了自己的開發構建工具
Vite
,可以在一定程度上取代vue-cli
和webpack-dev-server
的功能;Vite
在開發環境下基於瀏覽器原生ES Modules
開發,在生產環境下基於Rollup
打包;Vite
主要有以下特性:
- 快速的冷啟動
- 即時的模組熱更新
- 真正的按需編譯
- 重構了
Virtual DOM
Vue2.x
版本會遍歷template
模板中的所有內容,並根據這些標籤生成對應的虛擬DOM
,當有內容改變時,遍歷虛擬DOM
來diff
找到對應的標籤元素所對應的DOM
節點,實現雙向資料繫結;但是對於那些純靜態的節點進行diff
其實是比較浪費資源的,當節點的數量很少時,表現並不明顯,但是一旦節點的數量過大,在效能上就會慢很多;vue3.0
在此基礎上優化有:
- 標記靜態內容,並區分動態內容
- 更新時只
diff
動態的部分
- 基於Proxy的響應式物件,替換了
Object.defineProperty()
Proxy
可以直接監聽物件和陣列(而非屬性)的變化,並且有多達13種攔截方法。並且作為新標準將受到瀏覽器廠商重點持續的效能優化Proxy
返回的是一個新物件,我們可以只操作新的物件達到目的,而Object.defineProperty
只能遍歷物件屬性直接修改;Object.defineProperty
無法監聽變異陣列(會修改原來陣列的方法:push、pop、shift、unshift、splice、sort、reverse
等,是無法觸發set
的)的變化, 需要單獨處理; 必須遍歷物件的每個屬性並新增劫持;必須深層遍歷巢狀的物件,直到把每個物件的每個屬性都呼叫Object.defineProperty()
為止;Proxy
只會代理物件的第一層,Vue3
是怎樣處理這個問題的呢?
- 判斷當前
Reflect.get
的返回值是否為Object
,如果是則再通過reactive
方法做代理, 這樣就實現了深度觀測。- 監測陣列的時候可能觸發多次
get/set
,那麼如何防止觸發多次呢?我們可以判斷key是否為當前被代理物件target
自身屬性,也可以判斷舊值與新值是否相等,只有滿足以上兩個條件之一時,才有可能執行trigger
。
composition-api
composition-api
(配置式API開發)提供了一種建立響應式物件的方法reactive
,選擇性的監聽屬性,這樣就可以不用針對每個屬性來一一進行新增,減少開銷提升效能;
Tree shaking
支援
移除
JavaScript
上下文中的未引用程式碼不會被打包;一定程度上減少了資源的大小, 提升了構建、執行速度
TypeScript
的支援
Vue3
中直接採用了typescript
來進行重寫,從原始碼層面來提升專案的可維護性Typescript
能夠在開發時及時發現問題,而非執行時,大大的提升了系統的穩定性- 靜態型別檢查很大程度的統一了團隊之間的開發習慣,易於專案的維護,提高開發人員的工作效率節約開發成本
- 更加豐富的型別支援及開發便利
- 移除的一些API和方法
- 取消
KeyboardEvent.keyCode
: 使用別名代替- 移除
$on,$off
和$once
方法:EventBus --> mitt
方案來代替- 移除
filters
: 使用methods
的或者computed
來進行替代
- Vue Router 的變化
- 構建選項
mode
--> 由原來的mode "history"
更改為history: createWebHistory()
- 構建選項
base
--> 傳給createWebHistory()
的第一個引數作為base
。- 捕獲所有路由 ( /* ) 時,現在必須使用帶有自定義正則表示式的引數進行定義:/:catchAll(.*)
push
或者resolve
一個不存在的命名路由時,將會引發錯誤,而不是導航到根路由 "/" 並且不顯示任何內容- r
outer.match
(匹配路徑引數的物件) 與router.resolve
(頁面跳轉) 合併在一起為router.resolve
8. react hook
有用麼, 說說對react hook
理解
- 優點:
- 讓你在不編寫
class
的情況下使用state
以及其他的React
特性- 函數語言程式設計元件開發,高度解耦,狀態儲存在執行環境、每個功能都包裹在函式中,整體風格更美觀、優雅;
- 元件樹層級變淺、元件粒度越細更容易複用程式碼,通過自定義
hooks
來複用狀態,從而解決了類元件有些時候難以複用邏輯的問題useEffect、useMemo、useCallback
讓優化手段更加的簡單(利用hooks
鉤子)、useState
不用再去考慮this
的指向問題、useContext
讓父子元件傳值更加簡單
- 缺點:
- 響應式的
useEffect
: 需要精確掌握上下文的useEffect
的觸發時機。當邏輯較複雜的時候,useEffect
容易觸發多次- 狀態不同步: 最大的缺點。函式的執行是獨立的,每個函式都有一份獨立的作用域。函式的變數是儲存在執行時的作用域裡面,當我們有非同步操作的時候,經常會碰到非同步回撥的變數引用的是舊狀態
- 解決及避免:
- 不要在
useEffect
裡面寫太多的依賴項,劃分這些依賴項成多個單一功能的useEffect
。其實這點是遵循了軟體設計的“單一職責模式”;- 如果你碰到狀態不同步的問題,可以考慮下手動傳遞引數到函式;
- 複雜業務的時候,使用
Component
代替hooks
9. 工作中優化方向有哪些,依據什麼來確定是否需要優化
內容方向有點多;詳見部落格文章
https://www.cnblogs.com/ljx20180807/p/13543487.html
10. 工作中有沒有遇到比較難的需求,怎麼處理的,效能怎麼樣,你覺得有沒有可以優化的地方
根據實際情況自由發揮
11.帶團隊過程中,團隊和自己所花費時間比例,主要體現在哪些方面**
根據實際情況自由發揮