js面試題小結(1)
技術標籤:面試陣列物件深拷貝效能優化web常見攻擊git常用命令手寫ajax
1.效能優化
1.1優化原則和方向
原則
- 多使用記憶體、快取或者其他方法
- 減少 CPU 計算、較少網路
方向
- **載入頁面和靜態資源**
- **頁面渲染**
1.2方法:
## 載入資源優化
- 靜態資源的壓縮合並(JS程式碼壓縮合並、CSS程式碼壓縮合並、雪碧圖)
- 靜態資源快取(資源名稱加 MD5 戳)
- 使用 CND 讓資源載入更快
- 使用 SSR 後端渲染,資料直接突出到 HTML 中
## 渲染優化
- CSS 放前面 JS 放後面
- 懶載入(圖片懶載入、下拉載入更多)
- 減少DOM 查詢,對 DOM 查詢做快取
- 事件節流
- 儘早執行操作(`DOMContentLoaded`)
1.3 事件節流
例如要在文字改變時觸發一個 change 事件,通過 keyup 來監聽。使用節流。
var textarea = document.getElementById('text') var timeoutId textarea.addEventListener('keyup', function () { if (timeoutId) { clearTimeout(timeoutId) } timeoutId = setTimeout(function () { // 觸發 change 事件 }, 100) })
1.4 儘早執行操作
window.addEventListener('load', function () {
// 頁面的全部資源載入完才會執行,包括圖片、視訊等
})
document.addEventListener('DOMContentLoaded', function () {
// DOM 渲染完即可執行,此時圖片、視訊還可能沒有載入完
})
2.常見的 web 攻擊方式有哪些,簡述原理?如何預防?
2.1前端端最常見的攻擊就是 XSS(Cross Site Scripting,跨站指令碼攻擊):很多大型網站(例如 FaceBook 都被 XSS 攻擊過)。舉一個例子,我在一個部落格網站正常發表一篇文章,輸入漢字、英文和圖片,完全沒有問題。但是如果我寫的是惡意的 js 指令碼,例如獲取到`document.cookie`然後傳輸到自己的伺服器上,那我這篇部落格的每一次瀏覽,都會執行這個指令碼,都會把自己的 cookie 中的資訊偷偷傳遞到我的伺服器上來。
預防 XSS 攻擊就得對輸入的內容進行過濾,過濾掉一切可以執行的指令碼和指令碼連結。大家可以參考[xss.js](https://github.com/leizongmin/js-xss)這個開源工具。
簡單總結一下,XSS 其實就是攻擊者事先在一個頁面埋下攻擊程式碼,讓登入使用者去訪問這個頁面,然後偷偷執行程式碼,拿到當前使用者的資訊。
2.2還有一個比較常見的攻擊就是 CSRF/XSRF(Cross-site request forgery,跨站請求偽造):它是借用了當前操作者的許可權來偷偷的完成某個操作,而不是拿到使用者的資訊。例如,一個購物網站,購物付費的介面是`http://buy.com/pay?id=100`,而這個介面在使用時沒有任何密碼或者 token 的驗證,只要開啟訪問就付費購買了。一個使用者已經登入了`http://buy.com`在選擇商品時,突然收到一封郵件,而這封郵件正文有這麼一行程式碼`<img src="http://buy.com/pay?id=100"/>`,他訪問了郵件之後,其實就已經完成了購買。
預防 CSRF 就是加入各個層級的許可權驗證,例如現在的購物網站,只要涉及到現金交易,肯定輸入密碼或者指紋才行。
3.JS中使用`typeof`能得到的哪些型別
typeof undefined // undefined
typeof 'abc' // string
typeof 123 // number
typeof true // boolean
typeof {} // object
typeof [] // object
typeof null // object
typeof console.log // function
4.說明 this 幾種不同的使用場景
- 作為建構函式執行
- 作為物件屬性執行
- 作為普通函式執行
- call apply bind
5.前端使用非同步的場景有哪些
setTimeout setInterval
網路請求
6.建構函式
function DomElement(selector) {
var result = document.querySelectorAll(selector)
var length = result.length
var i
for (i = 0; i < length; i++) {
this[i] = selectorResult[i]
}
this.length = length
}
// 修改原型
DomElement.prototype = {
constructor: DomElement,
get: function (index) {
return this[index]
},
forEach: function (fn) {
var i
for (i = 0; i < this.length; i++) {
const elem = this[i]
const result = fn.call(elem, elem, i)
if (result === false) {
break
}
}
return this
},
on: function (type, fn) {
return this.forEach(elem => {
elem.addEventListener(type, fn, false)
})
}
}
// 使用
var $div = new DomElement('div')
$div.on('click', function() {
console.log('click')
})
7.用於`replace`的示例
function trim(str) {
return str.replace(/(^\s+)|(\s+$)/g, '')
}
8.獲取`2017-06-10`格式的日期
function formatDate(dt) {
if (!dt) { dt = new Date() }
var year = dt.getFullYear()
var month = dt.getMonth() + 1
var date = dt.getDate()
if (month < 10) {
// 強制型別轉換
month = '0' + month
}
if (date < 10) {
// 強制型別轉換
date = '0' + date
}
// 強制型別轉換
return year + '-' + month + '-' + date
}
var dt = new Date()
var formatDate = formatDate(dt)
console.log(formatDate)
9.獲取隨機數,要求是長度一直的字串格式
var random = Math.random()
var random = random + '0000000000' // 後面加上 10 個零
var random = random.slice(0, 10)
console.log(random)
10.寫一個能遍歷物件和陣列的`forEach`函式
function forEach(obj, fn) {
var key
if (obj instanceof Array) {
// 準確判斷是不是陣列
obj.forEach(function (item, index) {
fn(index, item)
})
} else {
// 不是陣列就是物件
for (key in obj) {
fn(key, obj[key])
}
}
}
var arr = [1,2,3]
// 注意,這裡引數的順序換了,為了和物件的遍歷格式一致
forEach(arr, function (index, item) {console.log(index, item)})
var obj = {x: 100, y: 200}
forEach(obj, function (key, value) {console.log(key, value)})
11.如何檢測瀏覽器的型別
var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(isChrome)
12.拆解url的各部分
console.log(location.href)
console.log(location.protocol) // 'http:' 'https:'
console.log(location.pathname) // '/learn/199'
console.log(location.search)
console.log(location.hash)
13.編寫一個通用的事件監聽函式
function bindEvent(elem, type, selector, fn) {
if (fn == null) {
fn = selector
selector = null
}
elem.addEventListener(type, function (e) {
var target
if (selector) {
target = e.target
if (target.matches(selector)) {
fn.call(target, e)
}
} else {
fn(e)
}
})
}
14.手動編寫一個 ajax,不依賴第三方庫
var xhr = new XMLHttpRequest()
xhr.open("GET", "/api", false)
xhr.onreadystatechange = function () {
// 這裡的函式非同步執行,可參考之前 JS 基礎中的非同步模組
if (xhr.readyState == 4) {
if (xhr.status == 200) { alert(xhr.responseText) }
}
}
xhr.send(null)
xhr.readyState 的狀態嗎說明
- 0 - (未初始化)還沒有呼叫send()方法
- 1 -(載入)已呼叫send()方法,正在傳送請求
- 2 -(載入完成)send()方法執行完成,已經接收到全部響應內容
- 3 -(互動)正在解析響應內容
- 4 -(完成)響應內容解析完成,可以在客戶端呼叫了
### status
http 狀態嗎有 `2xx` `3xx` `4xx` `5xx` 這幾種,比較常用的有以下幾種
- 200 正常
- 404 找不到資源
- 5xx 伺服器端出錯了
15.跨域的幾種實現方式
①JSONP;②伺服器端設定 http header
16.請描述一下 cookie,sessionStorage 和 localStorage 的區別?
## cookie 有它致命的缺點:
- 儲存量太小,只有 4KB
- 所有 http 請求都帶著,會影響獲取資源的效率
- API 簡單,需要封裝才能用
## locationStorage 和 sessionStorage
後來,HTML5標準就帶來了`sessionStorage`和`localStorage`,先拿`localStorage`來說,它是專門為了瀏覽器端快取而設計的。其優點有:
- 儲存量增大到 5M
- 不會帶到 http 請求中
- API 適用於資料儲存 `localStorage.setItem(key, value)` `localStorage.getItem(key)`
17.寫出一些常用的 git 命令
- git add .
- git checkout xxx
- git commit -m "xxx"
- git push origin master
- git pull origin master
- git stash / git stash pop
18.簡述多人使用 git 協作開發的基本流程
- git branch
- git checkout -b xxx / git checkout xxx
- git merge xxx
19.上線和迴歸
### 上線原理
- 將測試完成的程式碼提交到git版本庫的master分支
- 將當前伺服器的程式碼全部打包並記錄版本號,備份
- 將master分支的程式碼提交覆蓋到線上伺服器,生成新版本號
### 回滾原理
- 將當前伺服器的程式碼打包並記錄版本號,備份
- 將備份的上一個版本號解壓,覆蓋到線上伺服器,並生成新的版本號
20.從輸入url到得到html的詳細過程
- 瀏覽器根據 DNS 伺服器得到域名的 IP 地址
- 向這個 IP 的機器傳送 http 請求
- 伺服器收到、處理並返回 http 請求
- 瀏覽器得到返回內容
21.瀏覽器渲染頁面的過程
- 根據 HTML 結構生成 DOM Tree
- 根據 CSS 生成 CSS Rule
- 將 DOM 和 CSSOM 整合形成 RenderTree
- 根據 RenderTree 開始渲染和展示
- 遇到`<script>`時,會執行並阻塞渲染
22.window.onload 和 DOMContentLoaded 的區別
- 頁面的全部資源載入完才會執行,包括圖片、視訊等
- DOM 渲染完即可執行,此時圖片、視訊還沒有載入完
23.ES6
- 搭建 ES6 編譯環境(使用 babel 編譯,使用 webpack 做模組化處理)
- modules:關鍵字`import` `export`
- 瑣碎的功能:let/const,多行字串/模板變數,解構賦值,塊級作用域,函式預設引數,箭頭函式
- class:用法、和原型的關係、繼承
- Promise:用法
// 載入一張圖片
function loadImg(src) {
const promise = new Promise(function (resolve, reject) {
var img = document.createElement('img')
img.onload = function () {
resolve(img)
}
img.onerror = function () {
reject()
}
img.src = src
})
return promise
}
var src = 'http://www.imooc.com/static/img/index/logo_new.png'
var result = loadImg(src)
result.then(function (img) {
console.log(img.width)
}, function () {
console.log('failed')
}).then(function (img) {
console.log(img.height)
})
24.event-loop
將 js 中要執行的每個任務都做一個劃分,同步執行的放在“執行棧”,而非同步執行的**將**放在(不是馬上就放進去)“非同步佇列”。然後,將“執行棧”放在主執行緒中執行,挨個任務排隊執行,執行到最後就立馬去看“非同步佇列”是否有資料,有資料就拿到主執行緒中執行,執行完再去看“非同步佇列”是否有資料。
25.非同步的變同步的解決方案(非同步本身沒問題,但callback hell是有問題的,所以需要處理)
25.1 Promise (then的方式,參考23)
25.2 asyc-await(注意,async/await 並不是取代了 promise ,而是利用 promise 之後的另外一種寫法,取代了 then 函式,寫起來更加像同步程式碼,看著從上到下書寫,也從上到下執行)
function loadImg(src) {
const promise = new Promise(function (resolve, reject) {
var img = document.createElement('img')
img.onload = function () { resolve(img) }
img.onerror = function () { reject() }
img.src = src
})
return promise
}
const src1 = 'http://www.imooc.com/static/img/index/logo_new.png'
const src2 = 'https://avatars3.githubusercontent.com/u/9583120'
const load = async function () {
const result1 = await loadImg(src1)
console.log(result1)
const result2 = await loadImg(src2)
console.log(result2)
}
load()
26.陣列物件深拷貝(重點在遞迴呼叫,例如在物件層級好幾層(特別深的時候))
function deepClone(obj = {}) {
//如果不是物件直接返回
if(typeof obj !== 'object' || obj == null) { return obj }
let result;
if(obj instanceof Array) {
result = []
} else {
result = {}
}
for(let key in obj) {
// 保證key不是原型屬性
if(obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key])
}
}
return result
}
27.手寫jQuery
class Jquery{
constructor(selector){
const result = document.querySelectorAll(selector)
const length = result.length;
for(let i=0;i<result.length;i++){
this[i]=result[i]
}
this.length = result.length
this.selector = selector
}
get(index){
return this[index]
}
each(fn){
for (let i=0;i<this.length;i++){
const elem = this[i]
fn(elem)
}
}
on(type,fn){
return this.each(elem=>{
elem.addEventListener(type,fn,false)
})
}
}
28.手寫bind函式
Function.prototype.bind1 = function(){
// 將引數拆解為陣列
const args = Array.prototype.slice.call(arguments)
// 獲取this(陣列第一項)
const t = args.shift()
// fn1.bind(...) 中的fn1
const self = this
return function(){
return self.apply(t,args)
}
}
29.防抖(輸入框輸入keyword,做搜尋)
function debounce(fn,delay=500){
let timer = null
return function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.apply(this,arguments)
timer = null
},delay)
}
}
30.節流(拖拽根據位置做一些邏輯操作)
function throttle(fn, delay = 100) {
let timer = null
return function() {
if(timer) {
return
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}
31.事件繫結(普通事件和事件委託)
function bindEvent(elem,type,selector,fn){
if(fn==null){
fn=selector
selector = null
}
elem.addEventListener(type,event=>{
let target = event.target
if( selector ){
if(target.matches(selector)){
fn.call(target,event)
}
}else{
fn.call(target,event)
}
})
}