說說Js的資料型別都有哪些
基本型別
- String
- Number
- Boolean
- null
- undefined
- symbol
引用型別
- object
說說Http狀態碼
1** 資訊,伺服器收到請求,需要請求者繼續執行操作(101,升級為websocket協議)
2** 成功,操作被成功接收並處理(206,部分內容,分段傳輸)
3** 重定向,需要進一步操作以完成請求(301,302重定向;304命中快取)
4** 客戶端錯誤,請求包含語法錯誤或無法完成請求(401,要求身份驗證;403,伺服器理解客服端需求,但是禁止訪問)
5** 伺服器錯誤,伺服器在處理請求的過程中發生了錯誤
說說ajax狀態碼,ajax一定是非同步的嗎?
ajax不一定是非同步的,可以通過open方法的第三個引數來配置(預設為true,非同步)
狀態碼:
0 - (未初始化)還沒有呼叫send()方法
1 - (載入)已呼叫send()方法,正在傳送請求
2 - (載入完成)send()方法執行完成
3 - (互動)正在解析響應內容
4 - (完成)響應內容解析完成,可以在客戶端呼叫了
說說ajax是什麼?優勢?劣勢?應該注意的問題?
ajax是一種和後臺通訊的標準。全稱是Asynchronous Javascript And XML(非同步javascript和XML)。
優勢:
- 無需重新整理頁面請求資料,可以使產品更快、更小、更友好
- 可以把以前服務端的任務轉嫁到客戶端來處理,減輕伺服器負擔,節省頻寬
- 瀏覽器支援好,無需外掛
劣勢:
- 不支援瀏覽器的回退按鈕
- 安全性存在問題,能夠在使用者不知情的情況下傳送請求
- 暴露了http互動細節
- 對搜尋引擎(網路爬蟲)的支援比較弱
- 程式不容易除錯
注意的問題:
- 瀏覽器相容性問題,這個問題jQuery等庫已經幫我們封裝好了
- 跨域問題,不同域之間不允許通過ajax進行訪問,可以參考阮一峰老師的跨域資源共享 CORS 詳解
- 為了更快的速度和對搜尋引擎友好,首頁儘量不要用ajax而是服務端渲染(當然這看分場景)
- ajax適合增刪改查操作
你把下面的表示式的列印結果寫出來
1.toString() //Uncaught SyntaxError: Invalid or unexpected token
true.toString() //"true"
[].toString() //""
{}.toString() //Uncaught SyntaxError: Unexpected token .
null.toString() //Uncaught TypeError: Cannot read property 'toString' of null
undefined.toString() //Uncaught TypeError: Cannot read property 'toString' of undefined
NaN.toString() //"NaN"
複製程式碼
這些需要刻意背一下,其中1和{}是語法錯誤。null和undefined是因為沒有toString
方法,可以使用call
來借用(想詳細瞭解,可以到評論區看我如何被罵的):
1..toString() //"1"
(1).toString() //"1"
Number(1).toString() //"1"
({}).toString() //[object Object]
Object.prototype.toString.call(null) //[object Null]
Object.prototype.toString.call(undefined) //[object Undefined]
複製程式碼
前端效能優化你瞭解哪些
內容層面
- 使用CDN
- 單域名、多域名,單域名可以減少DNS查詢次數,多域名可以增加瀏覽器並行下載數量,這需要權衡,一般同一個域下不要超過四個資源。
- 避免重定向(分場景)
- 避免404
網路層面
- 利用快取,可以參考另一篇文章手寫檔案伺服器,說說前後端互動
- 檔案壓縮(通過響應頭Accept-Encoding: gzip, deflate, br告訴伺服器你支援的壓縮型別)
- 按需載入,提取公共程式碼,tree-shaking等(都可以通過webpack來實現)
- 減少cookie大小
- 檔案合併,通過css雪碧圖合併圖片
- 檔案預載入、圖片懶載入
渲染層間
- js放底部,css放頂部
- 減少reflow(迴流)和repaint(重繪)
- 減少dom節點
程式碼層面
- 快取dom節點,減少節點查詢,css選擇器層級優化
- 減少dom節點操作
- 合理使用break、continue、return等,優化迴圈
- 像react用到的事件委託、物件池等手段
說說瀏覽器的reflow和repaint
瀏覽器解析過程
- 解析html生成dom樹
- 解析css
- 把css應用於dom樹,生成render樹(這裡記錄這每一個節點和它的樣式和所在的位置)
- 把render樹渲染到頁面
reflow(迴流)
reflow翻譯為迴流,指的是頁面再次構建render樹。每個頁面至少發生一次迴流,就是第一次載入頁面的時候
此外,當頁面中有任何改變可能造成文件結構發生改變(即元素間的相對或絕對位置改變),都會發生reflow,常見的有:
- 新增或刪除元素(opacity:0除外,它不是刪除)
- 改變某個元素的尺寸或位置
- 瀏覽器視窗改變(resize事件觸發)
repaint(重繪)
repaint翻譯為重繪,它可以類比為上面的第四步,根據render樹繪製頁面,它的效能損耗比迴流要小。每次迴流一定會發生重繪。此外,以下操作(不影響文件結構的操作,影響結構的會發生迴流)也會發生重繪:
- 元素的顏色、透明度改變
- text-align等
瀏覽器優化
我們不太容易精確知道哪些操作具體會造成哪些元素迴流,不同的瀏覽器都有不同的實現。但是確定是他們的的耗時是比較長的,因為涉及到大量的計算。
瀏覽器為了提升效能也對這個問題進行了優化。方案就是維護一個佇列,把所有需要回流和重繪的操作都快取起來,一段時間之後再統一執行。但是,有的時候我們需要獲取一些位置屬性,當我們一旦呼叫這些api的時候,瀏覽器不得不立即計算佇列以保證提供的資料是準確的。例如以下操作:
- offsetTop, offsetLeft, offsetWidth, offsetHeight
- scrollTop/Left/Width/Height
- clientTop/Left/Width/Height
- width,height
- getComputedStyle或者IE的currentStyle
注意問題
- 批量處理
- 使用DocumentFragment進行快取,這樣只引發一次迴流
- 把頻繁操作的元素先display:null,只引發兩次迴流
- cloneNode和replaceChild,只引發兩次迴流
- 不要頻繁更改style,而是更改class
- 避免頻繁呼叫offsetTop等屬性,在迴圈前把它快取起來
- 絕對定位具有複雜動畫的元素,否則會引起父元素和後續大量元素的頻繁迴流
如何去除字串首位空格?
//es6
' ab '.trim() //"ab"
//正則
' ab '.replace(/^\s*|\s*$/g,'') //"ab"
複製程式碼
如何獲取url中的查詢字串
function queryUrlParameter(str) {
let obj = {}
let reg = /([^?=&#]+)=([^?=&#]+)/g;
str.replace(reg, function () {
obj[arguments[1]] = arguments[2]
})
//如果加上hash
// reg = /#([^?&=#]+)/g
// if (reg.test(str)) {
// str.replace(reg, function () {
// obj.hash = arguments[1]
// })
// }
return obj
}
console.log(queryUrlParameter('http://www.baidu.com?a=1&b=2#12222')) //{ a: '1', b: '2'}
複製程式碼
如何實現一個深拷貝、深比較
深拷貝
function clone(obj) {
if (obj == null || typeof obj !== 'object') return obj
let newObj = null
// 時間物件有特殊性
if (obj.constructor === Date) {
newObj = new obj.constructor(obj)
} else {
newObj = obj.constructor()
}
for (let key in Object.getOwnPropertyDescriptors(obj)) {
newObj[key] = clone(obj[key])
}
return newObj
}
複製程式碼
深比較
function deepCompare(a, b){
if(a === null
|| typeof a !== 'object'
|| b === null
|| typeof b !== 'object'){
return a === b
}
const propsA = Object.getOwnPropertyDescriptors(a)
const propsB = Object.getOwnPropertyDescriptors(b)
if(Object.keys(propsA).length !== Object.keys(propsB).length){
return false
}
return Object.keys(propsA).every( key => deepCompare(a[key], b[key]))
}
複製程式碼
如何實現函式節流和防抖
節流
function throttle(fn, delay) {
delay = delay || 50
let statTime = 0
return function () {
statTime === 0 && fn.apply(this, arguments)
let currentTime = new Date()
if (currentTime - statTime > delay) {
fn.apply(this, arguments)
statTime = currentTime
}
}
}
let throttleFn = throttle(fn)
throttleFn()//只會執行一次
throttleFn()
throttleFn()
throttleFn()
複製程式碼
防抖
function debounce(fn, delay) {
delay = delay || 50
let timer = null
return function () {
let self = this
clearTimeout(timer)
timer = setTimeout(fn.bind(self, arguments), delay);
}
}
複製程式碼
你給我寫一個原生bind方法
Function.prototype._bind = function (context) {
let self = this
let args_1 = [].prototype.slice.call(arguments, 1)
return function () {
let args_2 = [].prototype.slice.call(arguments)
let args = args_1.concat(args_2)
return self.apply(context, args)
}
}
複製程式碼
這只是對bind的一種簡單實現,如果有興趣瞭解更多可以參考Javascript中bind()方法的使用與實現
如何實現一個陣列的展平
function (ary) {
return ary.toString().split(',')
}
複製程式碼
這是一個投機取巧的方法(面試寫個這個也湊合吧),如果有興趣可以搜尋一下其他實現方法
如何新增、刪除、移動、複製DOM節點
建立
- createTextNode() //建立文字節點
- createElement() //建立元素節點
- createDocumentFragment() //建立文件碎片
操作
- appendChild() //增加
- removeChild() //刪除
- replaceChild() //替換
- insertBefore() //插入
查詢
- getElementById()
- getElementByTagName()
- getElementByName()