年末前端面試題小結(jié)

前言

分享一下最近面試碰到的一些題目,有些題目特別基礎(chǔ)的題目,比如箭頭函數(shù)什么作用這里就不寫了。

vue實(shí)現(xiàn)原理

最核心的方法便是通過Object.defineProperty()來實(shí)現(xiàn)對(duì)屬性的劫持,達(dá)到監(jiān)聽數(shù)據(jù)變動(dòng)的目的.

  1. 實(shí)現(xiàn)一個(gè)數(shù)據(jù)監(jiān)聽器Observer,能夠?qū)?shù)據(jù)對(duì)象的所有屬性進(jìn)行監(jiān)聽,如有變動(dòng)可拿到最新值并通知訂閱者
  2. 實(shí)現(xiàn)一個(gè)指令解析器Compile,對(duì)每個(gè)元素節(jié)點(diǎn)的指令進(jìn)行掃描和解析,根據(jù)指令模板替換數(shù)據(jù),以及綁定相應(yīng)的更新函數(shù)
  3. 實(shí)現(xiàn)一個(gè)Watcher,作為連接Observer和Compile的橋梁,能夠訂閱并收到每個(gè)屬性變動(dòng)的通知,執(zhí)行指令綁定的相應(yīng)回調(diào)函數(shù),從而更新視圖.
  4. mvvm入口函數(shù),整合以上三者

defineProperty有一個(gè)缺點(diǎn)就是你無法繞過定義屬性這一行為,es6有一個(gè)解決方法參考學(xué)習(xí)es6的proxy 和 reflect。 再分享一個(gè)Github上一個(gè)mvvm實(shí)現(xiàn):vue實(shí)現(xiàn)原理

vuex 和 redux 差異

Vuex 其實(shí)是一個(gè)針對(duì) Vue 特化的 Flux,主要是為了配合 Vue 本身的響應(yīng)式機(jī)制。當(dāng)然吸取了一些 Redux 的特點(diǎn),比如單狀態(tài)樹和便于測(cè)試和熱重載的 API,但是也選擇性的放棄了一些在 Vue 的場(chǎng)景下并不契合的特性,比如強(qiáng)制的 immutability(在保證了每一次狀態(tài)變化都能追蹤的情況下強(qiáng)制的 immutability 帶來的收益就很有限了)、為了同構(gòu)而設(shè)計(jì)得較為繁瑣的 API、必須依賴第三方庫(kù)才能相對(duì)高效率地獲得狀態(tài)樹的局部狀態(tài)等等(相比之下 Vuex 直接用 Vue 本身的計(jì)算屬性就可以)所以 Vue + Vuex 會(huì)更簡(jiǎn)潔,也不需要考慮性能問題,代價(jià)就是 Vuex 只能和 Vue 配合。Vue + Redux 也不是不可以,但是 Redux 作為一個(gè)泛用的實(shí)現(xiàn)和 Vue 的契合度肯定不如 Vuex。

作者:尤雨溪
鏈接:https://www.zhihu.com/question/38546875/answer/76970954
來源:知乎
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。

通過proxy實(shí)現(xiàn)數(shù)據(jù)劫持

   const OBSERVERS = new  Set(); //定義一個(gè)集合存放監(jiān)聽函數(shù)
   const observeFn = fn => OBSERVERS.add(fn);  //定義一個(gè)監(jiān)聽器函數(shù)
   const observeObj = obj => new Proxy(obj, {set});
   function set(target, key, value, receiver) { //劫持set
     const result = Reflect.set(target, key, value,receiver);
     OBSERVERS.forEach(observeFn => observeFn());
     return result;
  }
  
          var person = observeObj({
            name: '張三',
            age: 20
          });
  
          function print() {
            console.log(`${person.name}, ${person.age}`)
          }
          function warn() {
            alert(person.name)
          }
          observeFn(warn);
          observeFn(print);
          person.name = '李四';

react生命周期 & setState

組件掛載中執(zhí)行的函數(shù):

  1. constructor():ES6類的構(gòu)造函數(shù)(為了初始化state或綁定this)
  2. getInitialState():ES5中初始化state。(現(xiàn)在只需要在寫在1中)
  3. getDefaultProps():ES5中初始化props。在ES6中使用defaultProps()方法。(可以寫在1中,通過props)
  4. componentWillMount:組件掛載前調(diào)用,只執(zhí)行一次。
  5. render():渲染組件,必須實(shí)現(xiàn)該方法。
  6. componentDidMount: 組件掛載后調(diào)用,只執(zhí)行一次

組件的props或者state改變時(shí)更新函數(shù):

  1. componentWillReceiveProps(nextProps): 當(dāng)父組件的render()方法執(zhí)行后就會(huì)觸發(fā)該方法。初始化時(shí)不調(diào)用。
  2. shouldComponentUpdate(nextProps,nextState):當(dāng)props改變或state改變時(shí)調(diào)用,初始化時(shí)不調(diào)用,返回boolean。true表示繼續(xù)執(zhí)行render方法,fasle表示放棄本次渲染。
  3. render():渲染組件。

