2019 Web 前端熱點筆試面試題總結【終結版】

提醒:我只是答案的搬運工,如果在瀏覽中發現有錯誤,歡迎評論中提出來,我好修改,謝謝!

簡述異步和同步的區別:

同步:瀏覽器訪問服務器請求,用戶看得到頁面刷新,重新發請求,等請求完,頁面刷新,新內容出現,用戶看到新內容,進行下一步操作

異步:瀏覽器訪問服務器請求,用戶正常操作,瀏覽器後端進行請求。等請求完,頁面不刷新,新內容也會出現,用戶看到新內容

簡述JavaScript基本數據類型

undefined、null、string、boolean、number、symbol

簡述div元素和span元素的區別

DIV(division)是一個塊級元素,可以包含段落、標題、表格,乃至諸如章節、摘要和備註等。

SPAN 是行內元素,SPAN 的前後是不會換行的,它沒有結構的意義,純粹是應用樣式,當其他行內元素都不合適時,可以使用SPAN。塊元素相當於內嵌元素在前後各加一個換行。其實,塊元素和行內元素也不是一成不變的,只要給塊元素定義display:inline,塊元素就成了內嵌元素,同樣地,給內嵌元素定義了display:block就成了塊元素了。 

什麼是css預處理器|後處理器

CSS預處理器定義了一種新的語言,其基本思想是,用一種專門的編程語言,爲CSS增加了一些編程的特性,將CSS作爲目標生成文件,然後開發者就只要使用這種語言進行編碼工作。通俗的說,CSS預處理器用一種專門的編程語言,進行Web頁面樣式設計,然後再編譯成正常的CSS文件。CSS 後處理器是對 CSS 進行處理,並最終生成 CSS 的預處理器,它屬於廣義上的 CSS 預處理器。

Css優先級算法如何計算

通配符選擇器:0  元素選擇符:1  關係選擇器:1  僞元素選擇器:1  類選擇符:10  屬性選擇器:10 僞類選擇器:10  id選擇器:100  內聯樣式:1000  !important聲明的樣式優先級最高

This指向問題

Display有哪些值及作用

  1. block :塊對象的默認值。用該值爲對象之後添加新行
  2. none :隱藏對象。與visibility屬性的hidden值不同,其不爲被隱藏的對象保留其物理空間
  3. inline :內聯對象的默認值。用該值將從對象中刪除行
  4. compact :分配對象爲塊對象或基於內容之上的內聯對象
  5. marker :指定內容在容器對象之前或之後。要使用此參數,對象必須和:after及:before 僞元素一起使用
  6. inline-table :將表格顯示爲無前後換行的內聯對象或內聯容器
  7. list-item :將塊對象指定爲列表項目。並可以添加可選項目標誌
  8. run-in :分配對象爲塊對象或基於內容之上的內聯對象
  9. table :將對象作爲塊元素級的表格顯示

position的值

  1. static(默認):在一般情況下,我們不需要特別的去聲明它,但有時候遇到繼承的情況,我們不願意見到元素所繼承的屬性影響本身,從而可以用Position:static取消繼承,即還原元素定位的默認值。設置爲 static 的元素,它始終會處於頁面流給予的位置(static 元素會忽略任何 top、 bottom、left 或 right 聲明)。一般不常用。
  2. relative(相對定位):相對定位是相對於元素默認的位置的定位,它偏移的 top,right,bottom,left 的值都以它原來的位置爲基準偏移,而不管其他元素會怎麼 樣。注意 relative 移動後的元素在原來的位置仍佔據空間。
  3. absolute(絕對定位):設置爲 absolute 的元素,如果它的 父容器設置了 position 屬性,並且 position 的屬性值爲 absolute 或者 relative,那麼就會依據父容器進行偏移。如果其父容器沒有設置 position 屬性,那麼偏移是以 body 爲依據。注意設置 absolute 屬性的元素在標準流中不佔位置。
  4. fixed(固定定位):位置被設置爲 fixed 的元素,可定位於相對於瀏覽器窗口的指定座標。不論窗口滾動與否,元素都會留在那個位置。它始終是以 body 爲依據的。 注意設置 fixed 屬性的元素在標準流中不佔位置。

介紹JavaScript有哪些內置對象

1.ObjectJavaScript中所有對象的父對象

2.數據封裝類對象:Object、Array、Boolean、Number和String

3.其他對象:Function、Arguments、Math、Date、RegExp、Error

JavaScript作用域鏈

JS引擎中,通過標識符查找標識符的值,會從當前作用域向上查找,直到作用域找到第一個匹配的標識符位置。就是JS的作用域鏈。

模塊化的好處及實現方式

解決命名衝突;提供複用性;提高代碼可維護性

立即執行函數;AMDCMDCommonjsES Module

什麼是window對象,什麼是document對象

window它是一個頂層對象,而不是另一個對象的屬性即瀏覽器的窗口。

document對象是window對象的一個對象屬性

Document.writeinnerHTML的區別

document.write是直接寫入到頁面的內容流,如果在寫之前沒有調用document.open, 瀏覽器會自動調用open。每次寫完關閉之後重新調用該函數,會導致頁面被重寫。

innerHTML則是DOM頁面元素的一個屬性,代表該元素的html內容。你可以精確到某一個具體的元素來進行更改。如果想修改document的內容,則需要修改document.documentElement.innerElement。

innerHTML將內容寫入某個DOM節點,不會導致頁面全部重繪;innerHTML很多情況下都優於document.write,其原因在於其允許更精確的控制要刷新頁面的那一個部分。

Map,filter,reduce各自有什麼作用

  1. Map是生成一個新數組,遍歷原數組,將每個元素拿出來做一些變換然後放入到新的數組中。
  2. Filter是生成一個新數組,在遍歷數組的時候將返回值爲 true 的元素放入新數組,我們可以利用這個函數刪除一些不需要的元素。
  3. Reduce將數組中的元素通過回調函數最終轉換爲一個值。

寫出一個圖片懶加載的實現步驟

先將img標籤的src鏈接設爲同一張圖片(比如空白圖片),然後給img標籤設置自定義屬性(比如 data-src),然後將真正的圖片地址存儲在data-src中,當JS監聽到該圖片元素進入可視窗口時,將自定義屬性中的地址存儲到src屬性中。達到懶加載的效果。

Doctype作用,標準模式和兼容模式的區別

DOCTYPEdocument type (文檔類型) 的縮寫。<!DOCTYPE >聲明位於文檔的最前面,處於標籤之前,它不是html標籤。主要作用是告訴瀏覽器的解析器使用哪種HTML規範或者XHTML規範來解析頁面。

標準模式和兼容模式都是瀏覽器的呈現模式,瀏覽器究竟使用兼容模式還是標準模式呈現頁面與網頁中的DTD(文件類型定義)有關,DTD裏面包含了文檔的規則。

標準模式是指瀏覽器按照W3C標準來解析代碼,呈現頁面;兼容模式,是指瀏覽器按照自己的方式來解析代碼,使用一種比較寬鬆的向後兼容的方式來顯示頁面。

介紹下您對瀏覽器內核的理解

使用Trident內核的瀏覽器:IE、Maxthon、TT、The World等;使用Gecko內核的瀏覽器:Netcape6及以上版本、FireFox、MozillaSuite/SeaMonkey;使用Presto內核的瀏覽器:Opera7及以上版本;使用Webkit內核的瀏覽器:Safari、Chrome

簡述一下對HTML語義化的理解

用正確的標籤做正確的事情;html語義化就是讓頁面的內容結構化,便於對瀏覽器、搜索引擎解析;在沒有樣式CSS情況下也以一種文檔格式顯示,並且是容易閱讀的;搜索引擎的爬蟲依賴於標記來確定上下文和各個關鍵字的權重,利於SEO;使閱讀源代碼的人對網站更容易將網站分塊,便於閱讀維護理解。

Iframe有哪些缺點

iframe會阻塞主頁面的Onload事件;搜索引擎的檢索程序無法解讀這種頁面,不利於SEO;iframe和主頁面共享連接池,而瀏覽器對相同域的連接有限制,所以會影響頁面的並行加載;使用iframe之前需要考慮這兩個缺點。如果需要使用iframe,最好是通過javascript動態給iframe添加src屬性值,這樣可以繞開以上兩個問題。

塊級元素有哪些;行內元素有哪些;空(void)元素有哪些

塊級元素:div ul ol li dl dt dd h1 h2 h3 h4

行內元素:a b span img input select strong

空元素:br hr img input link meta

Display:nonevisibility:hidden的區別

display:none 不顯示對應的元素,在文檔佈局中不再分配空間(迴流+重繪)

visibility:hidden 隱藏對應元素,在文檔佈局中仍保留原來的空間(重繪)

