前端面試題全面整理-帶解析 涵蓋CSS、JS、瀏覽器、Vue、React、移動web、前端效能、演算法、Node
前端面試題全面整理-帶解析 涵蓋CSS、JS、瀏覽器、Vue、React、移動web、前端效能、演算法、Node
本篇文章整理總結了一些前端面試題,涵蓋面很廣,並且面的都是知名大廠,所以這些題還是很有代表性的,都掌握以後一面基礎面應該沒什麼問題,二面也能應付大半,奉上:
css相關
1. 萬能居中
1.margin: 0 auto;水平
2.text-align: center;水平
3.行高,垂直
4.表格,center,middle;水平垂直
5.display:table-cell;模擬表格,all
6.絕對定位,50%減自身寬高
7.絕對定位,上下左右全0,margin:auto
8.絕對定位加相對定位。不需要知道寬高
2. BFC優化
塊格式化上下文, 特性:
-
使 BFC 內部浮動元素不會到處亂跑;
-
和浮動元素產生邊界。
-
標準模式: box-sizing: content-box; 寬高不包括內邊距和邊框
-
怪異模式: box-sizing: border-box
當父元素不給高度的時候,內部元素不浮動時會撐開, 而浮動的時候,父元素變成一條線, 造成塌陷.
-
額外標籤法(在最後一個浮動標籤後,新加一個標籤,給其設定clear:both;)(不推薦)
-
父元素新增overflow:hidden; (觸發BFC)
-
使用after偽元素清除浮動(推薦使用)
-
使用before和after雙偽元素清除浮動
比如antd的row和col, 將一行等分為24份, col是幾就佔幾份, 底層按百分比實現; 結合媒體查詢, 可以實現響應式
6. 純css實現三角形
// 通過設定border
.box
{
width:0px;
height:0px;
border-top:50px solid rgba(0,0,0,0);
border-right:50px solid rgba(0,0,0,0);
border-bottom:50px solid green;
border-left:50px solid rgba(0,0,0,0);
}
7. 高度不定,寬100%,內一p高不確定,如何實現垂直居中?
-
verticle-align: middle;
-
絕對定位50%加translateY(-50%)
-
絕對定位,上下左右全0,margin:auto
-
rem, em
-
百分比
-
媒體查詢
-
bs, antd等的柵格佈局
9. 設定一段文字的大小為6px
-
谷歌最小12px, 其他瀏覽器可以更小
-
通過transform: scale實現
10. css菊花圖
四個小圓點一直旋轉
// 父標籤
animation: antRotate 1.2s infinite linear;
// 子標籤
animation: antSpin 1s infinite linear;
@keyframe antSpin {
to {
opacity: 1
}
}
@keyframe antRotate {
to {
transform: rotate(405)
}
}
// animation-delay: 逐個延遲0.4s
11. 關於em
<p style="font-size: 20px">
123
<p style="font-size: 2em;width: 2em">456</p>
</p>
//此時子元素的font-size為40px,寬度為80px(還要乘以子元素font-size的係數)
12. 關於vh, vw
vw:viewpoint width,視窗寬度,1vw等於視窗寬度的1%。
vh:viewpoint height,視窗高度,1vh等於視窗高度的1%。
vmin:vw和vh中較小的那個。
vmax:vw和vh中較大的那個。
13. Flex佈局
-
flex-direction控制主副軸
-
flex-wrap控制換行(預設不換行)
-
flex-flow是上兩個的結合
-
justify-content主軸對齊方式
-
align-items交叉軸對齊方式
-
overflow: hidden
能清除塊內子元素的浮動影響. 因為該屬性進行超出隱藏時需要計算盒子內所有元素的高度, 所以會隱式清除浮動 -
建立BFC條件(滿足一個):
-
float的值不為none;
-
overflow的值不為visible;
-
position的值為fixed / absolute;
-
display的值為table-cell / table-caption / inline-block / flex / inline-flex。
-
使用vw, vh
-
width
百分比,height: 0
,padding-top(bottom): 50%
16. 標準模式和怪異模式
-
document.compatMode屬性可以判斷是否是標準模式,當 document.compatMode為“CSS1Compat”,是標準模式,“BackCompat”是怪異模式。
-
怪異模式是為了相容舊版本的瀏覽器, 因為IE低版本document.documentElement.clientWidth獲取不到
-
怪異模式盒模型:
box-sizing: border-box
; 標準模式:box-sizing: content-box
兩個對半矩形遮罩, 使用rotate
以及overflow: hidden
進行旋轉
18. css優先順序
選擇器的特殊性值表述為4個部分,用0,0,0,0表示。
-
ID選擇器的特殊性值,加0,1,0,0。
-
類選擇器、屬性選擇器或偽類,加0,0,1,0。
-
元素和偽元素,加0,0,0,1。
-
通配選擇器*對特殊性沒有貢獻,即0,0,0,0。
-
最後比較特殊的一個標誌!important(權重),它沒有特殊性值,但它的優先順序是最高的,為了方便記憶,可以認為它的特殊性值為1,0,0,0,0。
JS相關
1. ES5和ES6繼承方式區別
-
ES5定義類以函式形式, 以prototype來實現繼承
-
ES6以class形式定義類, 以extend形式繼承
ES6 提供的一種非同步程式設計解決方案, Generator 函式是一個狀態機,封裝了多個內部狀態。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
呼叫後返回指向內部狀態的指標, 呼叫next()才會移向下一個狀態, 引數:
hw.next()
//{value:'hello',done:false}
hw.next()
//{value:'world',done:false}
hw.next()
//{value:'ending',done:true}
hw.next()
// { value: undefined, done: true }
3. 手寫Promise實現
var myPromise = new Promise((resolve, reject) => {
// 需要執行的程式碼
...
if (/* 非同步執行成功 */) {
resolve(value)
} else if (/* 非同步執行失敗 */) {
reject(error)
}
})
myPromise.then((value) => {
// 成功後呼叫, 使用value值
}, (error) => {
// 失敗後呼叫, 獲取錯誤資訊error
})
4. Promise優缺點
-
優點: 解決回撥地獄, 對非同步任務寫法更標準化與簡潔化
-
缺點: 首先,無法取消Promise,一旦新建它就會立即執行,無法中途取消; 其次,如果不設定回撥函式,Promise內部丟擲的錯誤,不會反應到外部; 第三,當處於pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成).
極簡版promise封裝:
function promise () {
this.msg = '' // 存放value和error
this.status = 'pending'
var that = this
var process = arguments[0]
process (function () {
that.status = 'fulfilled'
that.msg = arguments[0]
}, function () {
that.status = 'rejected'
that.msg = arguments[0]
})
return this
}
promise.prototype.then = function () {
if (this.status === 'fulfilled') {
arguments[0](this.msg)
} else if (this.status === 'rejected' && arguments[1]) {
arguments[1](this.msg)
}
}
5. 觀察者模式
又稱釋出-訂閱模式, 舉例子說明.
實現: 釋出者管理訂閱者佇列, 並有新訊息推送功能. 訂閱者僅關注更新就行
6. 手寫實現bind
Function.prototype.bind = function () {
// 儲存原函式
var self = this
// 取出第一個引數作為上下文, 相當於[].shift.call(arguments)
var context = Array.prototype.shift.call(arguments)
// 取剩餘的引數作為arg; 因為arguments是偽陣列, 所以要轉化為陣列才能使用陣列方法
var arg = Array.prototype.slice.call(arguments)
// 返回一個新函式
return function () {
// 繫結上下文並傳參
self.apply(context, Array.prototype.concat.call(arg, Array.prototype.slice.call(arguments)))
}
}
7. 手寫實現4種繼承
function Father () {}
function Child () {}
// 1\. 原型繼承
Child.prototype = new Father()
// 2\. 構造繼承
function Child (name) {
Father.call(this, name)
}
// 3\. 組合繼承
function Child (name) {
Father.call(this, name)
}
Child.prototype = new Father()
// 4\. 寄生繼承
function cloneObj (o) {
var clone = object.create(o)
clone.sayName = ...
return clone
}
// 5\. 寄生組合繼承
// 6\. ES6 class extend繼承
8. css菊花圖
四個小圓點一直旋轉
// 父標籤
animation: antRotate 1.2s infinite linear;
// 子標籤
animation: antSpin 1s infinite linear;
@keyframe antSpin {
to {
opacity: 1
}
}
@keyframe antRotate {
to {
transform: rotate(405)
}
}
// animation-delay: 逐個延遲0.4s
9. http狀態碼
-
1**: 伺服器收到請求, 需請求者進一步操作
-
2**: 請求成功
-
3**: 重定向, 資源被轉移到其他URL了
-
4**: 客戶端錯誤, 請求語法錯誤或沒有找到相應資源
-
5**: 服務端錯誤, server error
-
304: Not Modified. 指定日期後未修改, 不返回資源
11. async和await:
-
Generator函式的語法糖,將*改成async,將yield換成await。
-
是對Generator函式的改進, 返回promise。
-
非同步寫法同步化,遇到await先返回,執行完非同步再執行接下來的.
-
內建執行器, 無需next()
-
演算法:
解決具體問題所需要的解決方法。執行效率最快的最優演算法。時間複雜度。輸入,輸出,有窮性,確定性,可行性。氣泡排序,二叉樹遍歷,最長迴文,二分查詢,指標,連結串列等,堆疊,佇列等。力扣,codewar,演算法導論。 -
資料結構:
邏輯結構:集合、線性、樹形、圖形結構
物理結構:順序、鏈式儲存結構
function jsonp ({url, param, callback}) {
return new Promise((resolve, reject) => {
var script = document.createElement('script')
window.callback = function (data) {
resolve(data)
document.body.removeChild('script')
}
var param = {...param, callback}
var arr = []
for (let key in param) {
arr.push(`${key}=${param[key]}`)
}
script.src = `${url}?${arr.join('&')}`
document.body.appendChild(script)
})
}
14. 手動實現map(forEach以及filter也類似)
// for迴圈實現
Array.prototype.myMap = function () {
var arr = this
var [fn, thisValue] = Array.prototype.slice.call(arguments)
var result = []
for (var i = 0; i < arr.length; i++) {
result.push(fn.call(thisValue, arr[i], i, arr))
}
return result
}
var arr0 = [1, 2, 3]
console.log(arr0.myMap(v => v + 1))
// forEach實現(reduce類似)
Array.prototype.myMap = function (fn, thisValue) {
var result = []
this.forEach((v, i, arr) => {
result.push(fn.call(thisValue, v, i, arr))
})
return result
}
var arr0 = [1, 2, 3]
console.log(arr0.myMap(v => v + 1))
15. js實現checkbox全選以及反選
<body>
<button id="other">反選</button>
<input type="checkbox" id="all" />全選
<input type="checkbox" class="check" />1
<input type="checkbox" class="check" />2
<input type="checkbox" class="check" />3
<script>
var checkbox = document.getElementsByClassName('check')
var checkAll = document.getElementById('all')
var checkOther = document.getElementById('other')
checkAll.onclick = function() {
var flag = true
for (var i = 0; i < checkbox.length; i++) {
if (!checkbox[i].checked) flag = false
}
if (flag) {
for (var i = 0; i < checkbox.length; i++) {
checkbox[i].checked = false
}
} else {
for (var i = 0; i < checkbox.length; i++) {
checkbox[i].checked = true
}
}
}
checkOther.onclick = function() {
for (var i = 0; i < checkbox.length; i++) {
checkbox[i].checked = !checkbox[i].checked
}
}
</script>
</body>
16. 對原型鏈的理解?prototype上都有哪些屬性
-
在js裡,繼承機制是原型繼承。繼承的起點是 物件的原型(Object prototype)。
-
一切皆為物件,只要是物件,就會有 proto 屬性,該屬性儲存了指向其構造的指標。
-
Object prototype也是物件,其 proto 指向null。
-
物件分為兩種:函式物件和普通物件,只有函式物件擁有『原型』物件(prototype)。
-
prototype的本質是普通物件。
-
Function prototype比較特殊,是沒有prototype的函式物件。
-
new操作得到的物件是普通物件。
-
當調取一個物件的屬性時,會先在本身查詢,若無,就根據 proto 找到構造原型,若無,繼續往上找。最後會到達頂層Object prototype,它的 proto 指向null,均無結果則返回undefined,結束。
-
由 proto 串起的路徑就是『原型鏈』。
-
通過prototype可以給所有子類共享屬性
通常在一般的專案裡不需要,因為應用簡單,但你要用純js做一些複雜的工具或框架系統就要用到了,比如webgis、或者js框架如jquery、ext什麼的,不然一個幾千行程式碼的框架不用繼承得寫幾萬行,甚至還無法維護。
18. setTimeout時間延遲為何不準
單執行緒, 先執行同步主執行緒, 再執行非同步任務佇列
19. 事件迴圈述,巨集任務和微任務有什麼區別?
-
先主執行緒後非同步任務佇列
-
先微任務再巨集任務
塊級作用域, 暫時性死區
21. 節流和防抖
-
函式節流是指一定時間內js方法只跑一次。比如人的眨眼睛,就是一定時間內眨一次。這是函式節流最形象的解釋。
// 函式節流 滾動條滾動
var canRun = true;
document.getElementById("throttle").onscroll = function(){
if(!canRun){
// 判斷是否已空閒,如果在執行中,則直接return
return;
}
canRun = false;
setTimeout(function(){
console.log("函式節流");
canRun = true;
}, 300);
};
-
函式防抖是指頻繁觸發的情況下,只有足夠的空閒時間,才執行程式碼一次。比如生活中的坐公交,就是一定時間內,如果有人陸續刷卡上車,司機就不會開車。只有別人沒刷卡了,司機才開車。
// 函式防抖
var timer = false;
document.getElementById("debounce").onscroll = function(){
clearTimeout(timer); // 清除未執行的程式碼,重置回初始化狀態
timer = setTimeout(function(){
console.log("函式防抖");
}, 300);
};
22. 實現一個sleep函式
// 這種實現方式是利用一個偽死迴圈阻塞主執行緒。因為JS是單執行緒的。所以通過這種方式可以實現真正意義上的sleep()。
function sleep(delay) {
var start = (new Date()).getTime();
while ((new Date()).getTime() - start < delay) {
continue;
}
}
function test() {
console.log('111');
sleep(2000);
console.log('222');
}
test()
23. 閉包
-
概念: 內層函式能夠訪問外層函式作用域的變數
-
缺點: 引起記憶體洩漏(釋放記憶體)
-
作用:
-
使用閉包修正列印值
-
實現柯里化
-
實現node commonJs 模組化, 實現私有變數
-
保持變數與函式活性, 可延遲迴收和執行
Facebook出品, 倡導資料的不可變性, 用的最多就是List和Map.
25. js實現instanceof
// 檢測l的原型鏈(__proto__)上是否有r.prototype,若有返回true,否則false
function myInstanceof (l, r) {
var R = r.prototype
while (l.__proto__) {
if (l.__proto__ === R) return true
}
return false
}
27. ES6的模組引入和CommonJs區別
28. 嚴格模式
// 嚴格模式下, 隱式繫結丟失後this不會指向window, 而是指向undefined
'use strict'
var a = 2
var obj = {
a: 1,
b: function() {
// console.log(this.a)
console.log(this)
}
}
var c = obj.b
c() // undefined
29. fetch, axios區別
30. typescript缺點
-
並不是嚴格意義的js的超集, 與js不完全相容, 會報錯
-
更多的限制, 是一種桎梏
-
有些js第三方庫沒有dts, 有問題
-
建構函式中沒有顯示的建立Object物件, 實際上後臺自動建立了
-
直接給this物件賦值屬性和方法, this即指向建立的物件
-
沒有return返回值, 後臺自動返回了該物件
// 模擬建構函式實現
var Book = function(name) {
this.name = name;
};
//正常用法
var java = new Book(‘Master Java’);
//使用程式碼模擬,在非IE瀏覽器中測試,IE瀏覽器不支援
var python = {};
python.__proto__ = Book.prototype;
Book.call(python, 'Master Python');
32. for in 和 for of區別
-
for in
遍歷陣列會遍歷到陣列原型上的屬性和方法, 更適合遍歷物件 -
forEach
不支援break, continue, return
等 -
使用
for of
可以成功遍歷陣列的值, 而不是索引, 不會遍歷原型 -
for in 可以遍歷到myObject的原型方法method,如果不想遍歷原型方法和屬性的話,可以在迴圈內部判斷一下,hasOwnPropery方法可以判斷某屬性是否是該物件的例項屬性
使用訊息佇列以及setInterval
或promise
進行入隊和出隊
34. ajax和axios、fetch的區別
35. promise.finally實現
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};
瀏覽器網路相關
1. reflow(迴流)和repaint(重繪)優化
-
瀏覽器渲染過程: DOM tree, CSS tree --> Render tree --> Paint
-
DOM tree根節點為html
-
渲染從瀏覽器左上角到右下角
-
第一次開啟頁面至少觸發一次重繪和迴流, 結構如寬高位置變化時, 觸發reflow迴流;非結構如背景色變化時, 觸發repaint重繪. 二者都會造成體驗不佳
-
如何減少重繪和迴流?
-
通過classname或cssText一次性修改樣式, 而非一個一個改
-
離線模式: 克隆要操作的結點, 操作後再與原始結點交換, 類似於虛擬DOM
-
避免頻繁直接訪問計算後的樣式, 而是先將資訊儲存下來
-
絕對佈局的DOM, 不會造成大量reflow
-
p不要巢狀太深, 不要超過六層
2.一個頁面從輸入 URL 到頁面載入顯示完成,這個過程中都發生了什麼?
-
瀏覽器根據請求的URL交給DNS域名解析,找到真實IP,向伺服器發起請求;
-
伺服器交給後臺處理完成後返回資料,瀏覽器接收檔案(HTML、JS、CSS、圖象等);
-
瀏覽器對載入到的資源(HTML、JS、CSS等)進行語法解析,建立相應的內部資料結構(如HTML的DOM Tree);
-
載入解析到的資原始檔,渲染頁面,完成。
-
共同點: 都儲存在瀏覽器端, 且同源
-
localStorage 與 sessionStorage 統稱webStorage,儲存在瀏覽器,不參與伺服器通訊,大小為5M
-
生命週期不同: localStorage永久儲存, sessionStorage當前會話, 都可手動清除
-
作用域不同: 不同瀏覽器不共享local和session, 不同會話不共享session
-
Cookie: 設定的過期時間前一直有效, 大小4K.有個數限制, 各瀏覽器不同, 一般為20個.攜帶在HTTP頭中, 過多會有效能問題.可自己封裝, 也可用原生
-
阻止事件傳播(冒泡): e.stopPropagation()
-
阻止預設行為: e.preventDefault()
虛擬DOM可提升效能, 無須整體重新渲染, 而是區域性重新整理.
JS物件, diff演算法
6.瀏覽器事件機制中事件觸發三個階段
-
事件捕獲階段: 從dom樹節點往下找到目標節點, 不會觸發函式
-
事件目標處理函式: 到達目標節點
-
事件冒泡: 最後從目標節點往頂層元素傳遞, 通常函式在此階段執行.
addEventListener第三個引數預設false(冒泡階段執行),true(捕獲階段執行).
阻止冒泡見以上方法
-
跨域是指一個域下的文件或指令碼試圖去請求另一個域下的資源
-
防止XSS、CSFR等攻擊, 協議+域名+埠不同
-
jsonp; 跨域資源共享(CORS)(Access control); 伺服器正向代理等
-
預檢請求: 需預檢的請求要求必須首先使用 OPTIONS 方法發起一個預檢請求到伺服器,以獲知伺服器是否允許該實際請求。"預檢請求“的使用,可以避免跨域請求對伺服器的使用者資料產生未預期的影響
-
瀏覽器快取就是把一個已經請求過的資源拷貝一份儲存起來,當下次需要該資源時,瀏覽器會根據快取機制決定直接使用快取資源還是再次向伺服器傳送請求.
-
from memory cache ; from disk cache
-
作用: 減少網路傳輸的損耗以及降低伺服器壓力。
-
優先順序: 強制快取 > 協商快取; cache-control > Expires > Etag > Last-modified
DOM本身是一個js物件, 操作這個物件本身不慢, 但是操作後觸發了瀏覽器的行為, 如repaint和reflow等瀏覽器行為, 使其變慢
10.什麼情況會阻塞渲染?
-
js指令碼同步執行
-
css和圖片雖然是非同步載入, 但js檔案執行需依賴css, 所以css也會阻塞渲染
判斷有無全域性物件global和window
12.關於web以及瀏覽器處理預載入有哪些思考?
圖片等靜態資源在使用之前就提前請求
資源使用到的時候能從快取中載入, 提升使用者體驗
頁面展示的依賴關係維護
13.http多路複用
-
Keep-Alive: Keep-Alive解決的核心問題:一定時間內,同一域名多次請求資料,只建立一次HTTP請求,其他請求可複用每一次建立的連線通道,以達到提高請求效率的問題。這裡面所說的一定時間是可以配置的,不管你用的是Apache還是nginx。
-
解決兩個問題: 序列檔案傳輸(採用二進位制資料幀); 連線數過多(採用流, 並行傳輸)
14. http和https:
-
http: 最廣泛網路協議,BS模型,瀏覽器高效。
-
https: 安全版,通過SSL加密,加密傳輸,身份認證,金鑰
-
https相對於http加入了ssl層, 加密傳輸, 身份認證;
-
需要到ca申請收費的證書;
-
安全但是耗時多,快取不是很好;
-
注意相容http和https;
-
連線方式不同, 埠號也不同, http是80, https是443
16. cookie可設定哪些屬性?httponly?
chrome控制檯的application下可檢視:
-
name 欄位為一個cookie的名稱。
-
value 欄位為一個cookie的值。
-
domain 欄位為可以訪問此cookie的域名。
-
path 欄位為可以訪問此cookie的頁面路徑。比如domain是abc.com,path是/test,那麼只有/test路徑下的頁面可以讀取此cookie。
-
expires/Max-Age 欄位為此cookie超時時間。若設定其值為一個時間,那麼當到達此時間後,此cookie失效。不設定的話預設值是Session,意思是cookie會和session一起失效。當瀏覽器關閉(不是瀏覽器標籤頁,而是整個瀏覽器) 後,此cookie失效。
-
Size 欄位 此cookie大小。
-
http 欄位 cookie的httponly屬性。若此屬性為true,則只有在http請求頭中會帶有此cookie的資訊,而不能通過document.cookie來訪問此cookie。
-
secure 欄位 設定是否只能通過https來傳遞此條cookie
-
前端存放服務端下發的cookie, 簡單說就是寫一個欄位在cookie中表明已登入, 並設定失效日期
-
或使用後端返回的token, 每次ajax請求將token攜帶在請求頭中, 這也是防範csrf的手段之一
- 1**: 伺服器收到請求, 需請求者進一步操作
- 2**: 請求成功
- 3**: 重定向, 資源被轉移到其他URL了
- 4**: 客戶端錯誤, 請求語法錯誤或沒有找到相應資源
- 5**: 服務端錯誤, server error
- 301: 資源(網頁等)被永久轉移到其他URL, 返回值中包含新的URL, 瀏覽器會自動定向到新URL
- 302: 臨時轉移. 客戶端應訪問原有URL
- 304: Not Modified. 指定日期後未修改, 不返回資源
- 403: 伺服器拒絕執行請求
- 404: 請求的資源(網頁等)不存在
- 500: 內部伺服器錯誤
Cache-control, expire, last-modify
20. 實現頁面回退重新整理
-
舊: window.history.back() + window.location.href=document.referrer;
-
新: HTML5的新API擴充套件了window.history,使歷史記錄點更加開放了。可以儲存當前歷史記錄點、替換當前歷史記錄點、監聽歷史記錄點onpopstate, replaceState
-
正向代理:
(1)訪問原來無法訪問的資源,如google
(2) 可以做快取,加速訪問資源
(3)對客戶端訪問授權,上網進行認證
(4)代理可以記錄使用者訪問記錄(上網行為管理),對外隱藏使用者資訊
-
反向代理:
(1)保證內網的安全,可以使用反向代理提供WAF功能,阻止web攻擊大型網站,通常將反向代理作為公網訪問地址,Web伺服器是內網。
(2)負載均衡,通過反向代理伺服器來優化網站的負載
22. 關於預檢請求
在非簡單請求且跨域的情況下,瀏覽器會自動發起options預檢請求。
23. 三次握手四次揮手
-
開啟連線用三次握手, 關閉用四次揮手
-
TCP(Transmission Control Protocol:傳輸控制協議;面向連線,可靠傳輸
-
UDP(User Datagram Protocol):使用者資料報協議;面向無連線,不可靠傳輸
25. 程序和執行緒的區別
-
程序:是併發執行的程式在執行過程中分配和管理資源的基本單位,是一個動態概念,競爭計算機系統資源的基本單位。
-
執行緒:是程序的一個執行單元,是程序內科排程實體。比程序更小的獨立執行的基本單位。執行緒也被稱為輕量級程序。
-
一個程式至少一個程序,一個程序至少一個執行緒。
vue相關
1. 生命週期
2 .雙向資料繫結v-model。這個最好也是自己實現一下 理解更深
通過v-model
VUE實現雙向資料繫結的原理就是利用了 Object.defineProperty() 這個方法重新定義了物件獲取屬性值(get)和設定屬性值(set)的操作來實現的。
// 依賴收集
// 簡化版
var obj = { }
var name
//第一個引數:定義屬性的物件。
//第二個引數:要定義或修改的屬性的名稱。
//第三個引數:將被定義或修改的屬性描述符。
Object.defineProperty(obj, "data", {
//獲取值
get: function () {
return name
},
//設定值
set: function (val) {
name = val
console.log(val)
}
})
//賦值呼叫set
obj.data = 'aaa'
//取值呼叫get
console.log(obj.data)
// 詳細版
myVue.prototype._obverse = function (obj) { // obj = {number: 0}
var value;
for (key in obj) { //遍歷obj物件
if (obj.hasOwnProperty(key)) {
value = obj[key];
if (typeof value === 'object') { //如果值是物件,則遞迴處理
this._obverse(value);
}
Object.defineProperty(this.$data, key, { //關鍵
enumerable: true,
configurable: true,
get: function () {
console.log(`獲取${value}`);
return value;
},
set: function (newVal) {
console.log(`更新${newVal}`);
if (value !== newVal) {
value = newVal;
}
}
})
}
}
}
3.vue父子元件傳遞引數
-
父 -->子: 通過props
-
子 -->父: 通過 $$refs 或 $emit
4.vue傳遞引數方法
-
父子元件傳參如上, v-bind : v-on @
-
兄弟元件傳參:(通過EventBus事件匯流排實現)
// 1\. 新建eventBus.js
import Vue from 'vue'
export default new Vue
// 或直接在main.js中初始化EventBus(全域性)
Vue.prototype.$EventBus = new Vue()
// 2\. 發射與接收
// 如果是定義在eventBus.js中
import eventBus from 'eventBus.js'
eventBus.$emit()
eventBus.$on()
// 如果是定義在main.js中
this.bus.$emit()
this.bus.$on()
// 3\. 移除監聽
eventBus.$off()
5.vue自定義元件
可以使用獨立可複用的自定義元件來構成大型應用, 採用帕斯卡命名法或橫線連線, 通過以上方式進行元件間通訊. 每一個元件都是Vue例項, 可以使用生命週期鉤子.
6. vue自定義指令
-
除核心指令之外的指令, 使用directive進行註冊.
-
指令自定義鉤子函式: bind, inserted, update, componentUpdated, unbind
7.vuex組成和原理
-
組成: 元件間通訊, 通過store實現全域性存取
-
修改: 唯一途徑, 通過commit一個mutations(同步)或dispatch一個actions(非同步)
-
簡寫: 引入mapState、mapGetters、mapActions
-
vue-router用法:
在router.js或者某一個路由分發頁面配置path, name, component對應關係 -
每個按鈕一個value, 在watch功能中使用this.$router.push實現對應跳轉, 類似react的this.history.push
-
或直接用router-link to去跳轉, 類似react的link to
-
vue-router原理: 通過hash和History interface兩種方式實現前端路由
-
HashHistory: 利用URL中的hash(“#”);replace()方法與push()方法不同之處在於,它並不是將新路由新增到瀏覽器訪問歷史的棧頂,而是替換掉當前的路由
-
History interface: 是瀏覽器歷史記錄棧提供的介面,通過back(), forward(), go()等方法,我們可以讀取瀏覽器歷史記錄棧的資訊,進行各種跳轉操作. pushState(), replaceState() 這下不僅是讀取了,還可以對瀏覽器歷史記錄棧進行修改
seo關係到網站排名, vue搭建spa做前後端分離不好做seo, 可通過其他方法解決:
-
SSR服務端渲染: 將同一個元件渲染為伺服器端的 HTML 字串.利於seo且更快.
-
vue-meta-info, nuxt, prerender-spa-plugin頁面預渲染等
以上
11.生命週期內create和mounted的區別
-
created: 在模板渲染成html前呼叫,即通常初始化某些資料,然後再渲染成檢視。
-
mounted: 在模板渲染成html後呼叫,通常是初始化頁面完成後,再對html的dom節點進行一些需要的操作和方法。
對應一個物件,鍵是觀察表示式,值是對應回撥。值也可以是methods的方法名,或者是物件,包含選項。在例項化時為每個鍵呼叫 $watch()
13.登入驗證攔截(通過router)
-
先設定requireAuth:
routes = [
{
name: 'detail',
path: '/detail',
meta: {
requireAuth: true
}
},
{
name: 'login',
path: '/login'
}
]
-
再配置router.beforeEach:
router.beforeEach((from, to, next) => {
if (to.meta.requireAuth) { // 判斷跳轉的路由是否需要登入
if (store.state.token) { // vuex.state判斷token是否存在
next() // 已登入
} else {
next({
path: '/login',
query: {redirect: to.fullPath} // 將跳轉的路由path作為引數,登入成功後跳轉到該路由
})
}
} else {
next()
}
})
14. v-for key值
不寫key值會報warning, 和react的array渲染類似. 根據diff演算法, 修改陣列後, 寫key值會複用, 不寫會重新生成, 造成效能浪費或某些不必要的錯誤
15. vue3.0的更新和defineProperty優化
-
放棄 Object.defineProperty ,使用更快的原生 Proxy (訪問物件攔截器, 也成代理器)
-
提速, 降低記憶體使用, Tree-shaking更友好
-
支援IE11等
-
使用Typescript
正常要通過vm.[圖片上傳失敗...(image-6d2f4e-1570591304185)]
root傳參取值
16. jQuery的優缺點,與vue的不同,vue的優缺點?
-
jq優點: 比原生js更易書寫, 封裝了很多api, 有豐富的外掛庫; 缺點: 每次升級與之前版本不相容, 只能手動開發, 操作DOM很慢, 不方便, 變數名汙染, 作用域混淆等。
-
vue優缺點: 雙向繫結, 虛擬DOM, diff演算法, MVVM, 元件化, 通訊方便, 路由分發等
let obj = JSON.parse(JSON.stringify(this.temp1));
18. vue非同步元件
為了簡化,Vue 允許你以一個工廠函式的方式定義你的元件,這個工廠函式會非同步解析你的元件定義。Vue 只有在這個元件需要被渲染的時候才會觸發該工廠函式,且會把結果快取起來供未來重渲染
Vue.component(
'async-webpack-example',
// 這個 `import` 函式會返回一個 `Promise` 物件。
() => import('./my-async-component')
)
19. MVC與MVVM
-
model-資料層 view-檢視層 controller-控制層
-
MVC的目的是實現M和V的分離,單向通訊,必須通過C來承上啟下
-
MVVM中通過VM(vue中的例項化物件)的釋出者-訂閱者模式實現雙向繫結,資料繫結,dom事件監聽
-
區別:MVC和MVVM的區別並不是VM完全取代了C,ViewModel存在目的在於抽離Controller中展示的業務邏輯,而不是替代Controller,其它檢視操作業務等還是應該放在Controller中實現。也就是說MVVM實現的是業務邏輯元件的重用
小到可以只使用核心功能,比如單檔案元件作為一部分嵌入;大到使用整個工程,vue init webpack my-project來構建專案;VUE的核心庫及其生態系統也可以滿足你的各式需求(core+vuex+vue-route)
react相關
1. 新舊生命週期
-
舊: will, did; mount, update...
-
新: 16版本之後:
-
getDerivedStateFromProps
: 虛擬dom之後,實際dom掛載之前, 每次獲取新的props或state之後, 返回新的state, 配合didUpdate可以替代willReceiveProps -
getSnapshotBeforeUpdate
: update發生的時候,元件更新前觸發, 在render之後,在元件dom渲染之前;返回一個值,作為componentDidUpdate的第三個引數;配合componentDidUpdate, 可以覆蓋componentWillUpdate的所有用法 -
componentDidCatch
: 錯誤處理 -
對比: 棄用了三個will, 新增兩個get來代替will, 不能混用, 17版本會徹底刪除. 新增錯誤處理
-
虛擬DOM, Diff演算法, 遍歷key值
-
react-dom: 提供了針對DOM的方法,比如:把建立的虛擬DOM,渲染到頁面上 或 配合ref來操作DOM
-
react-router
-
舊: 瀏覽器渲染引擎單執行緒, 計算DOM樹時鎖住整個執行緒, 所有行為同步發生, 有效率問題, 期間react會一直佔用瀏覽器主執行緒,如果元件層級比較深,相應的堆疊也會很深,長時間佔用瀏覽器主執行緒, 任何其他的操作(包括使用者的點選,滑鼠移動等操作)都無法執行。
-
新: 重寫底層演算法邏輯, 引入fiber時間片, 非同步渲染, react會在渲染一部分樹後檢查是否有更高優先順序的任務需要處理(如使用者操作或繪圖), 處理完後再繼續渲染, 並可以更新優先順序, 以此管理渲染任務. 加入fiber的react將元件更新分為兩個時期(phase 1 && phase 2),render前的生命週期為phase1,render後的生命週期為phase2, 1可以打斷, 2不能打斷一次性更新. 三個will生命週期可能會重複執行, 儘量避免使用。
-
分為首次渲染和更新渲染
-
生命週期, 建立虛擬DOM, 進行diff演算法
-
對比新舊DOM, 節點對比, 將演算法複雜度從O(n^3)降低到O(n)
-
key值優化, 避免用index作為key值, 兄弟節點中唯一就行
高階元件就是一個函式,且該函式(wrapper)接受一個元件作為引數,並返回一個新的元件。
高階元件並不關心資料使用的方式和原因,而被包裹的元件也不關心資料來自何處.
-
react-dnd: 根元件, source, target等
export default DragSource(type, spec, collect)(MyComponent)
-
重構程式碼庫使用HOC提升開發效率
在無狀態元件(如函式式元件)中也能操作state以及其他react特性, 通過useState
7. redux和vuex以及dva:
-
redux: 通過store儲存,通過action唯一更改,reducer描述如何更改。dispatch一個action
-
dva: 基於redux,結合redux-saga等中介軟體進行封裝
-
vuex:類似dva,整合化。action非同步,mutation非非同步
-
資料是否可變: react整體是函式式的思想,把元件設計成純元件,狀態和邏輯通過引數傳入,所以在react中,是單向資料流,推崇結合immutable來實現資料不可變; vue的思想是響應式的,也就是基於是資料可變的,通過對每一個屬性建立Watcher來監聽,當屬性變化的時候,響應式的更新對應的虛擬dom。總之,react的效能優化需要手動去做,而vue的效能優化是自動的,但是vue的響應式機制也有問題,就是當state特別多的時候,Watcher也會很多,會導致卡頓,所以大型應用(狀態特別多的)一般用react,更加可控。
-
通過js來操作一切,還是用各自的處理方式: react的思路是all in js,通過js來生成html,所以設計了jsx,還有通過js來操作css,社群的styled-component、jss等; vue是把html,css,js組合到一起,用各自的處理方式,vue有單檔案元件,可以把html、css、js寫到一個檔案中,html提供了模板引擎來處理。
-
類式的元件寫法,還是宣告式的寫法: react是類式的寫法,api很少; 而vue是宣告式的寫法,通過傳入各種options,api和引數都很多。所以react結合typescript更容易一起寫,vue稍微複雜。
-
擴充套件不同: react可以通過高階元件(Higher Order Components--HOC)來擴充套件,而vue需要通過mixins來擴充套件。
-
什麼功能內建,什麼交給社群去做: react做的事情很少,很多都交給社群去做,vue很多東西都是內建的,寫起來確實方便一些,
比如 redux的combineReducer就對應vuex的modules,
比如reselect就對應vuex的getter和vue元件的computed,
vuex的mutation是直接改變的原始資料,而redux的reducer是返回一個全新的state,所以redux結合immutable來優化效能,vue不需要。
React是單向資料流,資料主要從父節點傳遞到子節點(通過props)。如果頂層(父級)的某個props改變了,React會重渲染所有的子節點。
10. React演算法複雜度優化
react樹對比是按照層級去對比的, 他會給樹編號0,1,2,3,4.... 然後相同的編號進行比較。所以複雜度是n,這個好理解。
關鍵是傳統diff的複雜度是怎麼算的?傳統的diff需要出了上面的比較之外,還需要跨級比較。他會將兩個樹的節點,兩兩比較,這就有n^2的複雜度了。然後還需要編輯樹,編輯的樹可能發生在任何節點,需要對樹進行再一次遍歷操作,因此複雜度為n。加起來就是n^3了。
11. React優點
宣告式, 元件化, 一次學習, 隨處編寫. 靈活, 豐富, 輕巧, 高效
移動端相關
1. 移動端相容適配
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
-
rem, em, 百分比
-
框架的柵格佈局
-
media query媒體查詢
-
手淘團隊的一套flexible.js, 自動判斷dpr進行整個佈局視口的放縮
判斷機型, 找出樣本機型去適配. 比如iphone以6為樣本, 寬度375px, dpr是2
3. 為什麼以iPhone6為標準的設計稿的尺寸是以750px寬度來設計的呢?
iPhone6的滿屏寬度是375px,而iPhone6採用的視網膜屏的物理畫素是滿屏寬度的2倍,也就是dpr(裝置畫素比)為2, 並且設計師所用的PS設計軟體解析度和畫素關係是1:1。所以為了做出的清晰的頁面,設計師一般給出750px的設計圖,我們再根據需求對元素的尺寸設計和壓縮。
4. 如何處理異形屏iphone X
-
safe area
: 預設放置在安全區域以避免遮擋, 但會壓縮 -
在meta中新增
viewport-fit=cover
: 告訴瀏覽器要講整個頁面渲染到瀏覽器中,不管裝置是圓角與否,這個時候會造成頁面的元素被圓角遮擋 -
padding: constant(env)
: 解決遮擋問題
-
採用伺服器渲染ssr
-
按需載入配合webpack分塊打包, 通過entry和commonChunkPlugin
-
很有必要將script標籤➕非同步
-
有輪播圖 最好給個預設 另外要處理圖片懶載入
-
打包線上也要注意去掉map 檔案
-
元件, 路由懶載入
-
webpack的一切配置 肯定是必須的
-
壓縮圖片tinypng.com/
-
建議還是用webpack的圖片壓縮外掛
-
骨架屏
-
Loading頁面
一個 PWA 應用首先是一個網頁, 可以通過 Web 技術編寫出一個網頁應用. 隨後新增上 App Manifest 和 Service Worker 來實現 PWA 的安裝和離線等功能
解決了哪些問題?
-
可以新增至主螢幕,點選主螢幕圖示可以實現啟動動畫以及隱藏位址列
-
實現離線快取功能,即使使用者手機沒有網路,依然可以使用一些離線功能
-
實現了訊息推送
它解決了上述提到的問題,這些特性將使得 Web 應用漸進式接近原生 App。
現在 web 頁面在移動端的地位越來越高,大部分主流 App 採用 native + webview 的 hybrid 模式,載入遠端頁面受限於網路,本地 webview 引擎,經常會出現渲染慢導致的白屏現象,體驗很差,於是離線包方案應運而生。動態下載的離線包可以使得我們不需要走完整的 App 稽核釋出流程就完成了版本的更新
8. 自適應和響應式佈局的區別
-
自適應佈局通過檢測視口解析度,來判斷當前訪問的裝置是:pc端、平板、手機,從而請求服務層,返回不同的頁面;響應式佈局通過檢測視口解析度,針對不同客戶端在客戶端做程式碼處理,來展現不同的佈局和內容。
-
自適應佈局需要開發多套介面,而響應式佈局只需要開發一套介面就可以了。
-
自適應對頁面做的螢幕適配是在一定範圍:比如pc端一般要大於1024畫素,手機端要小於768畫素。而響應式佈局是一套頁面全部適應。
-
自適應佈局如果螢幕太小會發生內容過於擁擠。而響應式佈局正是為了解決這個問題而衍生出的概念,它可以自動識別螢幕寬度並做出相應調整的網頁設計。
外掛及工具相關
1. babel和polyfill
-
Babel
: Babel 是一個廣泛使用的 ES6 轉碼器,可以將 ES6 程式碼轉為 ES5 程式碼。注意:Babel 預設只轉換新的 JavaScript 句法(syntax),而不轉換新的 API -
Polyfill
: Polyfill的準確意思為,用於實現瀏覽器並不支援的原生API的程式碼。
-
jpg是jpeg的縮寫, 二者一致
-
PNG就是為取代GIF而生的, 無失真壓縮, 佔用記憶體多
-
jpg犧牲圖片質量, 有損, 佔用記憶體小
-
PNG格式可編輯。如圖片中有字型等,可利用PS再做更改。JPG格式不可編輯
前端效能優化
-
減少HTTP請求(合併css、js,雪碧圖/base64圖片)
-
壓縮(css、js、圖片皆可壓縮,使用webpack uglify和 svg)
-
樣式表放頭部,指令碼放底部
-
使用CDN(這部分,不少前端都不用考慮,負責釋出的兄弟可能會負責搞好)
-
http快取
-
bosify圖片壓縮: 根據具體情況修改圖片字尾或格式 後端根據格式來判斷儲存原圖還是縮圖
-
懶載入, 預載入
-
替代方案: 骨架屏, SSR
-
webpack優化
原生通訊
1.JSBridge通訊原理, 有哪幾種實現的方式?
JsBridge給JavaScript提供了呼叫Native功能,Native也能夠操控JavaScript。這樣前端部分就可以方便使用地理位置、攝像頭以及登入支付等Native能力啦。JSBridge構建 Native和非Native間訊息通訊的通道,而且是 雙向通訊的通道。
-
JS 向 Native 傳送訊息 : 呼叫相關功能、通知 Native 當前 JS 的相關狀態等。
-
Native 向 JS 傳送訊息 : 回溯呼叫結果、訊息推送、通知 JS 當前 Native 的狀態等。
演算法相關
1. 二分查詢和氣泡排序
-
二分查詢: 遞迴(分左右, 傳遞start,end引數)和非遞迴(使用while(l < h))
-
氣泡排序: 兩個for迴圈
function quickSort (arr) {
if (arr.length < 2) return arr
var middle = Math.floor(arr.length / 2)
var flag = arr.splice(middle, 1)[0]
var left = [],
right = []
for (var i = 0; i < arr.length; i++) {
if (arr[i] < flag) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return quickSort(left).concat([flag], quickSort(right))
}
3. 最長公共子串
function findSubStr(str1, str2) {
if (str1.length > str2.length) {
[str1, str2] = [str2, str1]
}
var result = ''
var len = str1.length
for (var j = len; j > 0; j--) {
for (var i = 0; i < len - j; i++) {
result = str1.substr(i, j)
if (str2.includes(result)) return result
}
}
}
console.log(findSubStr('aabbcc11', 'ppooiiuubcc123'))
4. 最長公共子序列(LCS動態規劃)
另一篇
// dp[i][j] 計算去最大長度,記住口訣:相等左上角加一,不等取上或左最大值
function LCS(str1, str2){
var rows = str1.split("")
rows.unshift("")
var cols = str2.split("")
cols.unshift("")
var m = rows.length
var n = cols.length
var dp = []
for(var i = 0; i < m; i++){
dp[i] = []
for(var j = 0; j < n; j++){
if(i === 0 || j === 0){
dp[i][j] = 0
continue
}
if(rows[i] === cols[j]){
dp[i][j] = dp[i-1][j-1] + 1 //對角+1
}else{
dp[i][j] = Math.max( dp[i-1][j], dp[i][j-1]) //對左邊,上邊取最大
}
}
console.log(dp[i].join(""))//除錯
}
return dp[i-1][j-1]
}
//!!!如果它來自左上角加一,則是子序列,否則向左或上回退。
//findValue過程,其實就是和 就是把T[i][j]的計算反過來。
// 求最長子序列
function findValue(input1,input2,n1,n2,T){
var i = n1-1,j=n2-1;
var result = [];//結果儲存在陣列中
console.log(i);
console.log(j);
while(i>0 && j>0){
if(input1[i] == input2[j]){
result.unshift(input1[i]);
i--;
j--;
}else{
//向左或向上回退
if(T[i-1][j]>T[i][j-1]){
//向上回退
i--;
}else{
//向左回退
j--;
}
}
}
console.log(result);
}
5. 陣列去重,多種方法
-
雙for迴圈, splice剔除並i--回退
-
indexOf等於index
-
filter indexOf === index
-
新陣列indexOf === index
-
使用空物件等
// 使用柯里化 + 遞迴
function curry ( fn ) {
var c = (...arg) => (fn.length === arg.length) ?
fn (...arg) : (...arg1) => c(...arg, ...arg1)
return c
}
7. 反轉二叉樹
var invertTree = function (root) {
if (root !== null) {
[root.left, root.right] = [root.right, root.left]
invertTree(root.left)
invertTree(root.right)
}
return root
}
8. 貪心演算法解決揹包問題
var items = ['A','B','C','D']
var values = [50,220,60,60]
var weights = [5,20,10,12]
var capacity = 32 //揹包容積
greedy(values, weights, capacity) // 320
function greedy(values, weights, capacity) {
var result = 0
var rest = capacity
var sortArray = []
var num = 0
values.forEach((v, i) => {
sortArray.push({
value: v,
weight: weights[i],
ratio: v / weights[i]
})
})
sortArray.sort((a, b) => b.ratio - a.ratio)
sortArray.forEach((v, i) => {
num = parseInt(rest / v.weight)
rest -= num * v.weight
result += num * v.value
})
return result
}
9. 輸入一個遞增排序的陣列和一個數字S,在陣列中查詢兩個數,使得他們的和正好是S,如果有多對數字的和等於S,輸出兩個數的乘積最小的。
function FindNumbersWithSum(array, sum)
{
var index = 0
for (var i = 0; i < array.length - 1 && array[i] < sum / 2; i++) {
for (var j = i + 1; j < array.length; j++) {
if (array[i] + array[j] === sum) return [array[i], array[j]]
}
//index = array.indexOf(sum - array[i], i + 1)
// if (index !== -1) {
// return [array[i], array[index]]
//}
}
return []
10. 二叉樹各種(層序)遍歷
深度廣度遍歷
// 根據前序和中序重建二叉樹
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function reConstructBinaryTree(pre, vin)
{
var result = null
if (pre.length === 1) {
result = {
val: pre[0],
left: null,
right: null
}
} else if (pre.length > 1) {
var root = pre[0]
var vinRootIndex = vin.indexOf(root)
var vinLeft = vin.slice(0, vinRootIndex)
var vinRight = vin.slice(vinRootIndex + 1, vin.length)
pre.shift()
var preLeft = pre.slice(0, vinLeft.length)
var preRight = pre.slice(vinLeft.length, pre.length)
result = {
val: root,
left: reConstructBinaryTree(preLeft, vinLeft),
right: reConstructBinaryTree(preRight, vinRight)
}
}
return result
}
// 遞迴
// 前序遍歷
function prevTraverse (node) {
if (node === null) return;
console.log(node.data);
prevTraverse(node.left);
prevTraverse(node.right);
}
// 中序遍歷
function middleTraverse (node) {
if (node === null) return;
middleTraverse(node.left);
console.log(node.data);
middleTraverse(node.right);
}
// 後序遍歷
function lastTraverse (node) {
if (node === null) return;
lastTraverse(node.left);
lastTraverse(node.right);
console.log(node.data);
}
// 非遞迴
// 前序遍歷
function preTraverse(tree) {
var arr = [],
node = null
arr.unshift(tree)
while (arr.length) {
node = arr.shift()
console.log(node.root)
if (node.right) arr.unshift(node.right)
if (node.left) arr.unshift(node.left)
}
}
// 中序遍歷
function middleTraverseUnRecursion (root) {
let arr = [],
node = root;
while (arr.length !== 0 || node !== null) {
if (node === null) {
node = arr.shift();
console.log(node.data);
node = node.right;
} else {
arr.unshift(node);
node = node.left;
}
}
}
// 廣度優先-層序遍歷
// 遞迴
var result = []
var stack = [tree]
var count = 0
var bfs = function () {
var node = stack[count]
if (node) {
result.push(node.value)
if (node.left) stack.push(node.left)
if (node.right) stack.push(node.right)
count++
bfs()
}
}
bfs()
console.log(result)
// 非遞迴
function bfs (node) {
var result = []
var queue = []
queue.push(node)
while (queue.length) {
node = queue.shift()
result.push(node.value)
node.left && queue.push(node.left)
node.right && queue.push(node.right)
}
return result
}
11. 各種排序
// 插入排序
function insertSort(arr) {
var temp
for (var i = 1; i < arr.length; i++) {
temp = arr[i]
for (var j = i; j > 0 && temp < arr[j - 1]; j--) {
arr[j] = arr[j - 1]
}
arr[j] = temp
}
return arr
}
console.log(insertSort([3, 1, 8, 2, 5]))
// 歸併排序
function mergeSort(array) {
var result = array.slice(0)
function sort(array) {
var length = array.length
var mid = Math.floor(length * 0.5)
var left = array.slice(0, mid)
var right = array.slice(mid, length)
if (length === 1) return array
return merge(sort(left), sort(right))
}
function merge(left, right) {
var result = []
while (left.length || right.length) {
if (left.length && right.length) {
if (left[0] < right[0]) {
result.push(left.shift())
} else {
result.push(right.shift())
}
} else if (left.length) {
result.push(left.shift())
} else {
result.push(right.shift())
}
}
return result
}
return sort(result)
}
console.log(mergeSort([5, 2, 8, 3, 6]))
// 二分插入排序
function twoSort(array) {
var len = array.length,
i,
j,
tmp,
low,
high,
mid,
result
result = array.slice(0)
for (i = 1; i < len; i++) {
tmp = result[i]
low = 0
high = i - 1
while (low <= high) {
mid = parseInt((high + low) / 2, 10)
if (tmp < result[mid]) {
high = mid - 1
} else {
low = mid + 1
}
}
for (j = i - 1; j >= high + 1; j--) {
result[j + 1] = result[j]
}
result[j + 1] = tmp
}
return result
}
console.log(twoSort([4, 1, 7, 2, 5]))
12. 使用尾遞迴對斐波那契優化
遞迴非常耗費記憶體,因為需要同時儲存成千上百個呼叫幀,很容易發生“棧溢位”錯誤(stack overflow)。但對於尾遞迴來說,由於只存在一個呼叫幀,所以永遠不會發生“棧溢位”錯誤。
// 傳統遞迴斐波那契, 會造成超時或溢位
function Fibonacci (n) {
if ( n <= 1 ) {return 1};
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
Fibonacci(10) // 89
Fibonacci(100) // 超時
Fibonacci(500) // 超時
// 使用尾遞迴優化, 可規避風險
function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
if( n <= 1 ) {return ac2};
return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}
Fibonacci2(100) // 573147844013817200000
Fibonacci2(1000) // 7.0330367711422765e+208
Fibonacci2(10000) // Infinity
13. 兩個升序數組合併為一個升序陣列
function sort (A, B) {
var i = 0, j = 0, p = 0, m = A.length, n = B.length, C = []
while (i < m || j < n) {
if (i < m && j < n) {
C[p++] = A[i] < B[j] ? A[i++] : B[j++]
} else if (i < m) {
C[p++] = A[i++]
} else {
C[p++] = B[j++]
}
}
return C
}
node相關
1. node的router是什麼2. 資料庫索引是啥
-
狹義上: 索引是資料庫針對每條資料自動生成的內部唯一id標識, 用以快速搜尋定位資料
-
廣義上: 是資料庫根據每條資料形成的關鍵字, 將劃分為樹形結構, 便於sql語句對資料的查詢, 使演算法複雜度降低到O(logn)