組件卸載函數(shù):

  1. componentWillUnmount():將組件從DOM樹移出,防止內(nèi)存溢出。

setState() 用于安排一個(gè)組件的 state 對(duì)象的一次更新。當(dāng)狀態(tài)改變時(shí),組件通過重新渲染來響應(yīng)。簡(jiǎn)單說一下原理,調(diào)用函數(shù),react內(nèi)部會(huì)執(zhí)行一個(gè)任務(wù),獲取當(dāng)前state,執(zhí)行state更新,如果一個(gè)任務(wù)還未完成,并發(fā)了很多個(gè)setstate,react會(huì)將這些set 統(tǒng)一成一個(gè)更新任務(wù),執(zhí)行后只會(huì)進(jìn)行一次dom更新。

虛擬dom原理

利用js解析html生成虛擬dom樹,大致結(jié)構(gòu)如下代碼,然后利用這個(gè)虛擬dom樹生成一個(gè)真的dom樹渲染到頁(yè)面。

diff算法:在改變dom的時(shí)候,會(huì)重新構(gòu)造一個(gè)dom樹,用來和舊的樹相比,提取差異的部分應(yīng)用到真實(shí)的dom樹上。

采用虛擬dom最根本的原因就是dom操作性能消耗極大,缺點(diǎn)就是要寫一大堆構(gòu)件虛擬dom的js代碼。根據(jù)項(xiàng)目dom操作多不多,權(quán)衡利弊合理使用虛擬dom。


var element = {
  tagName: 'ul', // 節(jié)點(diǎn)標(biāo)簽名
  props: { // DOM的屬性,用一個(gè)對(duì)象存儲(chǔ)鍵值對(duì)
    id: 'list'
  },
  children: [ // 該節(jié)點(diǎn)的子節(jié)點(diǎn)
    {tagName: 'li', props: {class: 'item'}, children: ["Item 1"]},
    {tagName: 'li', props: {class: 'item'}, children: ["Item 2"]},
    {tagName: 'li', props: {class: 'item'}, children: ["Item 3"]},
  ]
}

上面對(duì)應(yīng)的HTML寫法是:

<ul id='list'>
  <li class='item'>Item 1</li>
  <li class='item'>Item 2</li>
  <li class='item'>Item 3</li>
</ul>


移動(dòng)端屏幕適配 & 觸碰事件

  1. 采用flexable等插件,需要了解其原理(比如動(dòng)態(tài)改寫meta,html的font-size,設(shè)備dpr值等)
  2. 使用css3 嗅探改變font-size ,適配不同dpr的font-size

觸摸屏幕發(fā)生的事件

  • click: 類似pc的click,但連續(xù)觸發(fā)有200ms左右的延遲
  • touchstart:手指觸摸到屏幕會(huì)觸發(fā)
  • touchmove:當(dāng)手指在屏幕上移動(dòng)時(shí),會(huì)觸發(fā)
  • touchend:當(dāng)手指離開屏幕時(shí),會(huì)觸發(fā)
  • touchcancel:可由系統(tǒng)進(jìn)行的觸發(fā),比如手指觸摸屏幕的時(shí)候,突然alert了一下,或者系統(tǒng)中其他打斷了touch的行為,則可以 觸發(fā)該事件

express 和 koa 差異

  1. express采用es5的callback模式,而koa采用co框架實(shí)現(xiàn)promise回調(diào)
  2. koa只保留基本語法,拋棄了express內(nèi)置route,static等中間件

webpack原理及優(yōu)化

原理

  1. 初始化參數(shù):從配置文件和 Shell 語句中讀取與合并參數(shù),得出最終的參數(shù)
  2. 開始編譯:用上一步得到的參數(shù)初始化 Compiler 對(duì)象,加載所有配置的插件,執(zhí)行對(duì)象的 run 方法開始執(zhí)行編譯;
  3. 確定入口:根據(jù)配置中的 entry 找出所有的入口文件;
  4. 編譯模塊:從入口文件出發(fā),調(diào)用所有配置的 Loader 對(duì)模塊進(jìn)行翻譯,再找出該模塊依賴的模塊,再遞歸本步驟直到所有入口依賴的文件都經(jīng)過了本步驟的處理;
  5. 完成模塊編譯:在經(jīng)過第4步使用 Loader 翻譯完所有模塊后,得到了每個(gè)模塊被翻譯后的最終內(nèi)容以及它們之間的依賴關(guān)系;
  6. 輸出資源:根據(jù)入口和模塊之間的依賴關(guān)系,組裝成一個(gè)個(gè)包含多個(gè)模塊的 Chunk,再把每個(gè) Chunk 轉(zhuǎn)換成一個(gè)單獨(dú)的文件加入到輸出列表,這步是可以修改輸出內(nèi)容的最后機(jī)會(huì);
  7. 輸出完成:在確定好輸出內(nèi)容后,根據(jù)配置確定輸出的路徑和文件名,把文件內(nèi)容寫入到文件系統(tǒng)。