簡單的介紹下彈性盒子模型的屬性和屬性值

flex-direction

row:橫向從左到右排列(左對齊),默認的排列方式。

row-reverse:反轉橫向排列(右對齊,從後往前排,最後一項排在最前面。

column:縱向排列。

column-reverse:反轉縱向排列,從後往前排,最後一項排在最上面。

flex-wrap

nowrap(默認):不換行。

wrap:換行,第一行在上方。

wrap-reverse:換行,第一行在下方。

flex-flow

flex-direction屬性和flex-wrap屬性的簡寫形式,默認值爲row nowrap

justify-content

flex-start:從左到右排列

flex-end:從右到左排列

center:中間開始排列

space-between:平分

space-around:平分,且兩邊佔1/2

align-items

flex-start:交叉軸的起點對齊。

flex-end:交叉軸的終點對齊。

center:交叉軸的中點對齊。

baseline: 項目的第一行文字的基線對齊。

stretch(默認值):如果項目未設置高度或設爲auto,將佔滿整個容器的高度。

align-content

flex-start:與交叉軸的起點對齊。

flex-end:與交叉軸的終點對齊。

center:與交叉軸的中點對齊。

space-between:與交叉軸兩端對齊,軸線之間的間隔平均分佈。

space-around:每根軸線兩側的間隔都相等。所以,軸線之間的間隔比軸線與邊框的間隔大一倍。

stretch(默認值):軸線佔滿整個交叉軸。

px、em、rem、%、vw、vh、vm這些單位的區別

Px 就是pixel的縮寫,意爲像素。px就是一張圖片最小的一個點,一張位圖就是千千萬萬的這樣的點構成的,比如常常聽到的電腦像素是1024x768的,表示的是水平方向是1024個像素點,垂直方向是768個像素點。

Em 參考物是父元素的font-size,具有繼承的特點。如果自身定義了font-size按自身來計算(瀏覽器默認字體是16px),整個頁面內1em不是一個固定的值。

Rem  css3新單位,相對於根元素html(網頁)的font-size,不會像em那樣,依賴於父元素的字體大小,而造成混亂。

% 一般廣泛的講是相對於父元素,但是並不是十分準確。

1、對於普通定位元素就是我們理解的父元素

2、對於position: absolute;的元素是相對於已定位的父元素

3、對於position: fixed;的元素是相對於 ViewPort(可視窗口)

Vw css3新單位,viewpoint width的縮寫,視窗寬度,1vw等於視窗寬度的1%。

舉個例子:瀏覽器寬度1200px, 1 vw = 1200px/100 = 12 px。

Vh css3新單位,viewpoint height的縮寫,視窗高度,1vh等於視窗高度的1%。

舉個例子:瀏覽器高度900px, 1 vh = 900px/100 = 9 px。

Vm css3新單位,相對於視口的寬度或高度中較小的那個。其中最小的那個被均分爲100單位的vm

舉個例子:瀏覽器高度900px,寬度1200px,取最小的瀏覽器高度, 1 vm = 900px/100 = 9 px。

說明cookie,session的概念與區別,另外說出你知道的瀏覽器緩存方式

cookie是客戶端記錄保存用戶身份信息;session是服務器記錄用戶信息的

 

cookie是保存在客戶端的,而session是保存在服務器的;

cookie不是很安全,別人可以分析存放在本地的COOKIE並進行COOKIE欺騙,如果主要考慮到安全應當使用session

session會在一定時間內保存在服務器上。當訪問增多,會比較佔用你服務器的性能,如果主要考慮到減輕服務器性能方面,應當使用COOKIE

單個cookie在客戶端的限制是4K,就是說一個站點在客戶端存放的COOKIE不能超過4K。

將登陸信息等重要信息存放爲SESSION;其他信息如果需要保留,可以放在COOKIE中。

 

  1. http緩存是基於HTTP協議的瀏覽器文件級緩存機制。即針對文件的重複請求情況下,瀏覽器可以根據協議頭判斷從服務器端請求文件還是從本地讀取文件,chrome控制檯下的Frames即展示的是瀏覽器的http文件級緩存。
  2. IndexedDB 是一個爲了能夠在客戶端存儲可觀數量的結構化數據,並且在這些數據上使用索引進行高性能檢索的 API
  3. Cookie(或者Cookies),指一般網站爲了辨別用戶身份、進行session跟蹤而儲存在用戶本地終端上的數據(通常經過加密)。
  4. localStorage是html5的一種新的本地緩存方案,目前用的比較多,一般用來存儲ajax返回的數據,加快下次頁面打開時的渲染速度。
  5. sessionStoragelocalstorage類似,但是瀏覽器關閉則會全部刪除,apilocalstorage相同,實際項目中使用較少。
  6. application cahce是將大部分圖片資源、jscss等靜態資源放在manifest文件配置中。當頁面打開時通過manifest文件來讀取本地文件或是請求服務器文件。
  7. Flash緩存主要基於flash有讀寫瀏覽器端本地目錄的功能,同時也可以向js提供調用的api,則頁面可以通過js調用flash去讀寫特定的磁盤目錄,達到本地數據緩存的目的

Ajax實現機制,簡述ajax跨域問題並提出解決方案

創建XMLHttpRequest對象。設置請求方式。調用回調函數。發送請求。

 

主流的前後端分離模式下,當前端調用後臺接口時,由於是在非同一個域下的請求,從而會引發瀏覽器的自我安全保護機制,最終結果是接口成功請求並響應,但前端不能正常處理該返回數據。因此,當同時滿足以下三個條件的情況下,就會出現跨域問題:瀏覽器限制;非同源請求(跨域);發送的是 XHR ( XMLHttpRequest ) 請求

 

通過jsonp跨域;document.domain + iframe跨域;location.hash + iframe跨域;window.name + iframe跨域;postMessage跨域;跨域資源共享(CORS);nginx代理跨域;nodejs中間件代理跨域;WebSocket協議跨域

靜態鏈接與動態鏈接的區別,爲什麼需要動態鏈接

靜態鏈接和動態鏈接兩者最大的區別就在於鏈接的時機不一樣,靜態鏈接是在形成可執行程序前,而動態鏈接的進行則是在程序執行時

動態鏈接出現的原因就是爲了解決靜態鏈接中提到的兩個問題,一方面是空間浪費,另外一方面是更新困難。

從瀏覽器地址欄輸入url到網頁徹底打開,中間發送了什麼

  1. DNS解析:客戶端輸入域名後,由DNS服務器來將域名解析成對應服務器的IP地址;

瀏覽器DNS緩存;系統DNS緩存;路由器DNS緩存;網絡運營商DNS緩存。

  1. TCP連接:獲得服務器IP之後,就需要三次握手的協議才能建立連接;

第一次握手:由瀏覽器發起請求,告訴服務器我要發起請求了;

第二次握手:由服務器發起,告訴瀏覽器我準備接收了,發送吧;

第三次握手:有瀏覽器發起,告訴服務器馬上發,準備接收。

  1. 發送請求:與服務器建立連接之後,就可以向服務器發送請求了,請求需要遵循http協議;
  2. 接受響應:被請求的服務器解析用戶請求的有哪些資源,通過服務器返回數據給客戶端;
  3. 給客戶端返回請求的狀態碼,通過狀態碼可以知道服務器端的處理是否正常;
  4. 渲染頁面:返回成功之後,瀏覽器拿到請求頁面的代碼,將其解析渲染出來;

遇見HTML標記,瀏覽器調用HTML解析器解析成Token並構建成DOM樹

遇見style/link標記,瀏覽器調用CSS解析器,處理css標記並構建CSSOM樹

遇見Script標記,調用JavaScript解析器,處理script代碼(綁定事件,修改DOM樹CSSOM樹)

將DOM樹和CSSOM樹合併成一個渲染樹

根據渲染樹來計算佈局,計算每個節點的幾何信息(佈局)

將各個節點顏色繪製到屏幕上(渲染)

  1. 斷開連接:數據傳輸完畢,需要斷開tcp連接,此時tcp發起4次揮手;

第一次揮手:由瀏覽器發起,發送給服務器,資源發送完畢(請求報文),你準備關閉吧

第二次揮手:由服務器發起,告訴瀏覽器資源接受完畢(請求報文),我準備關閉,你也準備吧

第三次揮手:由服務器發起,告訴瀏覽器我資源發送完畢(響應報文),你準備關閉吧

第四次揮手:由瀏覽器發起,告訴服務器資源接受完畢,我準備關閉(響應報文),你也準備吧

JavaScript的prototype原型的作用是什麼

prototype允許我們在創建對象之後來改變對象或類的行爲,並且這些通過prototype屬性添加的字段或方法所有對象實例是共享的。

列出幾個爲瀏覽器兼容而做的處理

1.不同瀏覽器的標籤默認的外補丁( margin )和內補丁(padding)不同?解決方案: css 裏增加通配符 * { margin: 0; padding: 0; }

2.IE6雙邊距問題;在 IE6中設置了float , 同時又設置margin , 就會出現邊距問題?解決方案:設置display:inline;

3.當標籤的高度設置小於10px,在IE6、IE7中會超出自己設置的高度?解決方案:超出高度的標籤設置overflow:hidden,或者設置line-height的值小於你的設置高度

4.圖片默認有間距?解決方案:使用float 爲img 佈局

5.邊距重疊問題;當相鄰兩個元素都設置了margin 邊距時,margin 將取最大值,捨棄最小值;?解決方案:爲了不讓邊重疊,可以給子元素增加一個父級元素,並設置父級元素爲overflow:hidden

6.cursor:hand 顯示手型在safari 上不支持?解決方案:統一使用 cursor:pointer

7.兩個塊級元素,父元素設置了overflow:auto;子元素設置了position:relative ;且高度大於父元素,在IE6、IE7會被隱藏而不是溢出?解決方案:父級元素設置position:relative

簡述事件傳播的各個階段

1.捕獲階段:事件傳播由目標節點的祖先節點逐級傳播到目標節點。先由文檔的根節點document(window)開始觸發對象,最後傳播到目標節點,從外向內捕獲事件對象;

2.目標階段:事件到達目標對象,事件觸發,如果事件不允許冒泡,事件會在這一階段停止傳播。

3.冒泡階段:從目標節點逐級傳播到document節點。

position 屬性值的含義

Static:元素框正常生成。塊級元素生成一個矩形框,作爲文檔流的一部分,行內元素則會創建一個或多個行框,置於其父元素中。

Relative:元素框偏移某個距離。元素仍保持其未定位前的形狀,它原本所佔的空間仍保留。

Absolute:元素框從文檔流完全刪除,並相對於其包含塊定位。包含塊可能是文檔中的另一個元素或者是初始包含塊。元素原先在正常文檔流中所佔的空間會關閉,就好像元素原來不存在一樣。元素定位後生成一個塊級框,而不論原來它在正常流中生成何種類型的框。

Fixed:元素框的表現類似於將 position 設置爲 absolute,不過其包含塊是視窗本身。

http請求中的8種請求方法

1、GET方法:發送一個請求來取得服務器上的某一資源

2、POST方法:向URL指定的資源提交數據或附加新的數據

3、PUT方法:跟POST方法很像,也是想服務器提交數據。但是,它們之間有不同。PUT指定了資源在服務器上的位置,而POST沒有

4、HEAD方法:只請求頁面的首部

5、DELETE方法:刪除服務器上的某資源

6、OPTIONS方法:它用於獲取當前URL所支持的方法。如果請求成功,會有一個Allow的頭包含類似“GET,POST”這樣的信息

7、TRACE方法:TRACE方法被用於激發一個遠程的,應用層的請求消息迴路

8、CONNECT方法:把請求連接轉換到透明的TCP/IP通道

GET和POST提交的區別

GET產生一個TCP數據包;POST產生兩個TCP數據包(Firefox只發送一次)。GET在瀏覽器回退時是無害的,而POST會再次提交請求。GET產生的URL地址可以被Bookmark,而POST不可以。GET請求會被瀏覽器主動cache,而POST不會,除非手動設置。GET請求只能進行url編碼,而POST支持多種編碼方式。GET請求參數會被完整保留在瀏覽器歷史記錄裏,而POST中的參數不會被保留。GET請求在URL中傳送的參數是有長度限制的,而POST沒有。對參數的數據類型,GET只接受ASCII字符,而POST沒有限制。GET比POST更不安全,因爲參數直接暴露在URL上,所以不能用來傳遞敏感信息。GET參數通過URL傳遞,POST放在Request body中。

爲什麼JavaScript裏面0.1+0.2 === 0.3是false

JavaScript 中的 number 類型就是浮點型,JavaScript 中的浮點數採用IEEE-754 格式的規定,這是一種二進制表示法,可以精確地表示分數,比如 1/2,1/8,1/1024,每個浮點數佔 64 位。但是,二進制浮點數表示法並不能精確的表示類似 0.1 這樣的簡單的數字,會有舍入誤差。由於採用二進制,JavaScript 也不能有限表示 1/10、1/2 等這樣的分數。在二進制中,1/10(0.1)被表示爲 0.00110011001100110011…… 注意 0011 是無限重複的,這是舍入誤差造成的,所以對於 0.1 + 0.2 這樣的運算,操作數會先被轉成二進制,然後再計算。

let,const,var有什麼區別

let 和 const 定義的變量不會出現變量提升,而 var 定義的變量會提升;let 和 const 是JS中的塊級作用域;let 和 const 不允許重複聲明(會拋出錯誤);let 和 const 定義的變量在定義語句之前,如果使用會拋出錯誤(形成了暫時性死區),而 var 不會;const 聲明一個只讀的常量,一旦聲明,常量的值就不能改變(如果聲明是一個對象,那麼不能改變的是對象的引用地址)。

Webpack如何實現打包

WebPack是一個模塊打包工具,你可以使用WebPack管理你的模塊依賴,並編繹輸出模塊們所需的靜態文件。它能夠很好地管理、打包Web開發中所用到的HTML、Javascript、CSS以及各種靜態文件(圖片、字體等),讓開發過程更加高效。對於不同類型的資源,webpack有對應的模塊加載器。webpack模塊打包器會分析模塊間的依賴關係,最後生成了優化且合併後的靜態資源。

Promise詳解

Promise有三種狀態:pengding(),resolved(已完成),rejected(已失敗);Promise從Pending狀態開始,如果成功就轉到成功態,並執行resolve回調函數;如果失敗就轉到失敗狀態並執行reject回調函數。

優點:一旦狀態改變,就不會再變,任何時候都可以得到這個結果;可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。

缺點:無法取消 Promise;當處於pending狀態時,無法得知目前進展到哪一個階段。

JavaScript基本規範

  1. 不要在同一行聲明多個變量;
  2. 請使用 ===/!==來比較true/false或者數值;
  3. 使用對象字面量替代new Array這種形 式;
  4. 不要使用全局函數;Switch語句必須帶有default分支;
  5. 函數不應該有時候有返回值,有時候沒有返回值;
  6. For循環必須使用大括號;
  7. If語句必須使用大括號;
  8. for-in循環中的變量應該使用var關鍵字明確限定作用域,從而 避免作用域污染;
  9. 命名規則中構造器函數首字母大寫,如function Person(){};
  10. 寫註釋。

Async/await的用法及優缺點

async 函數返回一個 Promise 對象,當函數執行的時候,一旦遇到 await 就會先返回,等到觸發的異步操作完成,再接着執行函數體內後面的語句。

async 和 await 相比直接使用 Promise 來說,優勢在於處理 then 的調用鏈,能夠更清晰準確的寫出代碼。缺點在於濫用 await 可能會導致性能問題,因爲 await 會阻塞代碼,也許之後的異步代碼並不依賴於前者,但仍然需要等待前者完成,導致代碼失去了併發性。

深淺拷貝的區別

淺拷貝只複製指向某個對象的指針,而不復制對象本身,新舊對象還是共享同一塊內存。但深拷貝會另外創造一個一模一樣的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象。

JQuery中$.get()提交和$.post()提交區別

相同點:都是異步請求的方式來獲取服務端的數據

不同點:請求方式不同:$.get() 方法使用GET方法來進行異步請求的。$.post() 方法使用POST方法來進行異步請求的;參數傳遞方式不同:get請求會將參數跟在URL後進行傳遞,而POST請求則是作爲HTTP消息的實體內容發送給Web服務器的,這種傳遞是對用戶不可見的;數據傳輸大小不同:get方式傳輸的數據大小不能超過2KB 而POST要大的多;安全問題: GET 方式請求的數據會被瀏覽器緩存起來,因此有安全問題。

XMLHttpRequest:XMLHttpRequest.readyState;狀態碼的意思

狀態0(請求未初始化):(XMLHttpRequest)對象已經創建或已被abort()方法重置,但還沒有調用open()方法;

狀態1(載入服務器連接已建立):已經調用open() 方法,但是send()方法未調用,尚未發送請求;

狀態2(載入完成,請求已接收):send()方法已調用,HTTP請求已發送到web服務器,請求已經發送完成,未接收到響應;

狀態3(交互,請求處理中):所有響應頭部都已經接收到。響應體開始接收但未完成,即可以接收到部分響應數據;

狀態4(請求完成,且相應已就緒):已經接收到了全部數據,並且連接已經關閉。

XSS攻擊

就是攻擊者想盡一切辦法將可以執行的代碼注入到網頁中。

持久型也就是攻擊的代碼被服務端寫入進數據庫中;非持久型相比於前者危害就小的多了,一般通過修改 URL 參數的方式加入攻擊代碼,誘導用戶訪問鏈接從而進行攻擊。

轉義輸入輸出的內容,對於引號、尖括號、斜槓進行轉義;建立白名單,開發者明確告訴瀏覽器哪些外部資源可以加載和執行。

CSRF攻擊

攻擊者構造出一個後端請求地址,誘導用戶點擊或者通過某些途徑自動發起請求。如果用戶是在登錄狀態下的話,後端就以爲是用戶在操作,從而進行相應的邏輯。

對 Cookie 設置 SameSite 屬性,表示 Cookie 不隨着跨域請求發送,可以很大程度減少 CSRF 的攻擊,但是該屬性目前並不是所有瀏覽器都兼容;驗證 Referer 來判斷該請求是否爲第三方網站發起的;服務器下發一個隨機 Token,每次發起請求時將 Token 攜帶上,服務器驗證 Token 是否有效。

HTTP請求構成

HTTP 請求由三部分構成,分別爲:請求行、首部、實體

請求行:基本由請求方法、URL、協議版本組成;首部:分爲請求首部和響應首部

設計模式有哪幾種

  1. 工廠模式、單列模式的核心就是保證全局只有一個對象可以訪問
  2. 適配器模式用來解決兩個接口不兼容的情況,不需要改變已有的接口,通過包裝一層的方式實現兩個接口的正常協作
  3. 裝飾模式作用是給對象添加功能
  4. 代理模式是爲了控制對對象的訪問,不讓外部直接訪問到對象
  5. 發佈-訂閱模式通過一對一或者一對多的依賴關係,當對象發生改變時,訂閱方都會收到通知
  6. 外觀模式提供了一個接口,隱藏了內部的邏輯,更加方便外部調用

標準盒子模型與IE盒子模型的不同

標準盒子模型:寬度=內容的寬度(content)+ border + padding + margin

低版本IE盒子模型:寬度=內容寬度(content+border+padding)+ margin

CSS3有哪些新特性

RGBA和透明度;  background-image background-origin(content-box/padding-box/border-box) background-size background-repeat;  word-wrap(對長的不可分割單詞換行)word-wrap:break-word;  文字陰影:text-shadow: 5px 5px 5px #FF0000;(水平陰影,垂直陰影,模糊距離,陰影顏色);  font-face屬性:定義自己的字體;  圓角(邊框半徑):border-radius 屬性用於創建圓角;  邊框圖片:border-image: url(border.png) 30 30 round;  盒陰影:box-shadow: 10px 10px 5px #888888;  媒體查詢:定義兩套css,當瀏覽器的尺寸變化時會採用不同的屬性

用純CSS創建一個三角形的原理是什麼

首先,需要把元素的寬度、高度設爲0。然後設置邊框樣式。

width: 0;

height: 0;

border-top: 40px solid transparent;

border-left: 40px solid transparent;

border-right: 40px solid transparent;

border-bottom: 40px solid #ff0000;

爲什麼會出現浮動和什麼時候需要清除浮動?清除浮動的方式

浮動元素碰到包含它的邊框或者浮動元素的邊框停留。由於浮動元素不在文檔流中,所以文檔流的塊框表現得就像浮動框不存在一樣。浮動元素會漂浮在文檔流的塊框上。

浮動帶來的問題:父元素的高度無法被撐開,影響與父元素同級的元素;與浮動元素同級的非浮動元素(內聯元素)會跟隨其後;若非第一個元素浮動,則該元素之前的元素也需要浮動,否則會影響頁面顯示的結構。

清除浮動的方式:

  • 父級div定義height:給父級DIV定義固定高度(height),能解決父級DIV 無法獲取高度得問題。優點:代碼簡潔缺點:高度被固定死了,是適合內容固定不變的模塊。
  • 最後一個浮動元素後加空div標籤 並添加樣式clear:both:添加一對空的DIV標籤,利用css的clear:both屬性清除浮動,讓父級DIV能夠獲取高度。優點:瀏覽器支持好缺點:多出了很多空的DIV標籤,如果頁面中浮動模塊多的話,就會出現很多的空置DIV了,這樣感覺視乎不是太令人滿意。
  • 包含浮動元素的父標籤添加樣式overflow爲hidden或auto:這個方法的關鍵在於觸發了BFC。在IE6中還需要觸發 hasLayout(zoom:1)優點:代碼簡介,不存在結構和語義化問題缺點:無法顯示需要溢出的元素
  • 父級div定義zoom:

.clearfix:after{

     content:'.';

     display:block;

     height:0;

     clear:both;

     visibility:hidden;

}

.clearfix {zoom:1;}

  • 父級div定義 display:table:將div屬性強制變成表格優點:不解缺點:會產生新的未知問題。

什麼是響應式設計?響應式設計的基本原理是什麼?如何兼容低版本的IE?

響應式網站設計(Responsive Web design)是一個網站能夠兼容多個終端,而不是爲每一個終端做一個特定的版本。

基本原理是通過媒體查詢檢測不同的設備屏幕尺寸做處理。

頁面頭部必須有meta聲明的viewport。

<meta name=’viewport’ content=”width=device-width, initial-scale=1. maximum-scale=1,user-scalable=no”>

漸進增強和優雅降級

優雅降級觀點認爲應該針對那些最高級、最完善的瀏覽器來設計網站。

漸進增強觀點則認爲應關注於內容本身,優先考慮低版本。

請闡述塊格式化上下文(Block Formatting Context)及其工作原理

塊格式上下文(BFC)是 Web 頁面的可視化 CSS 渲染的部分,是塊級盒佈局發生的區域,也是浮動元素與其他元素交互的區域。

一個 HTML 盒(Box)滿足以下任意一條,會創建塊格式化上下文:float的值不是none、  position的值不是static或relative、  display的值是table-cell、table-caption、inline-block、flex、或inline-flex、  overflow的值不是visible。

在 BFC 中,每個盒的左外邊緣都與其包含的塊的左邊緣相接。

Vue、React、Angular 介紹

MVC模式【Model(模型)+View(視圖)+controller(控制器)】

View通過Controller來和Model聯繫,Controller是View和Model的協調者,View和Model不直接聯繫,基本聯繫都是單向的。用戶User通過控制器Controller來操作模板Model從而達到視圖View的變化

MVP模式【是從MVC模式演變而來的,都是通過Controller/Presenter負責邏輯的處理+Model提供數據+View負責顯示】

在MVP中,Presenter完全把View和Model進行了分離,主要的程序邏輯在Presenter裏實現。

並且,Presenter和View是沒有直接關聯的,是通過定義好的接口進行交互,從而使得在變更View的時候可以保持Presenter不變。

MVVM模式【Model+View+ViewModel】

MVVM是把MVC裏的Controller和MVP裏的Presenter改成了ViewModel;View的變化會自動更新到ViewModel,ViewModel的變化也會自動同步到View上顯示。這種自動同步是因爲ViewModel中的屬性實現了Observer,當屬性變更時都能觸發對應的操作。

Vue框架【MVVM】

優點:輕量級框架,語法簡單,學習成本低;雙向數據綁定;組件化開發;數據和結構的分離;虛擬DOM;運行速度快;靈活漸進式框架

缺點:不支持IE8;生態環境差,不如angular和react;不適合偏大型的項目

應用場景:小型應用

React框架【MVC】

優點:jsx語法創建虛擬DOM,極速的渲染性能;組件化開發,組件獨立,方便重複使用;單向數據流;組件生命週期;跨瀏覽器兼容性好

缺點:不適合單獨做一個完整的框架

應用場景:個性化需求、中型應用

Angular框架

優點:模板功能強大豐富,並且是聲明式的,自帶了豐富的Angular指令;是一個比較完善的前端框架,包含服務,模板,數據雙向綁定,模塊化,路由,過濾器,依賴注入等所有功能;自定義指令,自定義指令後可以在項目中多次使用。ng模塊化比較大膽的引入了Java的一些東西(依賴注入);雙向綁定(髒檢查機制)

缺點:驗證功能錯誤信息顯示比較薄弱,需要寫很多模板標籤;ng提倡在控制器裏面不要有操作DOM的代碼,對於一些jQuery 插件的使用,如果想不破壞代碼的整潔性,需要寫一些directive去封裝插件;從1.0.X升級到1.2.X,貌似有比較大的調整,沒有完美兼容低版本,升級之後可能會導致一個兼容性的BUG;AngularJS 太笨重了,沒有讓用戶選擇一個輕量級的版本,當然1.2.X後,Angular也在做一些更改,比如把route,animate等模塊獨立出去,讓用戶自己去選擇。

應用場景:在大型超大型web應用開發上。

Vue與React的區別

相同點:組件化開發和Virtual DOM;支持props進行父子組件間數據通信;支持數據驅動視圖,不直接操作真實DOM,更新狀態數據界面就自動更新;支持服務端渲染;支持native的方案,react的React Native,Vue的Weex。

不同點:Vue支持雙向數據流,React單向數據流;組件寫法React推薦JSX,就是把HTML和css全都寫進JavaScript中,Vue推薦是webpack+vue+loader單文件組件格式,就是HTML、css、JavaScript都寫進一個文件;state對象在React應用中不可變,需要使用setState方法更新狀態,在Vue中,state對象不是必須得,數據有data屬性在vue對象中管理;Virtual DOM不一樣,vue會跟蹤每一個組件的依賴關係,不需要重新渲染整個組件樹,而對於React而言,每當應用的狀態被改變時,全部組件都會重新渲染,及React中會需要shouldComponentUpdata這個生命週期函數方法來進行控制;React嚴格上只針對MVC的view層,而Vue則是MVVM模式

Vue與Angular的區別

相同點:都支持指令:內置指令和自定義指令;都支持過濾器:內置過濾器和自定義過濾器;都支持雙向數據綁定;都不支持低端瀏覽器;vue和angular綁定都可以用{{}}

不同點:vue相當於angular要變得小巧很多,運行速度比angular快;vue指令用v-xxx,angular用ng-xxx;vue中數據放在data對象裏面,angular數據綁定在$scope上面;vue有組件化概念,angular中沒有;AngularJS的學習成本高,比如增加了Dependency Injection特性,而Vue.js本身提供的API都比較簡單、直觀;在性能上,AngularJS依賴對數據做髒檢查,所以Watcher越多越慢,Vue.js使用基於依賴追蹤的觀察並且使用異步隊列更新,所有的數據都是獨立觸發的

React與Angular的區別

相同點:都是單向數據流

不同點:React中沒有指令,Angular提供了豐富的指令

React中,並不是父子關係的組件,如何實現相互的數據通信

使用父組件,通過props將變量傳入子組件(如通過refs,父組件獲取一個子組件的方法,簡單包裝後,將包裝後的方法通過props傳入另一個子組件)

Vue生命週期理解

生命週期是從開始創建、初始化數據、編譯模版、掛載 Dom -> 渲染、更新 -> 渲染、卸載等一系列過程。

各個生命週期的作用

beforeCreate: 組件實例被創建之初,組件的屬性生效之前

created: 組件實例已經完全創建,屬性也綁定,但真實 dom 還沒有生成,$el 還不可用

beforeMount: 在掛載開始之前被調用:相關的 render 函數首次被調用

mounted: el被新創建的 vm.$el 替換,並掛載到實例上去之後調用該鉤子

beforeUpdate: 組件數據更新之前調用,發生在虛擬 DOM 打補丁之前

update: 組件數據更新之後

activated: keep-alive 專屬,組件被激活時調用

deadctivated: keep-alive 專屬,組件被銷燬時調用

beforeDestory: 組件銷燬前調用

destroyed: 組件銷燬後調用

v-if和v-show的區別

v-if 的話就得說到 Vue 底層的編譯了。當屬性初始爲 false 時,組件就不會被渲染,直到條件爲 true,並且切換條件時會觸發銷燬/掛載組件,所以總的來說在切換時開銷更高,更適合不經常切換的場景。並且基於 v-if 的這種惰性渲染機制,可以在必要的時候纔去渲染組件,減少整個頁面的初始渲染開銷。

v-show 只是在 display: none 和 display: block 之間切換。無論初始條件是什麼都會被渲染出來,後面只需要切換 CSS,DOM 還是一直保留着的。所以總的來說 v-show 在初始渲染時有更高的開銷,但是切換開銷很小,更適合於頻繁切換的場景。

Vue 的雙向綁定數據的原理

vue 實現數據雙向綁定主要是:採用數據劫持結合“發佈者 - 訂閱者”模式的方式,通過 Object.defineProperty() 來劫持各個屬性的 setter、 getter,在數據變動時發佈消息給訂閱者,觸發相應監聽回調。

簡述Vue的響應式原理

當一個Vue實例創建時,vue會遍歷data選項的屬性,用 Object.defineProperty 將它們轉爲getter/setter並且在內部追蹤相關依賴,在屬性被訪問和修改時通知變化。 每個組件實例都有相應的watcher程序實例,它會在組件渲染的過程中把屬性記錄爲依賴,之後當依賴項的setter被調用時,會通知watcher重新計算,從而致使它關聯的組件得以更新。

生命週期示意圖

說說Vue組件間通信方式

通信種類:父組件向子組件通信、子組件向父組件通信、隔代組件間通信、兄弟組件通信

實現通信方式:props、vue自定義事件、消息訂閱與發佈、vuex、slot

Props方式:通過一般屬性實現父向子通信;通過函數屬性實現子向父通信;缺點是隔代組件和兄弟組件間通信比較麻煩

Vue自定義事件:vue內置實現,可以代替函數類型的props(綁定監聽:<MyComp @eventName=”callback”>;觸發事件:this.$emit(“eventName”,data));缺點是隻適合子向父通信

消息訂閱與發佈:需要引入消息訂閱與發佈的實現庫,如pubsub-js(訂閱消息:PubSub.subscript(‘msg’,(msg,data)=>{}),發佈消息:PubSub.publish(‘msg’,data));優點是此方式可實現任意關係組件間通信

Vuex:vuex是vue官方提供的集中式管理vue多組件共享狀態數據的vue插件;優點是對組件間關係沒有限制,且相比於pubsub庫管理更集中,更方便

Slot:專門用來實現父向子傳遞數據的標籤;注意通信的標籤模板是在父組件中解析好後再傳遞給子組件的

vue父子通信

通過props來傳值:靜態傳值就是直接通過props來傳遞;動態傳值是通過v-bind來綁定一個要傳遞值的key,然後後面跟要傳遞的內容,不過這個內容是可以改變的。

通過ref來傳值:在父組件引用的子組件中採用ref=’要傳遞的值的key’

emit是子組件向父組件的傳值方式:子組件可以使用 $emit 觸發父組件的自定義事件

Slot:父組件向子組件傳遞模板採用slot,如果父組件沒傳遞模板,slot裏面有內容的話,就會顯示內容,如果有多個模板要進行傳遞,這需要在slot中通過命名(name)來區分。

computed 和 watch 區別和運用場景

computed 是計算屬性,依賴其他屬性計算值,並且 computed 的值有緩存,只有當計算值變化纔會返回內容;watch 監聽到值的變化就會執行回調,在回調中可以進行一些邏輯操作。

運用場景:

當我們需要進行數值計算,並且依賴於其它數據時,應該使用 computed,因爲可以利用 computed 的緩存特性,避免每次獲取值時,都要重新計算;

當我們需要在數據變化時執行異步或開銷較大的操作時,應該使用 watch,使用 watch 選項允許我們執行異步操作 ( 訪問一個 API ),限制我們執行該操作的頻率,並在我們得到最終結果前,設置中間狀態。這些都是計算屬性無法做到的。

keep-alive 組件有什麼作用

如果你需要在組件切換的時候,保存一些組件的狀態防止多次渲染,就可以使用 keep-alive 組件包裹需要保存的組件。

對於 keep-alive 組件來說,它擁有兩個獨有的生命週期鉤子函數,分別爲 activated 和 deactivated 。用 keep-alive 包裹的組件在切換時不會進行銷燬,而是緩存到內存中並執行 deactivated 鉤子函數,命中緩存渲染後會執行 actived 鉤子函數。

簡述Vuex及五個核心概念

Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化

State:一般在Vue組件中在計算屬性computed中返回一個某個狀態;

getter:是 store 的計算屬性, getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變纔會被重新計算;

mutation:包含多個直接更新state的方法(回調函數)的對象;

action:包含多個時間回調函數的對象;

module:Vuex 允許我們將 store 分割成模塊(module)。每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊。

說說您對SPA單頁面應用的理解,它的優缺點分別是是什麼?

SPA( single-page application )僅在 Web 頁面初始化時加載相應的 HTML、JavaScript 和 CSS。一旦頁面加載完成,SPA 不會因爲用戶的操作而進行頁面的重新加載或跳轉;取而代之的是利用路由機制實現 HTML 內容的變換,UI 與用戶的交互,避免頁面的重新加載。

優點:

用戶體驗好、快,內容的改變不需要重新加載整個頁面,避免了不必要的跳轉和重複渲染;基於上面一點,SPA 相對對服務器壓力小;前後端職責分離,架構清晰,前端進行交互邏輯,後端負責數據處理。

缺點:

初次加載耗時多:爲實現單頁 Web 應用功能及顯示效果,需要在加載頁面的時候將 JavaScript、CSS 統一加載,部分頁面按需加載;前進後退路由管理:由於單頁應用在一個頁面中顯示所有的內容,所以不能使用瀏覽器的前進後退功能,所有的頁面切換需要自己建立堆棧管理;SEO 難度較大:由於所有的內容都在一個頁面中動態替換顯示,所以在 SEO 上其有着天然的弱勢。

Vue 的父組件和子組件生命週期鉤子函數執行順序

Vue 的父組件和子組件生命週期鉤子函數執行順序可以歸類爲以下 4 部分:

加載渲染過程

父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted

子組件更新過程

父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated

父組件更新過程

父 beforeUpdate -> 父 updated

銷燬過程

父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

$route 和 $router 的區別

$router 爲 VueRouter 實例,想要導航到不同 URL,則使用 $router.push 方法。

$route 爲當前 router 跳轉對象裏面可以獲取 name 、 path 、 query 、 params 等。

Vue 中 key 的作用

key 的特殊屬性主要用在 Vue 的虛擬 DOM 算法,在新舊 nodes 對比時辨識 VNodes。如果不使用 key,Vue 會使用一種最大限度減少動態元素並且儘可能的嘗試修復/再利用相同類型元素的算法。使用 key,它會基於 key 的變化重新排列元素順序,並且會移除 key 不存在的元素。

有相同父元素的子元素必須有獨特的 key。重複的 key 會造成渲染錯誤。

Proxy 與 Object.defineProperty 優劣對比

Proxy 的優勢如下:

Proxy 可以直接監聽對象而非屬性;

Proxy 可以直接監聽數組的變化;

Proxy 有多達 13 種攔截方法,不限於 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具備的;

Proxy 返回的是一個新對象,我們可以只操作新的對象達到目的,而 Object.defineProperty 只能遍歷對象屬性直接修改;

Proxy 作爲新標準將受到瀏覽器廠商重點持續的性能優化,也就是傳說中的新標準的性能紅利;

Object.defineProperty 的優勢如下:

兼容性好,支持 IE9,而 Proxy 的存在瀏覽器兼容性問題,而且無法用 polyfill 磨平,因此 Vue 的作者才聲明需要等到下個大版本( 3.0 )才能用 Proxy 重寫。

虛擬 DOM 的優缺點

優點

保證性能下限: 框架的虛擬 DOM 需要適配任何上層 API 可能產生的操作,它的一些 DOM 操作的實現必須是普適的,所以它的性能並不是最優的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虛擬 DOM 至少可以保證在你不需要手動優化的情況下,依然可以提供還不錯的性能,即保證性能的下限;

無需手動操作 DOM: 我們不再需要手動去操作 DOM,只需要寫好 View-Model 的代碼邏輯,框架會根據虛擬 DOM 和 數據雙向綁定,幫我們以可預期的方式更新視圖,極大提高我們的開發效率;

跨平臺: 虛擬 DOM 本質上是 JavaScript 對象,而 DOM 與平臺強相關,相比之下虛擬 DOM 可以進行更方便地跨平臺操作,例如服務器渲染、weex 開發等等。

缺點:

無法進行極致優化: 雖然虛擬 DOM + 合理的優化,足以應對絕大部分應用的性能需求,但在一些性能要求極高的應用中虛擬 DOM 無法進行鍼對性的極致優化。

Webpack四個核心概念

entry(入口)entry point,入口起點(可以有多個),webpack會從該起點出發,找出哪些文件時入口文件所依賴的,從而構建內部依賴關係圖,並處理後輸出到稱之爲bundles的文件中。

output(輸出)指定經entry point處理後的bundles文件的輸出路徑(path)和名字(filename)。

loader(加載器)webpack 自身只能識別js文件,但是開發中會有css,圖片等靜態文件,webpack雖然自身不能識別,但是可以通過loader來處理;實現css、圖片等文件的打包。

plugins(插件):從打包優化和壓縮,一直到重新定義環境中的變量。

Redux管理狀態的機制

Redux基本理解:Redux是一個獨立專門用於做狀態管理的JS庫,不是React插件庫;它可以用在React,Angular,Vue等項目中,但基本與React配合使用;作用是集中式管理React應用中多個組件共享的狀態和從後臺獲取的數據

如何實現數組的隨機排序

第一種、利用數組自帶的sort方法

let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

function foo(arr) {

    let cloneArr = arr.concat();

    cloneArr.sort((n1, n2) => Math.random() - 0.5)

    return cloneArr;

}

console.log(foo(arr));

第二種、利用遞歸函數對比

let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

function foo(arr) {

 let result = [];

 let cloneArr = arr.concat();

 (function(){

     if (!cloneArr.length) { return }

     let index = Math.floor(Math.random() * cloneArr.length);

     result = result.concat(cloneArr.splice(index, 1));

     arguments.callee();

  })()

  return result;

}

console.log(foo(arr));

第三種、洗牌算法

let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

function foo(arr) {

    let cloneArr = arr.concat();

    let len = cloneArr.length;

    for (let i = 0; i < len; i++) {

        let index = Math.floor(Math.random() * cloneArr.length);

        let temp = cloneArr[index];

        cloneArr[index] = cloneArr[i];

        cloneArr[i] = temp;

    }

    return cloneArr;

}

console.log(foo(arr));

垂直上下居中的方法

第一種:知道元素的寬和高

div.box{

    weight:200px;

    height:400px;

    position:absolute;

    left:50%;

    top:50%;

    margin-left:-100px;

    margin-top:-200px;

 }

第二種:不知道元素的寬和高

div.box{

    weight:200px;

    height:400px;

    position:absolute;

    left:50%;

    top:50%;

transform:translate(-50%,-50%);

}

第三種:flex佈局

父級元素:{

   display:flex;

   flex-direction:row;

   justify-content:center;

   align-items:center;

}

子級元素:{

flex:1

}

簡述JavaScript實現繼承的幾種方式

組合繼承核心是在子類的構造函數中通過 Parent.call(this) 繼承父類的屬性,然後改變子類的原型爲 new Parent() 來繼承父類的函數

function Parent(value) {

    this.val = value;

}

Parent.prototype.getValue = function () {

    console.log(this.val);

}


function Child(value) {

    Parent.call(this, value);

}

Child.prototype = new Parent();

const child = new Child(1);

child.getValue(); // 1

child instanceof Parent; // true

寄生組合繼承核心就是將父類的原型賦值給了子類,並且將構造函數設置爲子類,這樣既解決了無用的父類屬性問題,還能正確的找到子類的構造函數

function Parent(value) {

    this.val = value;

}

Parent.prototype.getValue = function () {

    console.log(this.val);

}


function Child(value) {

    Parent.call(this, value);

}

Child.prototype = Object.create(Parent.prototype, {

    constructor: {

        value: Child,

        enumerable: false,

        writable: true,

        configurable: true

    }

})

const child = new Child(1);

child.getValue(); // 1

child instanceof Parent; // true

Class 繼承核心在於使用 extends 表明繼承自哪個父類,並且在子類構造函數中必須調用 super,因爲這段代碼可以看成 Parent.call(this, value)

class Parent {

    constructor(value) {

        this.val = value;

    }

    getValue() {

        console.log(this.val);

    }

}

class Child extends Parent {

    constructor(value) {

        super(value);

    }

}

let child = new Child(1);

child.getValue(); // 1

child instanceof Parent; // true

數組去重

第一種:Array.filter() + indexOf

function distinct(a, b) {

    let arr = a.concat(b);

    return arr.filter((item, index)=> {

        return arr.indexOf(item) === index

    })

}

第二種:雙重 for 循環

function distinct(a, b) {

    let arr = a.concat(b);

    for (let i=0, len=arr.length; i<len; i++) {

        for (let j=i+1; j<len; j++) {

            if (arr[i] == arr[j]) {

                arr.splice(j, 1);

                // splice 會改變數組長度,所以要將數組長度 len 和下標 j 減一

                len--;

                j--;

            }

        }

    }

    return arr

}

第三種:for...of + includes()

function distinct(a, b) {

    let arr = a.concat(b)

    let result = []

    for (let i of arr) {

        !result.includes(i) && result.push(i)

    }

    return result

}

第四種:Array.sort()

function distinct(a, b) {

    let arr = a.concat(b)

    arr = arr.sort()

    let result = [arr[0]]

    for (let i=1, len=arr.length; i<len; i++) {

        arr[i] !== arr[i-1] && result.push(arr[i])

    }

    return result

}

第五種:new Set()

function distinct(a, b) {

    return Array.from(new Set([...a, ...b]))

}

第六種:for...of + Object

function distinct(a, b) {

    let arr = a.concat(b)

    let result = []

    let obj = {}

    for (let i of arr) {

        if (!obj[i]) {

            result.push(i)

            obj[i] = 1

        }

    }

    return result

}

找出字符串中出現次數最多的字符,並統計次數

let str = "asddfssssaasswef";

let obj = {};

for (let i = 0; i < str.length; i++) {

    if (!obj[str.charAt(i)]) {

        obj[str.charAt(i)] = 1;

    } else {

        obj[str.charAt(i)]++;

    }

}

console.log(obj);

let max = 0;

let charmax;

for (let key in obj) {

    if (obj[key] > max) {

        max = obj[key];

        charmax = key;

    }

}

console.log(`出現最多的字符是${charmax},出現了${max}次`);

請用JavaScript寫一個方法:要求實現格式化輸出,比如輸入9999999,輸出爲9,999,999

function splitNum(n) {

    let b = parseInt(n).toString();

    let len = b.length;

    if (len < 3) { return b }

    let r = len % 3,

    b1 = b.slice(0, r) + "," + b.slice(r, len).match(/\d{3}/g).join(","),

    b2 = b.slice(r, len).match(/\d{3}/g).join(",")

    return r > 0 ? b1 : b2;

}

console.log(splitNum(9999999));

JavaScript中的閉包,及幾種寫法和用途

閉包是密閉的容器,類似於set、map容器,存儲數據的;且是一個對象,存放數據的格式是key:value

形成的條件是函數嵌套,內部函數引用外部函數的局部變量

第一種:給函數添加一些屬性

function Circle(r) {  

      this.r = r;  

}  

Circle.PI = 3.14159;  

Circle.prototype.area = function() {  

  return Circle.PI * this.r * this.r;  

}  

let c = new Circle(1.0);

alert(c.area());

第二種:聲明一個變量,將一個函數當作值賦給變量

let Circle = function() {  

   let obj = new Object();  

   obj.PI = 3.14159;  

   obj.area = function( r ) {  

       return this.PI * r * r;  

   }  

   return obj;  

}  

let c = new Circle();  

alert( c.area( 1.0 ) );

第三種:new 一個對象,然後給對象添加屬性和方法

let Circle = new Object();  

Circle.PI = 3.14159;  

Circle.Area = function( r ) {  

       return this.PI * r * r;  

}

alert( Circle.Area( 1.0 ) );

第四種:let obj = {}就是聲明一個空的對象

let Circle={  

   "PI":3.14159,  

 "area":function(r){  

          return this.PI * r * r;  

        }  

};  

alert( Circle.area(1.0) );

用途可以讀取函數內部的變量;讓變量的值始終保持在內存中

在JavaScript中實現不可變對象

const obj = {

    num: 10,

    obj: {

        content: "mutable object"

    }

}

function deepFreeze(obj) {

    let propNames = Object.getOwnPropertyNames(obj);

    propNames.forEach(function (name) {

        let prop = obj[name];

        if (typeof prop == 'object' && prop !== null) {

            deepFreeze(prop);

        }

    });

    return Object.freeze(obj);

}

deepFreeze(obj);

obj.num = 5;

obj.obj = { content: "changed!" }

console.log(obj);

如何阻止冒泡

function stopBubble(e) {

if ( e && e.stopPropagation ){

e.stopPropagation();

}else {

window.event.cancelBubble = true;

}

}

數組的排序方式

第一種:arrayObject.sort(sortby)

function sortNum(a, b) {

return a - b;

}

let arr = [524, 684, 5, 69, 15];

let res = arr.sort(sortNum);

console.log(res);

第二種:冒泡排序

function bubbleSort(arr) {

    for (let i = 0; i < arr.length; i++) {

        for (let j = 0; j < arr.length; j++) {

            if (arr[i] < arr[j]) {

                let temp = arr[j];

                arr[j] = arr[i];

                arr[i] = temp;

            }

        }

    }

    return arr;

}

let arr = [524, 684, 5, 69, 15];

console.log(bubbleSort(arr));

第三種:快速排序

function quickSort(arr) {

    if (arr.length <= 1) {

        return arr;

    }

    let pivotIndex = Math.floor(arr.length / 2),

    pivot = arr.splice(pivotIndex, 1)[0],

    lef = [],

    rig = [];

    for (var i = 0; i < arr.length; i++) {

        if (arr[i] < pivot) {

            lef.push(arr[i]);

        }else {

            rig.push(arr[i]);

        }

    }

    return quickSort(lef).concat(pivot, quickSort(rig));

}

let arr = [524, 684, 5, 69, 15];

console.log(quickSort(arr));

第四種:插入排序

function insertSort(arr, a) {

    for (let i = 1; i < arr.length; i++) {

        if (arr[i] >= a) {

            for (let j = arr.length; j > i; j--) {

                arr[j] = arr[j - 1];

            }

            arr[i] = a;

            break;

        }

    }

    return arr;

}

let arr = [5, 15, 69, 524, 684];

console.log(insertSort(arr, 92));

寫一個function ,清除字符串前後的空格

第一種:重寫trim方法

let str=’   abc    ’;

if(!String.prototype.trim){

    String.prototype.trim = function(){

        return this.replace(/^\s+/,"").replace(/\s+$/,"");

}

};

console.log(str.trim());

第二種:寫fntrim去掉左右空格

let str=’   abc   ’;

function fntrim(str){

return str.replace(/^\s+/,"").replace(/\s+$/,"");

};

Console.log(fntrim(str));

多行文本超出隱藏

overflow:hidden; //超出文本隱藏

text-overflow:ellipsis; //溢出省略號顯示

display:-webkit-box; //將對象作爲彈性伸縮盒子

-webkit-box-orient:vertical; //設置伸縮盒子的子元素排列方式-從上到下垂直排列

-webkit-line-clamp:2; //這個屬性不是css的規範屬性,需要組合上面兩個屬性,數組代表顯示的行數。

數組快速反轉

function reverse(arr){

let num=parseInt(arr.length/2);

    for(var i=0;i<num;i++){

        let temp = arr[i];

    arr[i] = arr[arr.length-i-1];

    arr[arr.length-i-1] = temp;

    }

      return arr;

}

var A=[1,10,5,13,26,50,2];

console.log(reverse(A));

用JavaScript實現隨機選取10-100之間的10個數,存入一個數組並排序

let iArray=[];

function getRandom(istart,iend){

let iChoice=iend-istart+1,

res=Math.floor(Math.random()*iChoice+istart);

return res;

}

for(let i=0;i<10;i++){

iArray.push(getRandom(10,100));

}

iArray.sort((a,b)=>a>b);

console.log(iArray);

手動實現promise函數

function Promise(fn) {

    let data = undefined, reason = undefined;

    let succallbacks = [];

    let failcallbacks = [];

    let status = "pending";

    this.then = function (fulfilled, rejected) {

        return new Promise(function(resolve,reject) {    //返回一個新的promise

            function suc(value) {   //成功

                let ret = typeof fulfilled === 'function' && fulfilled(value) || value;

                if( ret && typeof ret ['then'] == 'function'){    //判斷 then中的 返回的是否是promise對象,如果是註冊then方法

                    ret.then(function(value){

                        resolve(value);

                    });

                } else {

                    resolve(ret);

                }

            }

            function errback(reason) {  //失敗

                reason = typeof rejected === 'function'  && rejected(reason) || reason;

                reject(reason);

            }

            if (status === 'pending') {

                succallbacks.push(suc);

                failcallbacks.push(errback);

            } else if(status === 'fulfilled'){

                suc(data);

            } else {

                errback(reason);

            }

        })

    }

    function resolve(value) {

        setTimeout(function () {   //加入延時

            status = "fulfilled";

            data = value;

            succallbacks.forEach((callback) => {

                callback(value);

            })

        }, 0)

    }

    function reject(value) {

        setTimeout(function () {

            status = "rejected";

            reason = value;

            failcallbacks.forEach((callback) => {

                callback(value);

            })

        }, 0)

    }

    fn(resolve, reject);

}

let p = new Promise((resolve, reject) => {

                setTimeout(() => {

                resolve(1);

            }, 1000)

        }) ;

p.then(data =>{

    return  new Promise((resolve,reject) => {    //then 方法返回的是一個promise對象,故執行 promise中的then註冊該結果,在resolve

               setTimeout(() => { resolve(2);},1000)})

}).then(data =>{

    console.log(data);

})

Promise 實現了鏈式調用,也就是說每次調用 then 之後返回的都是一個 Promise,並且是一個全新的 Promise,原因也是因爲狀態不可變。如果你在 then 中 使用了 return,那麼 return 的值會被 Promise.resolve() 包裝

淺拷貝Object.assign()

let obj = { a: {a: "kobe", b: 39} };

var initalObj = Object.assign({}, obj);

initalObj.a.a = "wade";

console.log(obj.a.a);

淺拷貝Array.prototype.concat()

let arr = [1, 3, {

    username: 'kobe'

    }];

let arr2=arr.concat();    

arr2[2].username = 'wade';

console.log(arr);

淺拷貝Array.prototype.slice()

let arr = [1, 3, {

    username: ' kobe'

    }];

let arr3 = arr.slice();

arr3[2].username = 'wade'

console.log(arr);

深拷貝JSON.parse(JSON.stringify())

let arr = [1, 3, {

    username: ' kobe'

}];

let arr4 = JSON.parse(JSON.stringify(arr));

arr4[2].username = 'duncan';

console.log(arr, arr4);

深拷貝遞歸

function deepClone(initalObj, finalObj) {    

  let obj = finalObj || {};    

  for (var i in initalObj) {        

    let prop = initalObj[i];

    if(prop === obj) {            

      continue;

    }        

    if (typeof prop === 'object') {

      obj[i] = (prop.constructor === Array) ? [] : {};            

      arguments.callee(prop, obj[i]);

    } else {

      obj[i] = prop;

    }

  }    

  return obj;

}

深拷貝函數庫lodash

let _ = require('lodash');

let obj1 = {

    a: 1,

    b: { f: { g: 1 } },

    c: [1, 2, 3]

};

let obj2 = _.cloneDeep(obj1);

console.log(obj1.b.f === obj2.b.f);

深拷貝jquery的$.extend

let $ = require('jquery');

let obj1 = {

    a: 1,

    b: { f: { g: 1 } },

    c: [1, 2, 3]

};

let obj2 = $.extend(true, {}, obj1);

obj1.b.f === obj2.b.f;

將一個單向鏈表反轉

let reverseList = function(head) {

    if (!head || !head.next) return head

    let pre = null;

    let current = head;

    let next;

    while(current) {

        next = current.next;

        current.next = pre;

        pre = current;

        current = next;

    }

    return pre;

};

函數防抖(debounce)函數節流(throttle)的實現方法

函數防抖:一個需要頻繁觸發的函數,在規定時間內,只讓最後一次生效,前面的不生效

函數節流:一個函數執行一次後,只有大於設定的執行週期後纔會執行第二次

function debounceOrThrottle ({ fn, wait = 300, immediate = false, executeOncePerWait = false }) {

  if (typeof fn !== 'function') {

    throw new Error('fn is expected to be a function');

  }

  let tId = null;

  let context = null;

  let args = null;

  let lastTriggerTime = null;

  let result = null;

  let lastExecutedTime = null;

  const later = function() {

    const last = Date.now() - (executeOncePerWait ? lastExecutedTime : lastTriggerTime);

    if(last < wait && last > 0) {

      setTimeout(later, wait - last)

    } else {

      if (!immediate) {

        executeOncePerWait && (lastExecutedTime = Date.now());

        result = fn.apply(context, args);

        context = args = null;

      }

      tId = null;

    }

  }

  return function() {

    context = this;

    args = arguments;

    !executeOncePerWait && (lastTriggerTime = Date.now());

    const callNow = immediate && !tId;

    if(!tId) {

      executeOncePerWait && (lastExecutedTime = Date.now());

      tId = setTimeout(later, wait);

    }

    if (callNow) {

      executeOncePerWait && (lastExecutedTime = Date.now());

      result = fn.apply(context, args);

      context = args = null;

    }

    return result;

  }

}

const debounce = ({ fn, wait, immediate }) =>

  debounceOrThrottle({

    fn,

    wait,

    immediate

  })

const throttle = ({ fn, wait, immediate = true }) =>

  debounceOrThrottle({

    fn,

    wait,

    immediate,

    executeOncePerWait: true

  })

Call、apply、bind的實現

Call

function.prototype.call = function(thisArg){

   // this指向調用call的對象

    if (typeof this !== 'function') { // 調用call的若不是函數則報錯

        throw new TypeError('Error')

    }

  //若thisArg不是函數,或者不存在,thisArg指向window

  thisArg = thisArg || window

  thisArg.fn = this   // 將調用call函數的函數對象添加到thisArg的屬性中

  //去除參數的第一個值後執行這個添加的函數

  const res = thisArg.fn(...arguments.slice(1))

  delete thisArg.fn   // 刪除該屬性

  return res;

}

Apply

function.prototype.apply = function(thisArg,args){

   // this指向調用call的對象

    if (typeof this !== 'function') { // 調用call的若不是函數則報錯

        throw new TypeError('Error')

    }

  //若thisArg不是函數,或者不存在,thisArg指向window

  thisArg = thisArg || window

  thisArg.fn = this   // 將調用call函數的函數對象添加到thisArg的屬性中

  //去除參數的第一個值後執行這個添加的函數

  const res = thisArg.fn(...args)

  delete thisArg.fn   // 刪除該屬性

  return res;

}

Bind

function.prototype.bind = function(oThis){

      if(typeof this !== 'function'){

          throw new TypeError('被綁定的對象需要是函數')

      }

      let self = this

      let args = arguments.slice(1);

      fBound = function(){ //this instanceof fBound === true時,說明返回的fBound被當做new的構造函數調用

          return self.apply(this instanceof fBound ? this : oThis, args.concat([].slice.call(arguments)))

      }

      let func = function(){}

      //維護原型關係

      if(this.prototype){

          func.prototype = this.prototype;

      }

      //使fBound.prototype是func的實例,返回的fBound若作爲new的構造函數,新對象的__proto__就是func的實例

      fBound.prototype = new func();

      return fBound;

}

實現一個lazyman

class _LazyMan {

  constructor(name) {

    this.tasks = [];

    const task = () => {

      console.log(`Hi! This is ${name}`);

      this.next();

    }

    this.tasks.push(task);

    setTimeout(() => {               // 把 this.next() 放到調用棧清空之後執行

      this.next();

    }, 0);

  }

  next() {

    const task = this.tasks.shift(); // 取第一個任務執行

    task && task();

  }

  sleep(time) {

    this._sleepWrapper(time, false);

    return this;                     // 鏈式調用

  }

  sleepFirst(time) {

    this._sleepWrapper(time, true);

    return this;

  }

  _sleepWrapper(time, first) {

    const task = () => {

      setTimeout(() => {

        console.log(`Wake up after ${time}`);

        this.next();

      }, time * 1000)

    }

    if (first) {

      this.tasks.unshift(task);     // 放到任務隊列頂部

    } else {

      this.tasks.push(task);        // 放到任務隊列尾部

    }

  }

  eat(name) {

    const task = () => {

      console.log(`Eat ${name}`);

      this.next();

    }

    this.tasks.push(task);

    return this;

  }

}

function LazyMan(name) {

  return new _LazyMan(name);

}

Class 與 Style 如何動態綁定?

Class 可以通過對象語法和數組語法進行動態綁定:

對象語法:

<div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>

data: {

  isActive: true,

  hasError: false

}

數組語法:

<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>

data: {

  activeClass: 'active',

  errorClass: 'text-danger'

}

Style 也可以通過對象語法和數組語法進行動態綁定:

對象語法:

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

data: {

  activeColor: 'red',

  fontSize: 30

}

數組語法:

<div v-bind:style="[styleColor, styleSize]"></div>

data: {

  styleColor: {

     color: 'red'

   },

  styleSize:{

     fontSize:'23px'

  }

}

vue-router 路由模式有幾種

vue-router 有 3 種路由模式:hash、history、abstract,對應的源碼如下所示:

switch (mode) {

  case 'history':

this.history = new HTML5History(this, options.base)

break

  case 'hash':

this.history = new HashHistory(this, options.base, this.fallback)

break

  case 'abstract':

this.history = new AbstractHistory(this, options.base)

break

  default:

if (process.env.NODE_ENV !== 'production') {

  assert(false, `invalid mode: ${mode}`)

}

}

其中,3 種路由模式的說明如下:

hash:  使用 URL hash 值來作路由。支持所有瀏覽器,包括不支持 HTML5 History Api 的瀏覽器;

history :  依賴 HTML5 History API 和服務器配置。具體可以查看 HTML5 History 模式;

abstract :  支持所有 JavaScript 運行環境,如 Node.js 服務器端。如果發現沒有瀏覽器的 API,路由會自動強制進入這個模式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章