也不知道是何時我曾經立下一天一遍博文的豪言壯志,現在打開一看,有關技術的文章的建立時間都定格在2017年,豪言壯志當然不可靠,腳踏實地纔是硬道理。這幾個月個人變化很大,曾經懶惰睡懶覺的我天天都堅持6點起牀,如今我還在嘗試學着胖哥(技術胖)10點半睡覺4點半起牀,結果第一天就吃到了失敗的苦果。以上內容交代了文章主人公從一個懶惰it行業從事者慢慢向業界前輩學習的經歷。接下來我會記錄下關於前端面試的一些問題和解答,不能保證徹底是正確的,可是對我自身也算是種技術沉澱了。javascript
主要內容分爲 JS原生部分,vue框架使用部分和優化的部分。由於我最近在面試,因此可能會抽面試題來先寫,其實我也沒有太多思路,秉着沒人看的心態拼命亂寫,我仍是但願有我的在下面大聲說出博主你寫錯啦等等吐槽,我是菜雞我爲本身代鹽。html
因爲是我的總結因此並不會太詳細,也不會舉例去說明,只能按個人表達出來前端
前端的根基所在,原生JS是限制不少前端向更高方向發展的一個瓶之一,廢話很少說先從簡單的開始吧
複製代碼
這題看起很簡單,我會不假思索的答出const通常用來定義常量或者對象(對象類型在js中是保存一個指針地址因此不會有改變),而let咱們就用來定義變量,const是須要初始化的,在定義的時候就須要初始化賦值,而let則不用一開始就初始化,二者都不存在變量提高(暫時性死區),而且會造成一個塊級做用域,並且不能重複定義相同的變量名。如今咱們稍微整理一下他們的異同點,同時給他倆來個參照物var,得出如下結論。vue
相同點:html5
不一樣點:java
當程序的控制流程在新的做用域(module function 或 block 做用域)進行實例化時,在此做用域中用let/const聲明的變量會先在做用域中被建立出來,但所以時還未進行詞法綁定,因此是不能被訪問的,若是訪問就會拋出錯誤。所以,在這運行流程進入做用域建立變量,到變量能夠被訪問之間的這一段時間,就稱之爲暫時死區。git
翻譯一下人話就是,在函數做用域,或者使用let/const時造成的塊級做用域中,在執行到let/const之定義以前,定義的變量都是不可訪問的,在語法上這個暫時性死區(TDZ)。es6
面試超級喜歡問的問題,簡單來講,非要追求逼格的話google一下能夠找找到不少,這裏我只介紹最實用的方法,在處理性能方面方法3>方法1>方法2github
function unique(array) {
let temp = []
for (let i = 0, l = array.length; i < l; i++) {
if (temp.indexOf(arr[i]) === -1) temp.push(arr[i])
}
return temp
}
複製代碼
最普通的for循環,時間複雜度O(n^2) 空間複雜度O(n),固然咱們能夠價格hash表讓查找更加快速面試
function unique(array) {
let temp = []
let hash = {}
for (let i = 0, l = array.length; i < l; i++) {
if (!hash[array[i]]) {
hash[array[i]] = true //存入hash表
temp.push(arr[i])
}
}
return temp
}
複製代碼
這樣咱們就能夠把時間複雜度降到了O(n),但因爲新建了一個對象因此消耗內存方面要遠大於indexOf
function unique(array) {
return array.filter((item, index, array) => {
return array.indexOf(item) === index
})
}
複製代碼
這個性能應該是最差的了,從空間複雜度和時間複雜度上推論的
//Set 版本
function unique(array) {
return [...new Set(array))]
}
複製代碼
經測試這個最好
function unique(array: Array<number>): Array<number> {
let temp: Array<number> = []
for (let i: number = 0, l: number = array.length; i < l; i++) {
if (temp.indexOf(array[i]) === -1) temp.push(array[i])
}
return temp
}
複製代碼
ts用的很是少,隨手寫的,不夠健壯性,只能去掉重複的數字類型數組,二維數據,和其它類型的數據都沒有考慮到,由於比較隨意啊,因此我也不考慮那麼多了,總結下就行了
首先,事件冒泡和事件捕獲的提出都是爲了解決事件執行順序,咱們在使用中能夠經過addEventListener()的第三個參數來決定咱們註冊的事件是捕獲時觸發仍是冒泡時觸發,false的時候是冒泡觸發,true則爲捕獲。下面我就簡單舉個例子,請看如下代碼
<div id="el_1">
<p class="el_2">有本事點我</p>
<div>
//假設咱們給p添加一個點擊事件
複製代碼
事件冒泡:是微軟的方案,冒泡就像一塊石頭扔進水裏,氣泡會從底下往上冒,因此以上的事件執行順序是p>div>body>html>document
事件捕獲:網景公司提出,事件捕獲和事件冒泡相反,這時候p元素的點擊事件執行順序是 document>html>body>div>p
細心的小夥伴必定會發現咱們如今用的w3c標準實際上是存在事件冒泡和事件捕獲的,其實這就是w3c作的折中方案,這個方案就是先捕獲後冒泡,到事件target上時,則是誰先註冊誰就先執行,好比你在target上註冊了一個事件捕獲事件和事件冒泡事件,那決定他們執行順序的其實就是註冊的順序。
關於閉包,我想你們都很熟悉,此次我打算用變量對象的角度來解析閉包的原理。 閉包提供了函數外部訪問函數內部變量的能力,同時因爲沒法被回收會致使內存泄露等問題。
個人描述創建在有必定基礎的狀況下的,默認你明白函數執行上下文的建立,js垃圾回收機制,做用域鏈等基礎知識。
首先咱們講一下做用域的概念,做用域鏈其實由當前環境和上層環境一些類變量對象組成,它保證了當前環境對符合訪問權限的變量和函數的有序訪問。 這時候咱們舉例有一個執行上下文A和一個在執行上下文A下建立的函數也就是執行上下文B,當執行上下文A被激活的時候,這時候執行上下文就會被建立,在建立階段分別會建立變量對象,肯定this的指向,肯定做用域鏈, 以後就是執行階段,也就是執行咱們寫在函數體內的代碼,分別是變量賦值,函數引用和其它代碼,執行完畢後變執行上下文就會出棧,等待垃圾回收。 那麼閉包是什麼,上面提到了執行上下文B執行時若是訪問了執行上下文A的變量對象,那麼閉包就產生了,這時候函數B就是個閉包(這裏是有不一樣解釋的,各類大神書稱內層函數爲閉包,而chrome中則之外層函數爲閉包),變量對象中包含了argument,聲明的變量和聲明的函數,而咱們知道,函數的執行上下文,在執行完畢以後,生命週期結束,那麼該函數的執行上下文就會失去引用。其佔用的內存空間很快就會被垃圾回收器釋放。但是閉包的存在,會阻止這一過程。閉包的運用就很少說了,有柯里化等
這一個我以前寫過一篇總結,我就直接放進來了
history給咱們保存狀態的能力,經過pushState()添加激活歷史條目,經過replaceState()修改當前激活的歷史條目history接收三個參數
history.pushState({page:1},'title1','?page=1')
複製代碼
stateObj(狀態對象) : 是一個JavaScript對象類型,咱們能夠經過這個這個對象保存數據,能夠在新的歷史條目裏(新的頁面)獲取到這個對象
title(標題) :目前瀏覽器大多不支持,保險起見能夠傳一個空字符串
url(地址): 新的頁面地址,可選,傳入的地址必須是同源,不然pushState()會拋出異常l 不傳或傳空字符串新歷史條目默認爲當前文檔url
經過onpopstate事件的event對象會拷貝一份改歷史記錄條目的state,下面咱們來實現一個小案例。
history.pushState({
color: 'red',
},
'',
'?color=red') //添加並激活一個歷史條目,histroy.html?color=red
history.back() //返回上一條歷史條目 histroy.html
setTimeout(() => {
history.forward() //設置定時器後前進到下一條歷史條目 ,histroy.html?color=red
}, 1000)
// 狀態的歷史記錄條目發生變化時, popstate事件就會在對應window對象上觸發.
window.onpopstate = function (e) {
console.log(e.state)
if (e.state && e.state.color === 'red') {
document.body.style.color = 'red'
}
}
複製代碼
經過pushstate把頁面的狀態保存在state對象中,當頁面的url再變回這個url時,能夠經過event.state取到這個state對象,從而能夠對頁面狀態進行還原,這裏的頁面狀態就是頁面字體顏色,其實滾動條的位置,閱讀進度,組件的開關的這些頁面狀態均可以存儲到state的裏面。
補充資料
window.onpopstate = function (event) {
alert("location: " + document.location + ", state: " + JSON.stringify(event.state));
};
//綁定事件處理函數.
history.pushState({
page: 1
}, "title 1", "?page=1"); //添加並激活一個歷史記錄條目 http://example.com/example.html?page=1,條目索引爲1
history.pushState({
page: 2
}, "title 2", "?page=2"); //添加並激活一個歷史記錄條目 http://example.com/example.html?page=2,條目索引爲2
history.replaceState({
page: 3
}, "title 3", "?page=3"); //修改當前激活的歷史記錄條目 http://ex..?page=2 變爲 http://ex..?page=3,條目索引爲3
history.back(); // 彈出 "location: http://example.com/example.html?page=1, state: {"page":1}"
history.back(); // 彈出 "location: http://example.com/example.html, state: null
history.go(2); // 彈出 "location: http://example.com/example.html?page=3, state: {"page":3}
複製代碼
補充關於hash的一些知識點,咱們histroy是html5 的 api,在此以前還有個hash,瀏覽器經過記錄hash值讓頁面不刷新跳轉,背後的原理其實onhashchange 事件,該事件是在window上的,下面經過一個小demo來理解
<body>
<div>這是一段測試hash的小文字,改變hash值將觸發hashchange事件,這段文字的顏色取自hash值</div>
</body>
複製代碼
window.onhashchange = function (e) {
console.log('form:', e.newURL, 'from:', e.oldURL)
document.body.style.color = location.hash.slice(1)
console.log(location.hash)
}
複製代碼
這樣咱們就實現了對頁面狀態的保持,在chrome中前進後退咱們的頁面,但咱們只能經過修改#後面的值來達到咱們須要的效果,這樣自由性會很是低,因此有了咱們的history模式
預告:先佔個坑位,預告是單向數據流,mixin,低耦合可拓展路由配置
預告:組件分割,緩存,tree shaking,圖片優化