優(yōu)化

  • 利用UglifyPlugin刪除引用未使用的無用代碼,壓縮js代碼,css添加minimize參數(shù)壓縮css
  • 通過commonsChunkPlugin提取公共代碼
  • 通過ParallelUglifyPlugin多線程壓縮js代碼
  • 縮小文件范圍,通過exclude ,inclue等配置路徑,優(yōu)化打包效率
  • 通過happypack插件多線程處理loader
  • 通過dllplugin接入動(dòng)態(tài)鏈接庫(kù)

。。。webpack優(yōu)化的方法還有很多,有興趣的可以自行查閱

gulp基本原理

gulp可以認(rèn)為是grunt的升級(jí)版,運(yùn)用了node的流來處理文件,大大強(qiáng)化了性能。通過各種 Transform Stream 來實(shí)現(xiàn)文件的處理,然后再進(jìn)行輸出。Transform Streams 是 NodeJS Stream 的一種,是可讀又可寫的.

babel實(shí)現(xiàn)

  • babel-core:babel轉(zhuǎn)譯器本身,提供了babel的轉(zhuǎn)譯API,如babel.transform等,用于對(duì)代碼進(jìn)行轉(zhuǎn)譯。像webpack的babel-loader就是調(diào)用這些API來完成轉(zhuǎn)譯過程的。
  • babylon:js的詞法解析器
  • babel-traverse:用于對(duì)AST(抽象語法樹,想了解的請(qǐng)自行查詢編譯原理)的遍歷,主要給plugin用
  • babel-generator:根據(jù)AST生成代碼

babel的polyfill和runtime的區(qū)別:

Babel默認(rèn)只轉(zhuǎn)換新的JavaScript句法(syntax),而不轉(zhuǎn)換新的API,比如Set、Proxy、Reflect、Symbol、Promise等全局對(duì)象,以及一些定義在全局對(duì)象上的方法(比如Object.assign)都不會(huì)轉(zhuǎn)碼。

舉例來說,ES6在Array對(duì)象上新增了Array.from方法。Babel就不會(huì)轉(zhuǎn)碼這個(gè)方法。如果想讓這個(gè)方法運(yùn)行,必須使用babel-polyfill,為當(dāng)前環(huán)境提供一個(gè)墊片。

ssr和客戶端渲染的區(qū)別

* ssr 在服務(wù)器就把數(shù)據(jù)填充到html,返回的是完整的頁(yè)面,利于所搜索引擎seo
* 更快的內(nèi)容到達(dá)時(shí)間,特別是網(wǎng)絡(luò)較差的時(shí)候
* 服務(wù)端代碼沒有客戶端那么靈活組合
* 相比客戶端渲染,ssr有更高的服務(wù)器負(fù)載,成本增加。

垂直居中實(shí)現(xiàn)

///不知道父容器和自身高度

parentElement{
        position:relative;
    }

 childElement{
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
 }
 
 ///使用flex
 
 parentElement{
          display: flex;
          align-items: center;
    }
 


web安全

xss攻擊防御

  • 對(duì)輸出的html標(biāo)簽進(jìn)行轉(zhuǎn)義(htmlEncode)
  • 對(duì)用戶提交的數(shù)據(jù)進(jìn)行驗(yàn)證,限制長(zhǎng)度,特殊字符過濾等

sql注入防御

  • 層級(jí)化數(shù)據(jù)庫(kù)權(quán)限,不要使用管理員權(quán)限
  • 對(duì)用戶輸入的信息進(jìn)行數(shù)據(jù)庫(kù)查詢敏感詞的過濾,限制長(zhǎng)度等

后記

差不多就到這里了。這篇文章過年前po主將會(huì)持續(xù)更新,通過面試來找自己的不足是非常高效,面試官會(huì)針對(duì)你的簡(jiǎn)歷,你的所學(xué),針對(duì)性的考察你。前端路漫漫,其修遠(yuǎn)兮,慢慢走吧。

如果覺得本文對(duì)你有所幫助,就star一下吧~大傳送之術(shù)! 我的博客Github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 遇見一棵開花的樹, 便站在樹下沐浴芬芳。 如果畫下來, 更是一份小確幸。 十月的桂花香,經(jīng)久不散,當(dāng)時(shí)站在樹下出神...
    叮當(dāng)可好閱讀 290評(píng)論 0 0
  • 父親真的老了,變得孩子般纏人,每次打電話來,總是滿懷熱忱地問:你什么時(shí)候回家?且不說相隔一千多公里路,半夜起來趕飛...
    如意鴻閱讀 309評(píng)論 1 4
  • 我是小和的和閱讀 91評(píng)論 0 0

友情鏈接更多精彩內(nèi)容