主頁 > 前端設計 > 前端高頻面試題總結(2020年最新版),建議收藏慢慢吸收!持續更新中...

前端高頻面試題總結(2020年最新版),建議收藏慢慢吸收!持續更新中...

2020-12-24 13:21:55 前端設計

面試過不少前端從業者,簡歷寫的平平淡淡,別人會的技能他也都會,看起來什么都掌握一些;有些會請過來當面聊一下,有些就直接拒絕了(如果是公司內要求獨立完成專案的崗位,簡歷里放很多學習時候的DEMO專案,沒有真實上線的專案,這種簡歷一般都會拒絕掉),

當我們去一家公司面試,面試官有很大的主動權,面試官屬于攻擊方,求職者屬于防守方,我們不可能什么方向都研究的非常深,所以揚長避短是最好的方式,推薦大家要做帳篷型的人,而不是水桶型的人;市場上崗位非常多,我們需要做的就是找到適合自己長處發展的那個職業!

面試的時候一定不要和面試官硬剛,可以適度的夸大自己,但是一定不要以為自己技術牛,就瘋狂裝B;面試官非常希望找到和自己脾氣相投的人一起共事,哪怕你真的是萬里挑一的大牛,面試官感覺和你一起共事非常不舒服,那么拒絕你也不是什么太難的決定,

如果遇到自己期望薪資內的offer,一定不要猶豫,直接答應下來,但是入職的時間,可以稍微向后推一下,可以一個禮拜后,十天后等,這段時間再繼續面試,這時候薪資就一定要找高于自己答應offer的薪資,如果期間遇到自己更加滿意的offer,就在自己答應的offer里找一家自己最感興趣的入職,

目錄

HTML 面試題

CSS 面試題

JavaScript 面試題

網路通信面試題

jQuery 面試題

Vue.js 面試題

React.js 面試題

工具面試題

演算法面試題

綜合面試題

我比較喜歡的面試者



HTML 面試題

HTML5 有哪些新特性?

答:1.HTML4 規定了三種宣告方式,分別是:嚴格模式、過渡模式 和 框架集模式;

而HTML5因為不是SGML的子集,只需要<!DOCTYPE>就可以了:

2.語意化更好的內容標簽,header/footer/article等

3.音頻、視頻 API(audio,video)

4.表單控制元件:

HTML5 擁有多個新的表單輸入型別,這些新特性提供了更好的輸入控制和驗證,

  • color
  • date
  • datetime
  • datetime-local
  • email
  • month
  • number
  • range
  • search
  • tel
  • time
  • url
  • week

5.5個API-本地存盤,長期存盤資料的 localStorage比較常用,臨時存盤的 sessionStorage,瀏覽器關閉后自動洗掉,實際作業中使用的場景不多,

畫布/Canvas,canvas,figure,figcaption.

地理/Geolocation.地理位置 API 允許用戶向 Web 應用程式提供他們的位置,出于隱私考慮,報告地理位置前會先請求用戶許可,

拖拽釋放.HTML拖拽釋放 (Drag and drop) 介面使應用程式能夠在瀏覽器中使用拖放功能,例如,通過這些功能,用戶可以使用滑鼠選擇可拖動元素,將元素拖動到可放置元素,并通過釋放滑鼠按鈕來放置這些元素,可拖動元素的一個半透明表示在拖動操作期間跟隨滑鼠指標,

Web Workers.webworker, websocket, Geolocation,當在 HTML 頁面中執行腳本時,頁面的狀態是不可回應的,直到腳本已完成,web worker 是運行在后臺的 JavaScript,獨立于其他腳本,不會影響頁面的性能,您可以繼續做任何愿意做的事情:點擊、選取內容等等,而此時 web worker 在后臺運行,

Doctype作?? 嚴格模式與混雜模式如何區分?它們有何意義?

答:

  • ??被加載的時, link 會同時被加載,? @imort ??被加載的時, link 會同時被加
  • 載,? @import 引?的 CSS 會等到??被加載完再加載 import 只在 IE5 以上才能識
  • 別,? link 是 XHTML 標簽,?兼容問題 link ?式的樣式的權重 ?于 @import 的權
  • <!DOCTYPE> 宣告位于?檔中的最前?,處于 <html> 標簽之前,告知瀏覽器的決議器, ?什么?檔型別 規范來決議這個?檔
  • 嚴格模式的排版和 JS 運作模式是 以該瀏覽器?持的最?標準運?
  • 在混雜模式中,??以寬松的向后兼容的?式顯示,模擬?式瀏覽器的?為以防?站點?法?作, DOCTYPE 不存在或格式不正確會導致?檔以混雜模式呈現

如何實作瀏覽器內多個標簽頁之間的通信?

呼叫 localstorge、cookies 等本地存盤方式;

  • 1、在 B 頁面中可以使用 window.opener 獲得 A 頁面的 window 句柄,使用該句柄即可呼叫 A 頁面中的物件,函式等,
    • 例如 A 頁面定義 js 函式 onClosePageB,在 B 頁面可以用 window.opener.onClosePageB 來進行回呼,
  • 2、使用 window.showModalDialog(sURL [, vArguments] [,sFeatures])打開新視窗, 其中 vArguments 引數可以用來向對話框傳遞引數,傳遞的引數型別不限,包括陣列、函式等,對話框通過window.dialogArguments來取得傳遞進來的引數,
  • 3、如果是支持 HTML5 的話,建議用本地存盤 (local storage),它支持一個事件方法 window.onstorage,只要其中一個視窗修改了本地存盤,其他同源視窗會觸發這個事件,

總結:

  • WebSocket、SharedWorker;
  • 也可以呼叫 localstorge、cookies 等本地存盤方式;
  • localstorge 另一個瀏覽背景關系里被添加、修改或洗掉時,它都會觸發一個事件,
  • 我們通過監聽事件,控制它的值來進行頁面資訊通信;
  • 注意 quirks:Safari 在無痕模式下設定 localstorge 值時會拋出 QuotaExceededError 的例外;

方法一:呼叫 localstorge

標簽1:

<input id="name">
<input type="button" id="btn" value="提交">
<script type="text/javascript">
    $(function(){
        $("#btn").click(function(){
           var name=$("#name").val();
            localStorage.setItem("name", name); //存盤需要的資訊
        });
   });
</script>

標簽2:

$(function(){
    window.addEventListener("storage", function(event){
	   console.log(event.key + "=" + event.newValue);
    });     //使用storage事件監聽添加、修改、洗掉的動作
});

將要傳遞的資訊存盤在 cookie 中,每隔一定時間讀取 cookie 資訊,即可隨時獲取要傳遞的資訊,

標簽1:

$(function(){
    $("#btn").click(function(){
        var name=$("#name").val();
        document.cookie="name="+name;
    });
});

標簽2:

$(function(){
	function getCookie(key) {
	   return JSON.parse("{\"" + document.cookie.replace(/;\s+/gim,"\",\"").replace(/=/gim, "\":\"") + "\"}")[key];
	}
	setInterval(function(){
	   console.log("name=" + getCookie("name"));
	}, 10000);
});

?內元素有哪些?塊級元素有哪些? 空(void)元素有那些??內元 素和塊級元素有什么區別?

答:

  • ?內元素有: a b span img input select strong
  • 塊級元素有: div ul ol li dl dt dd h1 h2 h3 h4… p
  • 空元素: br,hr img input link meta
  • ?內元素不可以設定寬?,不獨占??
  • 塊級元素可以設定寬?,獨占??

簡述?下src與href的區別?

答:

  • src ?于替換當前元素,href?于在當前?檔和引?資源之間確?聯系,
  • src 是 source 的縮寫,指向外部資源的位置,指向的內容將會嵌?到?檔中當前標簽所在位置;在請求 src 資源時會將其指向的資源下載并應?到?檔內,例如 js 腳本,img 圖?和 frame 等元素
    • <script src ="js.js"></script> 當瀏覽器決議到該元素時,會暫停其他
    • 資源的下載和處理,直到將該資源加載、編譯、執?完畢,圖?和框架等元素
    • 也如此,類似于將所指向資源嵌?當前標簽內,這也是為什么將js腳本放在底
    • 部?不是頭部
  • href 是 Hypertext Reference 的縮寫,指向?絡資源所在位置,建?和當前元素(錨點)或當前?檔(鏈接)之間的鏈接,如果我們在?檔中添加
  • <link href="common.css" rel="stylesheet"/> 那么瀏覽器會識別該?檔為 css ?件,就會并?下載資源并且不會停?對當前?檔的處理,這也是為什么建議使? link ?式來加載 css ,?不是使? @import ?式

cookies,sessionStorage,localStorage 的區別?

答:

  • cookie 是網站為了標示用戶身份而儲存在用戶本地終端(Client Side)上的資料(通常經過加密),
  • cookie 資料始終在同源的 http 請求中攜帶(即使不需要),記會在瀏覽器和服務器間來回傳遞,
  • sessionStorage 和 localStorage 不會自動把資料發給服務器,僅在本地保存,

存盤大小

  • cookie 資料大小不能超過 4k,
  • sessionStorage 和 localStorage 雖然也有存盤大小的限制,但比 cookie 大得多,可以達到 5M 或更大,

有期時間

  • localStorage 存盤持久資料,瀏覽器關閉后資料不丟失除非主動洗掉資料;
  • sessionStorage 資料在當前瀏覽器視窗關閉后自動洗掉,
  • cookie 設定的 cookie 過期時間之前一直有效,即使視窗或瀏覽器關閉

HTML5 的離線儲存的使用和原理?

答:

相似存盤

localStorage 長期存盤資料,瀏覽器關閉后資料不丟失; sessionStorage 資料在瀏覽器關閉后自動洗掉,

離線的存盤

兩種方式

  • HTML5 的離線存盤.appcache檔案【廢棄】
  • service-worker 的標準

HTML5 的離線存盤.appcache檔案【廢棄】

在用戶沒有與因特網連接時,可以正常訪問站點或應用,在用戶與因特網連接時,更新用戶機器上的快取檔案,

原理:HTML5 的離線存盤是基于一個新建的,appcache 檔案的快取機制(不是存盤技術),通過這個檔案上的決議清單離線存盤資源,這些資源就會像 cookie 一樣被存盤了下來,

之后當網路在處于離線狀態下時,瀏覽器會通過被離線存盤的資料進行頁面展示,

如何使用

  • 1、頁面頭部像下面一樣加入一個 manifest 的屬性
  • 2、在 cache.manifest 檔案的撰寫離線存盤的資源

    CACHE MANIFEST
    #v0.11
    CACHE:
    js/app.js
    css/style.css
    NETWORK:
    resourse/logo.png
    FALLBACK:
    / /offline.html

  • 3、在離線狀態時,操作 window.applicationCache 進行需求實作,

service-worker

可以參考

  • http://www.alloyteam.com/2019/07/web-applications-offline/
  • https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API/Using_Service_Workers

怎樣處理 移動端 1px 被 渲染成 2px 問題?

答:

meta 標簽中的 viewport 屬性 ,initial-scale 設定為 1

rem 按照設計稿標準走,外加利用 transfrome 的 scale(0.5) 縮小一倍即可; 2 全域處理

meta 標簽中的 viewport 屬性 ,initial-scale 設定為 0.5

rem 按照設計稿標準走即可

解釋

UI 設計師設計的時候,畫的 1px(真實像素)實際上是 0.5px(css) 的線或者邊框,但是他不這么認為,他認為他畫的就是 1px 的線,因為他畫的稿的尺寸本身就是螢屏尺寸的 2 倍,假設手機視網膜屏的寬度是 320x480 寬,但實際尺寸是 640x960 寬,設計師設計圖的時候一定是按照 640x960 設計的,但是前端工程師寫代碼的時候,所有 css 都是按照 320x480 寫的,寫 1px(css),瀏覽器自動變成 2px(真實像素),

那么前端工程師為什么不能直接寫 0.5px(css) 呢?因為在老版本的系統里寫 0.5px(css) 的話,會被瀏覽器解讀為 0px(css),就沒有邊框了,所以只能寫成 1px(css),實際在螢屏上顯示出來就是設計師畫的 1px(真實像素)的 2 倍那么寬,所以設計師會覺得這個線太粗了,和他的設計稿不一樣,在新版的系統里,已經開始逐漸支持 0.5px(css) 這種寫法,所以如果設計師在大圖上設計了一個 1px(真實像素)的線的話,前端工程師直接除以 2,寫 0.5px(css) 就好了,

另外一種解釋

事實就是它并沒有變粗,就是 css 單位中的 1px,對于 dpr 為 2 的設備,它實際能顯示的最小值是 0.5px,

設計師口中說的 1px 是針對設備物理像素的,換算成 css 像素就是 0.5px,

一句話總結,background:1px solid black 在任何螢屏上都是一樣粗的,但是 retina 屏可以顯示比這更細的邊框,然后設計師就不樂意了,讓你改,..

瀏覽器是如何渲染頁面的?

答:

決議 HTML 檔案,創建 DOM 樹

自上而下,遇到任何樣式(link、style)與腳本(script)都會阻塞(外部樣式不阻塞后續外部腳本的加載),

決議 CSS

優先級:瀏覽器默認設定<用戶設定<外部樣式<行內樣式<HTML中的style樣式

構建渲染樹

將 CSS 與 DOM 合并,構建渲染樹(Render Tree)

布局和繪制

布局和繪制,重繪(repaint)和重排(reflow)

如何寫出高性能的 HTML?

答:

避免使用 Iframe

Iframe 也叫行內 frame,可以把一個 HTML 檔案嵌入到另一個檔案中,使用 iframe 的好處是被嵌入的檔案可以完全獨立于其父檔案,憑借此特點我們通常可以使瀏覽器模擬多執行緒,需要注意的是使用 iframe 并不會增加同域名下的并行下載數,瀏覽器對同域名的連接總是共享瀏覽器級別的連接池,即使是跨視窗或跨標簽頁,這在所有主流瀏覽器都是如此,也因為這樣這讓 iframe 帶來的好處大打折扣,

在頁面加載程序中 iframe 元素會阻塞父檔案 onl oad 事件的觸發,而開發者程式通常會在 onl oad 事件觸發時初始化 UI 操作,例如,設定登錄區域的焦點,因為用戶習慣等待這一操作,所以盡可能的讓 onl oad 事件觸發從而使用戶的等待時間變短是非常重要的,另外開發者會把一些重要的行為系結在 unload 事件上,而不幸的是在一些瀏覽器中,只有當 onl oad 事件觸發后 unload 事件才能觸發,如果 onl oad 事件長時間未觸發,而用戶已經離開當前頁面,那么 unload 事件也將永遠得不到觸發, 那是否有方案可以讓 onl oad 事件不被 iframe 阻塞嗎?有個簡單的解決方案來避免 onl oad 事件被阻塞,使用 JavaScript 動態的加載 iframe 元素或動態設定 iframe 的 src 屬性:

<iframe id=iframe1 ></iframe>
document.getElementById(‘iframe1’).setAttribute(‘src’, ‘url’);

但其僅在高級瀏覽器 中有效,對于 Internet Explorer 8 及以下的瀏覽器無效,除此之外我們必須知道 iframe 是檔案內最消耗資源的元素之一,在 Steve Souders 的測驗中 ,在測驗頁面中分別加載 100 個 A、DIV、SCRIPT、STYLE 和 IFRAME 元素,并且分別在 Chrome、Firefox、Internet Explorer、Opera、Safari 中運行了 10 次,結果顯示創建 iframe 元素的開銷比創建其他型別的 DOM 元素要高 1~2 個數量級,在測驗中所有的 DOM 元素都是空的,如加載大的腳本或樣式塊可能比加載某些 iframe 元素耗時更長,但從基準測驗結果來看,即使是空的 iframe,其開銷也是非常昂貴的,鑒于 iframe 的高開銷,我們應盡量避免使用,尤其是對于移動設備,對于目前大部分還是只有有限的 CPU 與記憶體的情況下,更應避免使用 iframe,

避免空鏈接屬性

空的鏈接屬性是指 img、link、script、ifrrame 元素的 src 或 href 屬性被設定了,但是屬性卻為空,如<img src=''>,我們創建了一個圖片,并且暫時設定圖片的地址為空,希望在未來動態的去修改它,但是即使圖片的地址為空,瀏覽器依舊會以默認的規則去請求空地址:

  • Internet Explorer 8 及以下版本瀏覽器只在 img 型別元素上出現問題,IE 會把 img 的空地址決議為當前頁面地址的目錄地址,例如:如果當前頁面地址為 http://example.com/dir/page.html,IE 會把空地址決議為 http://example.com/dir/ 地址并請求,
  • 早些版本的 Webkit 內核瀏覽器 與 Firefox 會把空地址決議為當前頁面的地址,如果頁面內有多個空鏈接屬性元素,當前頁面的服務器則會被請求多次,增加服務器的負載,相較桌面瀏覽器對內核的更新升級較積極,這個問題在 ios 與 android 系統的移動瀏覽器上問題可能較嚴重,
  • 幸運的是所有主流瀏覽器面對 iframe 的 src 屬性為空時,會把空地址決議為 about:blank 地址,而不會向服務器發出額外的請求,

避免節點深層級嵌套

深層級嵌套的節點在初始化構建時往往需要更多的記憶體占用,并且在遍歷節點時也會更慢些,這與瀏覽器構建 DOM 檔案的機制有關,例如下面 HTML 代碼:

<html>
<body>
<p>
Hello World
</p>
<div> <img src="example.png"/></div>
</body>
</html>

通過瀏覽器 HTML 決議器的決議,瀏覽器會把整個 HTML 檔案的結構存盤為 DOM 樹結構,當檔案節點的嵌套層次越深,構建的 DOM 樹層次也會越深,

縮減 HTML 檔案大小

提高下載速度最顯而易見的方式就是減少檔案的大小,特別是壓縮內嵌在 HTML 檔案中的 JavaScript 和 CSS 代碼,這能使得頁面體積大幅精簡,除此之外減少 HTML 檔案大小還可以采取下面幾種方法:

  • 刪掉 HTM 檔案對執行結果無影響的空格空行和注釋
  • 避免 Table 布局
  • 使用 HTML5

顯式指定檔案字符集

HTML 頁面開始時指定字符集,有助于瀏覽器可以立即開始決議 HTML 代碼,HTML 檔案通常被決議為一序列的帶字符集編碼資訊的字串通過 internet 傳送,字符集編碼在 HTTP 回應頭中,或者 HTML 標記中指定,瀏覽器根據獲得的字符集,把編碼決議為可以顯示在螢屏上的字符,如果瀏覽器不能獲知頁面的編碼字符集,一般都會在執行腳本和渲染頁面前,把位元組流快取,然后再搜索可進行決議的字符集,或以默認的字符集來決議頁面代碼,這會導致消耗不必要的時間,為了避免瀏覽器把時間花費在搜尋合適的字符集來進行解碼,所以最好在檔案中總是顯式的指定頁面字符集,

顯式設定圖片的寬高

當瀏覽器加載頁面的 HTML 代碼時,有時候需要在圖片下載完成前就對頁面布局進行定位,

如果 HTML 里的圖片沒有指定尺寸(寬和高),或者代碼描述的尺寸與實際圖片的尺寸不符時,瀏覽器則要在圖片下載完成后再 “回溯” 該圖片并重新顯示,這會消耗額外時間,

所以,最好為頁面里的每一張圖片都指定尺寸,不管是在頁面 HTML 里的<img> 標簽,還是在 CSS 里,

<img src="hello.png" width="400" height="300">

避免腳本阻塞加載

當瀏覽器在決議常規的 script 標簽時,它需要等待 script 下載完畢,再決議執行,而后續的 HTML 代碼只能等待,為了避免阻塞加載,應把腳步放到檔案的末尾,如把 script 標簽插入在 body 結束標簽之前:

<script src="example.js" ></script>
</body>

iframe 的優缺點?

答:

  • iframe 會阻塞主頁面的 onl oad 事件;
  • iframe 和主頁面共享連接池,而瀏覽器對相同域的連接有限制,所以會影響頁面的并行加載,
  • 使用 iframe 之前需要考慮這兩個缺點,如果需要使用 iframe,最好是通過 javascript 動態給 iframe 添加 src 屬性值,這樣可以可以繞開以上兩個問題,

Canvas 和 SVG 圖形的區別是什么?

答:Canvas 和 SVG 都可以在瀏覽器上繪制圖形,

SVG Canvas 繪制后記憶,換句話說任何使用 SVG 繪制的形狀都能被記憶和操作,瀏覽器可以再次顯示 Canvas 則是繪制后忘記,一旦繪制完成你就不能訪問像素和操作它 SVG 對于創建圖形例如 CAD 軟體是良好的,一旦東西繪制,用戶就想去操作它 Canvas 則用于繪制和遺忘類似動漫和游戲的場畫,

為了之后的操作,SVG 需要記錄坐標,所以比較緩慢,

因為沒有記住以后事情的任務,所以 Canvas 更快,

我們可以用繪制物件的相關事件處理我們不能使用繪制物件的相關事件處理,因為我們沒有他們的參考解析度獨立解析度依賴

  • SVG 并不屬于 html5 專有內容,在 html5 之前就有 SVG,
  • SVG 檔案的擴展名是”.svg”,
  • SVG 繪制的影像在圖片質量不下降的情況下被放大,
  • SVG 影像經常在網頁中制作小圖示和一些動態效果圖,

聊聊 meta 標簽?

答:核心

提供給頁面的一些元資訊(名稱 / 值對),有助于 SEO,

屬性值

name

名稱 / 值對中的名稱,author、description、keywords、generator、revised、others, 把 content 屬性關聯到一個名稱,

http-equiv

沒有 name 時,會采用這個屬性的值,content-type、expires、refresh、set-cookie,把 content 屬性關聯到 http 頭部

content

名稱 / 值對中的值, 可以是任何有效的字串, 始終要和 name 屬性或 http-equiv 屬性一起使用

scheme

用于指定要用來翻譯屬性值的方案,

CSS 面試題

CSS3有哪些新特性?

答:

  • 新增各種CSS選擇器 (: not(.input):所有 class 不是“input”的節點)
  • 圓角 (border-radius:8px)
  • 多列布局 (multi-column layout)
  • 陰影和反射 (Shadow\Reflect)
  • 文字特效 (text-shadow、)
  • 文字渲染 (Text-decoration)
  • 線性漸變 (gradient)
  • 旋轉 (transform)
  • 增加了旋轉,縮放,定位,傾斜,影片,多背景
  • transform:scale(0.85,0.90) translate(0px,-30px) skew(-9deg,0deg) Animation:

清除浮動的?種?式,各?的優缺點?

答:

  • ?級 div 定義 height
  • 結尾處加空 div 標簽 clear:both
  • ?級 div 定義偽類 :after 和 zoom
  • ?級 div 定義 overflow:hidden
  • ?級 div 也浮動,需要定義寬度
  • 結尾處加 br 標簽 clear:both
  • ?較好的是第3種?式,好多?站都這么?

請解釋一下CSS3的Flexbox(彈性盒布局模型)?

答:設為Flex布局以后( display: flex;),子元素的float、clear和vertical-align屬性將失效,

采用Flex布局的元素,稱為Flex容器(flex container),簡稱”容器”,

它的所有子元素自動成為容器成員,稱為Flex專案(flex item),簡稱”專案”,

容器默認存在兩根軸:水平的主軸(main axis)和垂直的交叉軸(cross axis),

主軸的開始位置(與邊框的交叉點)叫做main start,結束位置叫做main end;交叉軸的開始位置叫做cross start,結束位置叫做cross end,

專案默認沿主軸排列,單個專案占據的主軸空間叫做main size,占據的交叉軸空間叫做cross size,

容器的屬性

以下6個屬性設定在容器上(justify-content和align-items、flex-wrap:wrap最常用的),

  • justify-content:定義了專案在主軸上的對齊方式,它可能取5個值
    • center: 居中
    • flex-start(默認值):左對齊
    • flex-end:右對齊
    • space-between:兩端對齊,專案之間的間隔都相等,
    • space-around:每個專案兩側的間隔相等,所以,專案之間的間隔比專案與邊框的間隔大一倍,
  • align-items:屬性定義專案在交叉軸上如何對齊,它可能取5個值,
    • center:交叉軸的中點對齊,
    • flex-start:交叉軸的起點對齊,
    • flex-end:交叉軸的終點對齊,
    • baseline: 專案的第一行文字的基線對齊,
    • stretch(默認值):如果專案未設定高度或設為auto,將占滿整個容器的高度,
  • flex-flow:flex-flow屬性是flex-direction屬性和flex-wrap屬性的簡寫形式,默認值為row nowrap,
  • flex-direction:屬性決定主軸的方向(即專案的排列方向);
    • row(默認值):主軸為水平方向,起點在左端,
    • row-reverse:主軸為水平方向,起點在右端,
    • column:主軸為垂直方向,起點在上沿,
    • column-reverse:主軸為垂直方向,起點在下沿,
  • flex-wrap:默認情況下,專案都排在一條線(又稱”軸線”)上,flex-wrap屬性定義,如果一條軸線排不下,如何換行,
    • 它可能取三個值,
    • (1)nowrap(默認):不換行,
    • (2)wrap:換行,第一行在上方,【這個屬性經常用】
    • (3)wrap-reverse:換行,第一行在下方,
  • align-content:屬性定義了多根軸線的對齊方式,如果專案只有一根軸線,該屬性不起作用
    • flex-start:與交叉軸的起點對齊,
    • flex-end:與交叉軸的終點對齊,
    • center:與交叉軸的中點對齊,
    • space-between:與交叉軸兩端對齊,軸線之間的間隔平均分布,
    • space-around:每根軸線兩側的間隔都相等,所以,軸線之間的間隔比軸線與邊框的間隔大一倍,
    • stretch(默認值):軸線占滿整個交叉軸,

專案的屬性總結

以下6個屬性設定在專案上,(align-self、flex、order)

  • align-self
    • 屬性允許單個專案有與其他專案不一樣的對齊方式,可覆寫align-items屬性,默認值為auto,表示繼承父元素的align-items屬性,如果沒有父元素,則等同于stretch;該屬性可能取6個值,除了auto,其他都與align-items屬性完全一致,
    • auto / flex-start / flex-end / center / baseline / stretch;
  • flex
    • 屬性是flex-grow, flex-shrink 和 flex-basis的簡寫,默認值為0 1 auto,后兩個屬性可選,該屬性有兩個快捷值:auto (1 1 auto) 和 none (0 0 auto),建議優先使用這個屬性,而不是單獨寫三個分離的屬性,因為瀏覽器會推算相關值,
  • order
    • 屬性定義專案的排列順序,數值越小,排列越靠前,默認為0,
  • flex-grow
    • 屬性定義專案的放大比例,默認為0,即如果存在剩余空間,也不放大,如果所有專案的flex-grow屬性都為1,則它們將等分剩余空間(如果有的話),如果一個專案的flex-grow屬性為2,其他專案都為1,則前者占據的剩余空間將比其他項多一倍,
  • flex-shrink
    • flex-shrink屬性定義了專案的縮小比例,默認為1,即如果空間不足,該專案將縮小,如果所有專案的flex-shrink屬性都為1,當空間不足時,都將等比例縮小,如果一個專案的flex-shrink屬性為0,其他專案都為1,則空間不足時,前者不縮小,負值對該屬性無效,
  • flex-basis
    • flex-basis屬性定義了在分配多余空間之前,專案占據的主軸空間(main size),瀏覽器根據這個屬性,計算主軸是否有多余空間,它的默認值為auto,即專案的本來大小,它可以設為跟width或height屬性一樣的值(比如350px),則專案將占據固定空間,

解釋下浮動和它的作業原理?清除浮動的技巧?

答:浮動元素脫離檔案流,不占據空間,

浮動元素碰到包含它的邊框或者浮動元素的邊框停留

使用空標簽清除浮動

這種方法是在所有浮動標簽后面添加一個空標簽 定義css clear:both. 弊端就是增加了無意義標簽,

使用overflow

給包含浮動元素的父標簽添加css屬性 overflow:auto; zoom:1; zoom:1用于兼容IE6,

引入樣式表CSS的方式有幾種?分別是什么?優先級是什么?

答:在HTML中常用以下四種方式定義CSS:

  • inline(行內式,也稱行內樣式)、
  • embedding(嵌入式)、
  • linking(外部參考式)
  • 匯入樣式表(@import ),

詳細

  • 一:行內式/行內樣式:使用該屬性可以直接指定樣式,當然,該樣式僅能用于該元素的內容,對于另一個同名的元素則不起作用,
  • 二:嵌入式(style):用戶可在HTML檔案頭部定義多個style元素,實作多個樣式表,
  • 三:外部參考式(link)

    • ①可以在多個檔案間共享樣式表,對于較大規模的網站,將CSS樣式定義獨立成一個一個的檔案,可有效地提高效率,并有利于對網站風格的維護,
    • ②可以改變樣式表,而無需更改HTML檔案,這也與HTML語言內容與形式分開的原則相一致,
    • ③可以根據介質有選擇的加載樣式表,
  • 四:匯入樣式表:@import url(“css/base.css”);

優先級:就近原則

行內式>內嵌式>外部參考式>匯入樣式表

什么是回應式設計?回應式設計的基本原理是什么?

答:回應式設計簡而言之,就是一個網站能夠兼容多個終端——而不是為每個終端做一個特定的版本,

優點

  • 面對不同解析度設備靈活性強
  • 能夠快捷解決多設備顯示適應問題

缺點

兼容各種設備作業量大,效率低下

代碼累贅,會出現隱藏無用的元素,加載時間加長

其實這是一種折中性質的設計解決方案,多方面因素影響而達不到最佳效果

一定程度上改變了網站原有的布局結構,會出現用戶混淆的情況

【通過這三個步驟,你一定可以了解回應式設計的基本原理和media query】

第一步. Meta 標簽

為了適應螢屏,多數的移動瀏覽器會把HTML網頁縮放到設備螢屏的寬度,你可以使用meta標簽的viewport屬性來設定,下面的代碼告訴瀏覽器使用設備螢屏寬度作為內容的寬度,并且忽視初始的寬度設定,這段代碼寫在 <head>里面

<meta name="viewport" content="width=device-width, initial-scale=1.0">

IE8及以下的瀏覽器不支持media query,你可以使用 media-queries.js 或 respond.js ,這樣IE就能支持media query了,

<!--[if lt IE 9]> <script src="http://css3-mediaqueries-js.googlecode.com/svn/trunk/css3-mediaqueries.js"></script> <![endif]-->

第二步. HTML 結構

這個例子里面,有header、content、sidebar和footer等基本的網頁布局,

header 有固定的高180px,content 容器的寬是600px,sidebar的寬是300px,

第三步. Media Queries

CSS3 media query 回應式網頁設計的關鍵,它像一個if陳述句,告訴瀏覽器如何根據特定的螢屏寬口來加載網頁,

如果螢屏視窗小于980px,規則就生效,設定了容器的寬度為百分比的形式而不是像素單位,這樣會更加靈活,

如果檔案寬度小于 300 像素則修改背景演示(background-color):

@media screen and (max-width: 300px) {
    body {
        background-color:lightblue;
    }
}

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

答:均分原理

在矩形的直角,兩條邊的樣式要均分

比如左上角 border-topborder-left 的樣式要均分

如果border-left 無色透明, border-top有色, 就會出來一個45度的銳角

說說盒子模型?

答:很簡單,但是能說好比較難度的,可以擴展,回答越深,越廣,加分越多

內容+padding+border+margin這個說一遍,然后話風一轉,說如果在移動端,這個盒子模型就不是很適合做開發了,最好用box-sizing: border-box;屬性改變一下盒子模型;

寫了這個屬性之后寬度會包括border,因為移動端主要是用百分比,不可能通過像素精確控制,如果讓兩個螢屏50%的div并排,而且這兩個div還有border和padding,原來的盒子模型就做不到,必用用這個css屬性改變一下才可以的,但是還有一個問題;

用inline-block讓兩個div并排后,中間會一個空白字符,還要把換行代碼寫在一行,或者用font-size:0;來解決

兩種盒子模型

css3中定義了box-sizing屬性;在移動端的應用;

  • padding和margin的簡寫寫法
  • margin的兩種居中DIV方法;(margin:0 auto;和負margin的寫法)
  • margin的兼容性問題,IE6img下面有空白;
  • border的CSS3新增屬性;
  • border寫三角形的用法;

對BFC的規范的理解?

答:(W3C CSS 2.1 規范中的一個概念,它決定了元素如何對其內容進行定位,以及與其他元素的關 系和相互作用,)

BFC,塊級格式化背景關系,一個創建了新的BFC的盒子是獨立布局的,盒子里面的子元素的樣式不會影響到外面的元素

,在同一個 BFC 中的兩個毗鄰的塊 級盒在垂直方向(和布局方向有關系)的 margin 會發生折疊,

一個頁面是由很多個 Box 組成的,元素的型別和 display 屬性,決定了這個 Box 的型別,

不同型別的 Box,會參與不同的 Formatting Context(決定如何渲染檔案的容器),因此Box內的元素會以不同的方式渲染,也就是說BFC內部的元素和外部的元素不會互相影響,

為什么說position absolute 跟float 影響性能?

答:position: absolute會完全脫離檔案流

不再在z-index:0層保留占位符,其left、top、right、bottom 值是相對于自己最近的一個位置設定了position: relative 或 position: absolute的祖先元素的;

如果祖先元素都沒有設定position: relative 或 position: absolute,那么就相對于body元素,

float也能改變檔案流

不同的是,float屬性不會讓元素“上浮”到另一個z-index層,它仍然讓元素在z-index:0層排列,float不像position: relative 和 position: absolute那樣,它不能通過left、top、right、bottom屬性精確地控制元素的坐標,它只能通過float:left 和 float:right 來控制元素在同層里“左浮”和“右浮”,float會改變正常的檔案流排列,影響到周圍元素,

另一個有趣的現象是position: absolute 和 float會隱式地改變display型別,不論之前什么型別的元素(display:none除外),只要設定了position: absolute 或 float中任意一個,都會讓元素以display:inline-block的方式顯示:可以設定長寬,默認寬度并不占滿父元素,

就算我們顯示地設定display:inline或者display:block,也仍然無效(float在IE6 下的雙倍邊距bug就是利用添加display:inline來解決的),

值得注意的是,position: relative卻不改變display的型別,

position屬性為absolute或fixed的元素,重排的開銷會比較小,因為不用考慮它對其他元素的影響

這一條規則可以解釋:為什么在移動開發時盡量使用Position:absolute;而不是float來浮動元素

JS為什么會放在下面,CSS為什么放在上面?

答:瀏覽器從上到下依次決議html檔案,

將 css 檔案放到頭部, css 檔案可以先加載,避免先加載 body 內容,導致頁面一開始樣式錯亂,然后閃爍,

將 javascript 檔案放到底部是因為:若

將 javascript 檔案放到 head 里面,就意味著必須等到所有的 javascript 代碼都被 下載、決議和執行完成 之后才開始呈現頁面內容,

這樣就會造成呈現頁面時出現明顯的延遲,視窗一片空白,

為避免這樣的問題一般將全部 javascript 檔案放到 body 元素中頁面內容的后面,

浮動元素引起的問題和解決辦法?

答:

  • 父元素的高度無法被撐開,影響與父元素同級的元素
  • 與浮動元素同級的非浮動元素會跟隨其后
  • 若非第一個元素浮動,則該元素之前的元素也需要浮動,否則會影響頁面顯示的結構

額外標簽法

<div style="clear:both;"></div>

缺點:不過這個辦法會增加額外的標簽使HTML結構看起來不夠簡潔,

使用after偽元素

#parent:after{
    content : ".";
    height : 0;
    visibility : hidden;
    display : block;
    clear : both;
}

父元素也設定浮動

缺點:使得與父元素相鄰的元素的布局會受到影響,不可能一直浮動到body,不推薦使用

設定 overflow 為 hidden 或者 auto

CSS的瀏覽器兼容性問題總結:

答:所謂的瀏覽器兼容性問題,是指因為不同的瀏覽器對同一段代碼有不同的決議,造成頁面顯示效果不統一的情況,

在大多數情況下,我們的需求是,無論用戶用什么瀏覽器來查看我們的網站或者登陸我們的系統,都應該是統一的顯示效果,

除了IE6和7的自身bug,其他瀏覽器BUG很少的,如果你理解了每一句CSS的意思,規范撰寫代碼,一般很少會出bug,

舉個很簡單的例子,很多人float:left后,擔心IE6的雙margin bug,不管三七二十一,加display:inline,其實這是錯的,你要搞清楚IE6的雙margin bug是如何產生的:

浮動方向有同方向的margin值,才會出現這個bug,所以如果只是單純浮動,是不會產生這個bug的,

瀏覽器兼容問題一:不同瀏覽器的標簽默認的外補丁和內補丁不同

問題癥狀:隨便寫幾個標簽,不加樣式控制的情況下,各自的margin 和padding差異較大,

解決方案:CSS里 *{margin:0;padding:0;}

備注:這個是最常見的也是最易解決的一個瀏覽器兼容性問題,幾乎所有的CSS檔案開頭都會用通配符*來設定各個標簽的內外補丁是0,

瀏覽器兼容問題二:塊屬性標簽float后,又有橫行的margin情況下,在IE6顯示margin比設定的大

問題癥狀:常見癥狀是IE6中后面的一塊被頂到下一行

碰到頻率:90%(稍微復雜點的頁面都會碰到,float布局最常見的瀏覽器兼容問題)

解決方案:在float的標簽樣式控制中加入 display:inline;將其轉化為行內屬性

備注:我們最常用的就是div+CSS布局了,而div就是一個典型的塊屬性標簽,橫向布局的時候我們通常都是用div float實作的,橫向的間距設定如果用margin實作,這就是一個必然會碰到的兼容性問題,

瀏覽器兼容問題三:設定較小高度標簽(一般小于10px),在IE6,IE7,遨游中高度超出自己設定高度

問題癥狀:IE6、7和遨游里這個標簽的高度不受控制,超出自己設定的高度

碰到頻率:60%

解決方案:給超出高度的標簽設定overflow:hidden;或者設定行高line-height 小于你設定的高度,

備注:這種情況一般出現在我們設定小圓角背景的標簽里,出現這個問題的原因是IE8之前的瀏覽器都會給標簽一個最小默認的行高的高度,

即使你的標簽是空的,這個標簽的高度還是會達到默認的行高,

瀏覽器兼容問題四:行內屬性標簽,設定display:block后采用float布局,又有橫行的margin的情況,IE6間距bug

問題癥狀:IE6里的間距比超過設定的間距

碰到幾率:20%

解決方案:在display:block;后面加入display:inline;display:table;

備注:行內屬性標簽,為了設定寬高,我們需要設定display:block;(除了input標簽比較特殊),

在用float布局并有橫向的margin后,在IE6下,他就具有了塊屬性float后的橫向margin的bug,

不過因為它本身就是行內屬性標簽,所以我們再加上display:inline的話,它的高寬就不可設了,這時候我們還需要在display:inline后面加入display:talbe,

瀏覽器兼容問題五:圖片默認有間距

問題癥狀:幾個img標簽放在一起的時候,有些瀏覽器會有默認的間距,加了問題一中提到的通配符也不起作用,

碰到幾率:20%

解決方案:使用float屬性為img布局

備注:因為img標簽是行內屬性標簽,所以只要不超出容器寬度,img標簽都會排在一行里,但是部分瀏覽器的img標簽之間會有個間距,

去掉這個間距使用float是正道,(我的一個學生使用負margin,雖然能解決,但負margin本身就是容易引起瀏覽器兼容問題的用法,所以我禁止他們使用)

瀏覽器兼容問題六:標簽最低高度設定min-height不兼容

問題癥狀:因為min-height本身就是一個不兼容的CSS屬性,所以設定min-height時不能很好的被各個瀏覽器兼容

碰到幾率:5%

解決方案:如果我們要設定一個標簽的最小高度200px,需要進行的設定為:{min-height:200px; height:auto !important; height:200px; overflow:visible;}

備注:在B/S系統前端開時,有很多情況下我們又這種需求,當內容小于一個值(如300px)時,

容器的高度為300px;當內容高度大于這個值時,容器高度被撐高,而不是出現滾動條,這時候我們就會面臨這個兼容性問題,

瀏覽器兼容問題七:透明度的兼容CSS設定

做兼容頁面的方法是:每寫一小段代碼(布局中的一行或者一塊)我們都要在不同的瀏覽器中看是否兼容,當然熟練到一定的程度就沒這么麻煩了,

建議經常會碰到兼容性問題的新手使用,很多兼容性問題都是因為瀏覽器對標簽的默認屬性決議不同造成的,只要我們稍加設定都能輕松地解決這些兼容問題,

如果我們熟悉標簽的默認屬性的話,就能很好的理解為什么會出現兼容問題以及怎么去解決這些兼容問題,

/* CSS hack*/ 我很少使用hacker的,可能是個人習慣吧,我不喜歡寫的代碼IE不兼容,然后用hack來解決,

不過hacker還是非常好用的,使用hacker我可以把瀏覽器分為3類:IE6 ;IE7和遨游;其他(IE8 chrome ff safari opera等)

IE6認識的hacker 是下劃線_ 和星號 *

IE7 遨游認識的hacker是星號 *

比如這樣一個CSS設定:

height:300px;*height:200px;_height:100px; IE6瀏覽器在讀到height:300px的時候會認為高時300px;

繼續往下讀,他也認識*heihgt, 所以當IE6讀到*height:200px的時候會覆寫掉前一條的相沖突設定,認為高度是200px,

繼續往下讀,IE6還認識_height,所以他又會覆寫掉200px高的設定,把高度設定為100px;

IE7和遨游也是一樣的從高度300px的設定往下讀,

當它們讀到*height200px的時候就停下了,因為它們不認識_height,所以它們會把高度決議為200px,剩下的瀏覽器只認識第一個height:300px;所以他們會把高度決議為300px,

因為優先級相同且想沖突的屬性設定后一個會覆寫掉前一個,所以書寫的次序是很重要的,

怎么讓Chrome支持小于12px 的文字?

答:

html, body {-webkit-text-size-adjust: none;}

新版的chrome已經取消了;

CSS3有個新的屬性transform,而我們用到的就是transform:scale()

p{font-size:10px;-webkit-transform:scale(0.8);}

但是,如果,這個屬性會把整個p的屬性都縮放,如果我有背景呢?我有邊框呢?都會被縮小!

所以我們修改結構為

<p><span>我是一個小于12PX的字體</span></p>

代碼如下

body,p{ margin:0; padding:0}
p{font-size:10px;}
span{-webkit-transform:scale(0.8); display:inline-block}

定義 display:inline-block而不是 display:block;會發現,

會存在一定的邊距,貌似margin或者padding的間距,

這就是縮放存在問題,原來的位置還占有12px字體的大小,

所以,要對應修改margin了,定義為負的,

還有一個網上別人分享的方法;


html {font-size: 625%;}
body {font-size: 0.16rem;}

JavaScript 面試題

JavaScript 中有幾種資料型別?

答:資料型別可以分為基本資料型別參考資料型別

基本資料型別 :StringNumberBooleanNullUndefinedSymbolBigInt ;

參考資料型別:Object;

有些小伙伴喜歡把參考資料型別這塊分為 ObjectFuntion,這也是可以的,(主要是 typeof可以檢測function,還有就是Function這個類比較特殊)

其中 SymbolBigInt 是新增的資料型別

JavaScript []和{}表示什么?

答:[]表示陣列,

{}表示物件,

這2種宣告方式都為字面量方式,

除了字面量方式外還可以用new Arraynew Object來實體化,

JavaScript 什么叫全域變數?什么叫區域變數了?是如何定義出來的?

答:全域變數是在函式外部定義的變數,在JS中全域變數屬于window物件,其作用域是整個源程式,全域變數全部存放在靜態存盤區,在程式開始執行時給全域變數分配存盤區,程式運行完畢就釋放,

區域變數是相對與全域變數而言的,在特定程序或函式中可以訪問的變數,作用域較小,當函式運行結束釋放區域變數,

在JavaScript中并沒有明確區域變數的概念,是相對于其他編程語言來描述,

參考《JavaScript高級程式設計》中,變數分全域變數和函式變數,

什么叫保留字?在定義變數時我們應該注意哪些?

保留字是JavaScript中已經定義過的字,由于考慮擴展性,一些保留字可能并沒有應用于當前的語法中,這是保留字與關鍵字的區別,

如class、abstract是保留字,在定義變數時應避免與保留字取名相同;

說一說html代碼,css代碼和js代碼的注釋方法?

答:HTML注釋語法

<!--注釋的內容-->

css注釋語法

/* 注釋內容 */

javaScript注釋

//注釋內容
/*注釋內容*/

寫一個從0到59依次回圈的計時器?

答:

var flag = 0;
function timer () {
  flag++;
  if (flag > 59) {
    flag = 0;
    return;
  }
  console.log(flag);
}
setInterval(timer, 1000);

JavaScript 事件冒泡機制?

答:事件傳播機制(不管是DOM0還是DOM2,這個機制是天生就帶的);

當觸發底層元素的某一個事件行為,那么它的上級元素的對應事件行為也會一級級的觸發,一直出發到我們的document;(只有相同的事件型別才會觸發)

從底層一級級往上傳播的機制叫做冒泡;

DOM2級系結事件,第三個引數寫false代表的是冒泡階段執行,如果寫的是true,代表的是在捕獲階段執行;

同一個元素既可以在捕獲階段處理也可以在冒泡階段處理;

DOM0級基本上只能控制冒泡階段,而DOM2級是可以控制捕獲階段的;

事件委托

利用DOM的傳播機制(點擊任意元素,document的click都要出發),我們給document系結一個點擊事件,在事件中我們只需要獲取事件源;根據不同的事件源做不同的事件就可以的了(這樣就可以不用給元素一個個的系結事件的了);

當事件發生在子元素中的時候,旺旺會引起連鎖反應,就是在它的祖先元素上也會發生這個事件,比如說你點擊了div一個,也相當于點擊了一個body,同樣相當于點擊了html,同樣相當于點擊了document;

理解事件傳播的時候要注意兩點

一是事件本身在傳播,而不是系結在事件上的方法會傳播;

二是并非所有的事件都會傳播,像onfocusonblur事件就不傳播,onmouseenteronmouseleave事件也不會傳播;

我們要知道常見的事件默認行為有哪些,并且要知道組織默認行為,只要系結到這個行為事件的方法最后加一句,return false就可以了;

但是需要強調的是,如果你的事件系結是用addEventListener來實作的,那么組織默認行為必須用e.preventDefault=true;

阻止事件冒泡

function (e) {
e.preventDefault();
}

事件委托:事件委托就是用事件的傳播機制,無論哪一個網頁元素,它的click事件都會最終傳到document上,這樣,則只需在document上處理click事件即可;

document.onclick=function(e){
e=e||window.event;
var target=e.targrt||e.srcElement;//獲取事件源是關鍵;
alert(target.nodeName);
return false;
}

事件委托的關鍵是理解好事件源的概念;

JavaScript 事件兼容性問題有哪些?怎么解決?

答:1、事件物件本身,標準瀏覽器是時間發生時自動給方法傳一個實參,這個實參就是時間物件,IE是全域的window.event;(解決方法是:e=e||window.event

2、事件源:e.target,IE下是e.srcElement;(解決辦法是是 var target=e.target||e.srcElement;

3、DOM二級事件系結:ele.addEventListener,IE是ele.attachEvent

解決辦法是通過

if(ele.addEventListener){

}else if(ele.attachEvent){
}

這樣的方法來解決系結對應的移除方法是removeEventListener和IE的detachEvent;

4、第三點中的IE的attachEvent系結的方法上,

  1. 第一點、this不是當前元素了,而是變成了window;
  2. 第二點,事件的執行順序是混亂的;
    • 在IE678中,如果系結的方法少于9個,執行的順序是相反的,如果多于9個,執行的是混亂的;
  3. 第三點,同一函式可以重復系結在同一事件上;
    • 需要解決一個函式不能重復系結在同一個事件上,低版本IE沒有遵循這個原則;要保證一個方法只能被系結到某事件上一次;

5、阻止事件傳播;e.stopPropagation,IE是e.cancelBubble=true這個屬性;這個一般不做處理,用這個屬性,還可以做觀察者模式的;

6、阻止默認行為:e.preventDefault()方法,IE是e.returnValue=false

7、e.pageX,e.pageY;相對于檔案的滑鼠坐標IE不支持這兩個屬性;但都支持clentXclentY,這個是相對于瀏覽器的滑鼠坐標,可以通過scrollTop+clientY來實作; ?


e.pageX=(document.documentElement.scrollLeft||document.body.scrollLeft)+ e.clientX;
e.pageY=(document.documentElement.scrollTop||document.body.scrollTop)+ e.clientY;

JavaScript DOM創建元素,添加元素,移動元素,復制節點,洗掉,替換元素,查找節點的方法?

答:創建元素

document.createElement('tagName');

添加元素

parent.appendChild(childNode);

注:父元素呼叫該方法

移動元素

由于DOM物件屬于參考型別,所以在操作appendChild和insertBefore方法時,

控制的節點如果是檔案中存在的節點,那么將把這個節點移到目標處,

復制節點

oLi.cloneNode(true);

注:引數true表示深度克隆(深拷貝),false 表示淺度克隆(淺拷貝),深拷貝也就是復制節點及整個節點數;淺拷貝復制節點本身,復制后回傳的節點副本屬于檔案所有,但并沒有為它指定父節點,因此,整個節點副本就成為一個‘孤兒’:

<ul id='oUl'>
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
<ul>

<script>
var myList = document.getElementById("oUl");
var deepList = myList.cloneNode(true);
console.log(deepList.childNodes.length);//3 (IE<9) 或7 (其它瀏覽器)

var shallowList = myList.cloneNode(false);
console.log(shallowList.childNodes.length);//0
</script>

以上代碼注意childNode包含文本節點,

cloneNode()方法不會復制添加DOM節點的JS屬性,例如事件處理程式等,這個方法只復制特性,其他一切都不會復制,

洗掉節點

parentNode.removeChild(childNode);

注:父元素呼叫該方法,回傳值為被洗掉的節點

替換元素

parentNode.replaceChild(newNode,oldNode);

注:oldNode節點必須是parentNode的子節點,

插入元素

parentNode.insertBefore(newEle, oldNode);

注:父元素呼叫該方法

查找節點的總結

  • childNodes—回傳節點到子節點的節點串列
  • firstChild—回傳節點的首個子節點;
  • lastChild—回傳節點的最后一個子節點;
  • nextSibling—回傳節點之后緊跟的同級節點;
  • nodeName—回傳節點的名字,根據其型別;
  • nodeType—回傳節點的型別;
  • nodeValue—設定或回傳節點的值,根據其型別;
  • ownerDocument—回傳節點的根元素(document物件);
  • parentNode—回傳節點的父節點;
  • previousSibling—回傳節點之前緊跟的同級節點;
  • text—回傳節點及其后代的文本(IE獨有);
  • xml—回傳節點及其后代的XML(IE獨有);

節點物件的方法

  • appendChild()—向節點的子節點串列的結尾添加新的子節點;
  • cloneNode()—復制節點;
  • hasChildNodes()—判斷當前節點是否擁有子節點;
  • insertBefore()—在指定的子節點前插入新的子節點;
  • normalize()—合并相鄰的Text節點并洗掉空的Text節點;
  • removeChild()—洗掉(并回傳)當前節點的指定子節點;
  • replaceChild()—用新節點替換一個子節點;

IE6獨有方法

  • selectNodes()—用一個XPath運算式查詢選擇節點;
  • selectSingleNode()—查找和XPath查詢匹配的一個節點;
  • transformNode()—使用XSLT把一個節點轉換為一個字串,transformNodeToObject()—使用XSLT把一個節點轉換成為一個檔案,

經典的問題決議

創建新節點

  • createDocumentFragment() //創建一個DOM片段
  • createElement() //創建一個具體的元素
  • createTextNode() //創建一個文本節點

添加、移除、替換、插入

  • appendChild()
  • removeChild()
  • replaceChild()
  • insertBefore() //在已有的子節點前插入一個新的子節點

查找

  • getElementsByTagName() //通過標簽名稱
  • getElementsByName() //通過元素的Name屬性的值(IE容錯能力較強,會得到一個陣列,其中包括id等于name值的)
  • getElementById() //通過元素Id,唯一性

您的DOM怎么封裝的?各種庫是怎么寫的?(DOM庫,AJAX庫,影片庫,事件庫)?

答:在作用域套作用域的時候;子作用域內盡量不回傳參考資料型別,因為閉包內的值,是另外一個子閉包的回傳值的時候,如果子閉包的回傳值是字面量,那么瀏覽器會在空閑的時候,把作用域銷毀;而如果回傳值的是一個參考資料型別的值,那么閉包是不會銷毀的,在性能優化上,不好!

下面是封裝思路;

var Tool = function () {//建構式模式;用的時候需要new一下;
    this.flag = "getElementsByClassName" in document;
    //getElementsByClassName 在IE678中是不存在的,用這個來判斷是不是低版本的IE瀏覽器;
    //每次只需要判斷this.flag是否存在就可以了;如果存在就是標準瀏覽器,如果不存在就是IE;
};
Tool.prototype = {//方法是定義在Tool的prototype上的;
    constructor: Tool,//重寫prototype后,prototype的constructor已經不是原來的Tool了;需要手動給他強制寫會到Tool上去;
    getIndex: function () {},//簡單的備注說明;
    toJSON:function(){},//簡單的備注說明;
    likeArray:function(){}//簡單的備注說明;
}

JavaScript 字串的常用方法?

答:

  • charAt 獲取指定索引位置的字符
  • charCodeAt 獲取指定索引位置的字符對應的ASCII碼值
  • indeof/lasrIndexof 獲取某個字串在第一次(最后一次)出現位置的索引,沒有的話回傳-1,我們通常用這個來檢測字串中是否包含某一個字符;
  • toUpperCase/tolowerCase將字串中的字母轉大寫|小寫;
  • split按照指定的分隔符,講一個字串拆分成陣列,和陣列的join對應;
  • substr:substr(n,m)從索引n開始截取m個字符,把截取字符回傳一個新的字串;
  • substring:substring(n,m)從索引n開始截取到索引m處(不包含m),將找到的字符回傳成一個新的字串;
  • slice:slice(n,m)和substring的用法和意思一樣,只是slice可以支持負數作為索引,出現負數索引的時候,用字串的長度+負數索引,例如:ary.slice(-6,-2),其實是ary.slice(ary.length-6,ary.length-2)
  • 上面三個方法,如果只寫一個n,都是默認截取到字串末尾的位置;
  • Replace:replace(“要替換的老字符”,“替換成的新字符”)字串中字符替換的方法,可以應用正則來統一的進行替換,在正則中我們會詳細的講解replace的強大應用;
  • Match:把所有和正則匹配到的內容都進行捕獲(不能捕獲小分組中的內容)
  • trim: 去掉字串中末尾位置的空白字符(不兼容)

JavaScript 正則驗證,當下面的這個表單提交的時候,輸入框中不能為空,如果有空格必須把空格去掉,必須是合法的手機號?

答:

<form action="" id="form1" method="get">
  電話號碼:<input type="text" value="輸入電話號碼" id="mobi" name=""/>
  <input type="submit" name=""/>
</form>
var form1 = document.getElementById('form1');
form1.onsubmit = function () {
  var mobi = document.getElementById('mobi');
  var reg = /^1\d{10}$/;
  if (reg.test(mobi.value.replace(/ /g, ''))) {
    console.log('ok');
  } else {
    console.log('error');
    return false;
  }
}

解釋一下JavaScript的同源策略?

答:概念:同源策略是客戶端腳本(尤其是Javascript)的重要的安全度量標準,

它最早出自Netscape Navigator2.0,其目的是防止某個檔案或腳本從多個不同源裝載

這里的同源策略指的是:協議域名相同,

同源策略是一種安全協議:指一段腳本只能讀取來自同一來源的視窗和檔案的屬性,

簡述 JavaScript AJAX 的原理?

答:ajax是很多種技術的集合體,其中包括

瀏覽器的xmlHTTPRequest物件,他是負責為你開通另一條連接通道,可以傳遞資訊,

javascript:他是負責呼叫XMLHTTPRequest物件進行與后臺互動的媒介,

xml是一種資料格式,用于服務器應答傳遞資訊的格式,除了xml外,還可以使用任何的文本格式,包括text,html,json等,

ajax并非一種新的技術,而是幾種原有技術的結合體,它由下列技術組合而成,

  1. 使用CSS和XHTML來表示,

  2. 使用DOM模型來互動和動態顯示,

  3. 使用XMLHttpRequest來和服務器進行異步通信,

  4. 使用javascript來系結和呼叫,

在上面幾中技術中,除了XmlHttpRequest物件以外,其它所有的技術都是基于web標準并且已經得到了廣泛使用的,XMLHttpRequest雖然目前還沒有被W3C所采納,但是它已經是一個事實的標準,因為目前幾乎所有的主流瀏覽器都支持它,

XMLHttpRequest是ajax的核心機制,它是在IE5中首先引入的,是一種支持異步請求的技術,簡單的說,也就是javascript可以及時向服務器提出請求和處理回應,而不阻塞用戶,達到無重繪的效果,

所以我們先從XMLHttpRequest講起,來看看它的作業原理,

XMLHttpRequest 的屬性

它的屬性有:

  • onreadystatechange 每次狀態改變所觸發事件的事件處理程式,
  • responseText 從服務器行程回傳資料的字串形式,
  • responseXML 從服務器行程回傳的DOM兼容的檔案資料物件,
  • status 從服務器回傳的數字代碼,比如常見的404(未找到)和200(已就緒)
  • status Text 伴隨狀態碼的字串資訊
  • readyState 物件狀態值
    • 0 (未初始化) 物件已建立,但是尚未初始化(尚未呼叫open方法)
    • 1 (初始化) 物件已建立,尚未呼叫send方法
    • 2 (發送資料) send方法已呼叫,但是當前的狀態及http頭未知
    • 3 (資料傳送中) 已接收部分資料,因為回應及http頭不全,這時通過responseBody和responseText獲取部分資料會出現錯誤,
    • 4 (完成) 資料接收完畢,此時可以通過通過responseXml和responseText獲取完整的回應資料

但是,由于各瀏覽器之間存在差異,所以創建一個XMLHttpRequest物件可能需要不同的方法,

這個差異主要體現在IE和其它瀏覽器之間,下面是一個比較標準的創建XMLHttpRequest物件的方法,


function CreateXmlHttp () {
//非IE瀏覽器創建XmlHttpRequest物件
if (window.XmlHttpRequest) {
xmlhttp = new XmlHttpRequest();
}
//IE瀏覽器創建XmlHttpRequest物件
if (window.ActiveXObject) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {
try {
xmlhttp = new ActiveXObject("msxml2.XMLHTTP");
}
catch (ex) { }
}
}
}

function Ustbwuyi () {
var data = document.getElementById("username").value;
CreateXmlHttp();
if (!xmlhttp) {
alert("創建xmlhttp物件例外!");
return false;
}
xmlhttp.open("POST", url, false);
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4) {
document.getElementById("user1").innerHTML = "資料正在加載...";
if (xmlhttp.status == 200) {
document.write(xmlhttp.responseText);
}
}
}
xmlhttp.send();
}

如上所示,函式首先檢查XMLHttpRequest的整體狀態并且保證它已經完成(readyStatus=4),即資料已經發送完畢,

然后根據服務器的設定詢問請求狀態,如果一切已經就緒(status=200),那么就執行下面需要的操作,

對于XmlHttpRequest的兩個方法,open和send,其中open方法指定了:

  • a、向服務器提交資料的型別,即post還是get,

  • b、請求的url地址和傳遞的引數,

  • c、傳輸方式,false為同步,true為異步,默認為true,如果是異步通信方式(true),客戶機就不等待服務器的回應;如果是同步方式(false),客戶機就要等到服務器回傳訊息后才去執行其他操作,我們需要根據實際需要來指定同步方式,在某些頁面中,可能會發出多個請求,甚至是有組織有計劃有隊形大規模的高強度的request,而后一個是會覆寫前一個的,這個時候當然要指定同步方式,

Send方法用來發送請求

知道了XMLHttpRequest的作業流程,我們可以看出,XMLHttpRequest是完全用來向服務器發出一個請求的,它的作用也局限于此,但它的作用是整個ajax實作的關鍵,因為ajax無非是兩個程序,發出請求和回應請求,

并且它完全是一種客戶端的技術,而XMLHttpRequest正是處理了服務器端和客戶端通信的問題所以才會如此的重要,

現在,我們對ajax的原理大概可以有一個了解了,我們可以把服務器端看成一個資料介面,它回傳的是一個純文本流,當然,這個文本流可以是XML格式,可以是Html,可以是Javascript代碼,也可以只是一個字串,

這時候,XMLHttpRequest向服務器端請求這個頁面,服務器端將文本的結果寫入頁面,這和普通的web開發流程是一樣的,不同的是,客戶端在異步獲取這個結果后,不是直接顯示在頁面,而是先由javascript來處理,然后再顯示在頁面,至于現在流行的很多ajax控制元件,比如magicajax等,可以回傳DataSet等其它資料型別,只是將這個程序封裝了的結果,本質上他們并沒有什么太大的區別,

HTTP狀態碼有哪些?分別代表什么意思?

答:

  • 100-199 用于指定客戶端應相應的某些動作,
  • 200-299 用于表示請求成功,
  • 300-399 用于已經移動的檔案并且常被包含在定位頭資訊中指定新的地址資訊,
  • 400-499 用于指出客戶端的錯誤,400 1、語意有誤,當前請求無法被服務器理解,401 當前請求需要用戶驗證 403 服務器已經理解請求,但是拒絕執行它,
  • 500-599 用于支持服務器錯誤, 503 – 服務不可用;

常用的

  • 100 Continue 繼續,一般在發送post請求時,已發送了http header之后服務端將回傳此資訊,表示確認,之后發送具體引數資訊
  • 200 OK 正常回傳資訊
  • 201 Created 請求成功并且服務器創建了新的資源
  • 202 Accepted 服務器已接受請求,但尚未處理
  • 301 Moved Permanently 請求的網頁已永久移動到新位置,
  • 302 Found 臨時性重定向,
  • 303 See Other 臨時性重定向,且總是使用 GET 請求新的 URI,
  • 304 Not Modified 自從上次請求后,請求的網頁未修改過,
  • 400 Bad Request 服務器無法理解請求的格式,客戶端不應當嘗試再次使用相同的內容發起請求,
  • 401 Unauthorized 請求未授權,
  • 403 Forbidden 禁止訪問,
  • 404 Not Found 找不到如何與 URI 相匹配的資源,
  • 500 Internal Server Error 最常見的服務器端錯誤,
  • 503 Service Unavailable 服務器端暫時無法處理請求(可能是過載或維護),

一個頁面輸入URL到頁面加載顯示完成,中間發生了什么?

答:

  1. 在瀏覽器地址欄輸?URL
  2. 瀏覽器查看快取,如果請求資源在快取中并且新鮮,跳轉到轉碼步驟

    1. 如果資源未快取,發起新請求
    2. 如果已快取,檢驗是否?夠新鮮,?夠新鮮直接提供給客戶端,否則與服務器進?驗 證,
    3. 檢驗新鮮通常有兩個HTTP頭進?控制 Expires 和 Cache-Control :
      • HTTP1.0提供Expires,值為?個絕對時間表示快取新鮮?期
      • HTTP1.1增加了Cache-Control: max-age=,值為以秒為單位的最?新鮮時間
  3. 瀏覽器決議URL獲取協議,主機,端?,path

  4. 瀏覽器組裝?個HTTP(GET)請求報?

  5. 瀏覽器獲取主機ip地址,程序如下:

    1. 瀏覽器快取
    2. 本機快取
    3. hosts?件
    4. 路由器快取
    5. ISP DNS快取
    6. DNS遞回查詢(可能存在負載均衡導致每次IP不?樣)
  6. 打開?個socket與?標IP地址,端?建?TCP鏈接,三次握?如下:

    1. 客戶端發送?個TCP的SYN=1,Seq=X的包到服務器端?
    2. 服務器發回SYN=1, ACK=X+1, Seq=Y的回應包
    3. 客戶端發送ACK=Y+1, Seq=Z
  7. TCP鏈接建?后發送HTTP請求

  8. 服務器接受請求并決議,將請求轉發到服務程式,如虛擬主機使?HTTP Host頭部判斷請 求的服務程式

  9. 服務器檢查HTTP請求頭是否包含快取驗證資訊如果驗證快取新鮮,回傳304等對應狀態碼

  10. 處理程式讀取完整請求并準備HTTP回應,可能需要查詢資料庫等操作

  11. 服務器將回應報?通過TCP連接發送回瀏覽器

  12. 瀏覽器接收HTTP回應,然后根據情況選擇關閉TCP連接或者保留重?,關閉TCP連接的四 次握?如下:

    1. 主動?發送Fin=1, Ack=Z, Seq= X報?
    2. 被動?發送ACK=X+1, Seq=Z報?
    3. 被動?發送Fin=1, ACK=X, Seq=Y報?
    4. 主動?發送ACK=Y, Seq=X報?
  13. 瀏覽器檢查回應狀態嗎:是否為1XX,3XX, 4XX, 5XX,這些情況處理與2XX不同

  14. 如果資源可快取,進?快取

  15. 對回應進?解碼(例如gzip壓縮)

  16. 根據資源型別決定如何處理(假設資源為HTML?檔)

  17. 決議HTML?檔,構件DOM樹,下載資源,構造CSSOM樹,執?js腳本,這些操作沒有嚴 格的先后順序,以下分別解釋

  18. 構建DOM樹:

    1. Tokenizing:根據HTML規范將字符流決議為標記
    2. Lexing:詞法分析將標記轉換為物件并定義屬性和規則
    3. DOM construction:根據HTML標記關系將物件組成DOM樹
  19. 決議程序中遇到圖?、樣式表、js?件,啟動下載

  20. 構建CSSOM樹:

    1. Tokenizing:字符流轉換為標記流
    2. Node:根據標記創建節點
    3. CSSOM:節點創建CSSOM樹
  21. 根據DOM樹和CSSOM樹構建渲染樹 :

    1. 從DOM樹的根節點遍歷所有可?節點,不可?節點包括:1) script , meta 這樣本身 不可?的標簽,2)被css隱藏的節點,如 display: none
    2. 對每?個可?節點,找到恰當的CSSOM規則并應?
    3. 發布可視節點的內容和計算樣式
  22. js決議如下:

    1. 瀏覽器創建Document物件并決議HTML,將決議到的元素和?本節點添加到?檔中,此 時document.readystate為loading
    2. HTML決議器遇到沒有async和defer的script時,將他們添加到?檔中,然后執??內 或外部腳本,這些腳本會同步執?,并且在腳本下載和執?時決議器會暫停,這樣就可 以?document.write()把?本插?到輸?流中,同步腳本經常簡單定義函式和注冊事件 處理程式,他們可以遍歷和操作script和他們之前的?檔內容
    3. 當決議器遇到設定了async屬性的script時,開始下載腳本并繼續決議?檔,腳本會在它 下載完成后盡快執?,但是決議器不會停下來等它下載,異步腳本禁?使? document.write(),它們可以訪問??script和之前的?檔元素
    4. 當?檔完成決議,document.readState變成interactive
    5. 所有defer腳本會按照在?檔出現的順序執?,延遲腳本能訪問完整?檔樹,禁?使? document.write()
    6. 瀏覽器在Document物件上觸發DOMContentLoaded事件
    7. 此時?檔完全決議完成,瀏覽器可能還在等待如圖?等內容加載,等這些內容完成載? 并且所有異步腳本完成載?和執?,document.readState變為complete,window觸發 load事件
  23. 顯示??(HTML決議程序中會逐步顯示??)

異步加載和延遲加載?

答:

  1. 異步加載的方案: 動態插入script標簽
  2. 通過ajax去獲取js代碼,然后通過eval執行
  3. script標簽上添加defer或者async屬性
  4. 創建并插入iframe,讓它異步執行js
  5. 延遲加載:有些 js 代碼并不是頁面初始化的時候就立刻需要的,而稍后的某些情況才需要的,

對網站重構的理解?怎么重構頁面?

答:怎么重構頁面?

撰寫 CSS、讓頁面結構更合理化,提升用戶體驗,實作良好的頁面效果和提升性能,

對網站重構的理解

網站重構:在不改變外部行為的前提下,簡化結構、添加可讀性,而在網站前端保持一致的行為,也就是說是在不改變UI的情況下,對網站進行優化,在擴展的同時保持一致的UI,

對于傳統的網站來說重構通常是

  • 表格(table)布局改為DIV+CSS

  • 使網站前端兼容于現代瀏覽器(針對于不合規范的CSS、如對IE6有效的)

  • 對于移動平臺的優化

  • 針對于SEO進行優化

  • 深層次的網站重構應該考慮的方面

  • 減少代碼間的耦合

  • 讓代碼保持彈性

  • 嚴格按規范撰寫代碼

  • 設計可擴展的API

  • 代替舊有的框架、語言(如VB)

  • 增強用戶體驗

  • 通常來說對于速度的優化也包含在重構中

  • 壓縮JS、CSS、image等前端資源(通常是由服務器來解決)

  • 程式的性能優化(如資料讀寫)

  • 采用CDN來加速資源加載

  • 對于JS DOM的優化

  • HTTP服務器的檔案快取

JavaScript new運算子具體干了什么呢?

答:

  • 創建一個空物件,并且 this 變數參考該物件,同時還繼承了該函式的原型
  • 屬性和方法被加入到 this 參考的物件中
  • 新創建的物件由 this 所參考,并且最后隱式的回傳 this

var obj = {};
obj.__proto__ = Base.prototype;
Base.call(obj);

JavaScript call和apply()的作用和區別?

答:核心

動態改變某個類的某個方法的運行環境,就是改變 this 關鍵字

  • call是引數一個一個的傳
  • apply是把引數當做一個資料統一傳進去,類似arguments;

call和apply的區別

  • Function.prototype.call 和 Function.prototype.apply 它們的作用一樣,區別僅在于傳入引數的形式的不同,

  • 當使用 call 或者 apply 的時候,如果我們傳入的第一個引數為 null,函式體內的 this 會指 向默認的宿主物件,在瀏覽器中則是 window

  • 有時候我們使用 call 或者 apply 的目的不在于指 定this 指向,而是另有用途,比如借用其他物件的方法,那么我們可以傳入 null 來代替某個具體的物件

call和apply的用途

  1. 改變this指向
  2. Function.prototype.bind
  3. 借用其他物件的方法

call

function add (a, b) {
console.log(a + b);
}
function sub (a, b) {
console.log(a - b);
}
add.call(sub, 3, 1);
//用 add 來替換 sub,add.call(sub,3,1) == add(3,1) ,所以運行結果為: (4);

js 中的函式其實是物件,函式名是對 Function 物件的參考

JavaScript 談談 this 物件的理解?

答:

  • this 指的是呼叫函式的那個物件
  • this 在沒有運行之前不能知道代表誰;js的this 指向是不確定的;和定義沒有關系,和執行有關,
  • 執行的時候,點前面是誰,this 就是誰;自執行函式里面的this 代表的是 window
  • 定時器書寫的時候,window可以省略掉;定時器執行的時候,里面的this 代表的也是 window
  • this 是js的一個關鍵字,隨著函式使用場合不同,this 的值會發生變化,

JavaScript 什么是window物件? 什么是document物件?說說你的理解

答:

  • document 是 window 的一個物件屬性,
  • window 物件表示瀏覽器中打開的視窗,
  • 如果檔案包含框架(frame 或 iframe 標簽),瀏覽器會為 HTML 檔案創建一個 window 物件,并為每個框架創建一個額外的 window 物件,
  • 所有的全域函式和物件都屬于Window 物件的屬性和方法,
    • 例如,可以只寫 document,而不必寫 window.document,
    • 同樣,可以把當前視窗物件的方法當作函式來使用,如只寫 alert(),而不必寫 Window.alert(),
  • document 對 Document 物件的只讀參考,

Javascript創建物件的幾種方式?

答:

  1. 工廠模式
  2. 建構式模式
  3. 原型模式
  4. 混合建構式和原型模式
  5. 動態原型模式
  6. 寄生建構式模式
  7. 穩妥建構式模式

javascript創建物件簡單的說,無非就是使用內置物件或各種自定義物件. 當然還可以用JSON;但寫法有很多種. 也能混合使用,

1、物件字面量的方式

person={
firstname:"zhu",
lastname:"anbang",
age:25,
eyecolor:"black"};

2、用function來模擬無參的建構式

function Person(){}
var person=new Person();//定義一個function. 如果使用new"實體化",該function可以看作是一個Class
person.name="zhu";
person.age="25";
person.work=function(){
alert(person.name+" hello...");
}
person.work();

3、用function來模擬參建構式來實作(用this關鍵字定義構造的背景關系屬性)

function Pet(name,age,hobby){
this.name=name;//this作用域:當前物件
this.age=age;
this.hobby=hobby;
this.eat=function(){
alert("我叫"+this.name+",我喜歡"+this.hobby+",是個程式員");
}
}
var maidou =new Pet("麥兜",25,"coding");//實體化、創建物件
maidou.eat();//呼叫eat方法

4、用工廠方式來創建(內置物件)

var wcDog =new Object();
wcDog.name="旺財";
wcDog.age=3;
wcDog.work=function(){
alert("我是"+wcDog.name+",汪汪汪......");
}
wcDog.work();

5、用原型方式來創建

function Dog(){}
Dog.prototype.name="旺財";
Dog.prototype.eat=function(){
alert(this.name+"是個吃貨");
}

var wangcai =new Dog();
wangcai.eat();

5、用混合方式來創建


function Car(name,price){
this.name=name;
this.price=price;
}
Car.prototype.sell=function(){
alert("我是"+this.name+". 我現在賣"+this.price+"萬元");
}
var camry =new Car("凱美瑞",27);
camry.sell();

JavaScript 模塊化開發怎么做?

答:核心:立即執行函式,不暴露私有成員

var module1 = (function () {
var _count = 0;
var m1 = function () {
//...
};
var m2 = function () {
//..
};
return {
m1: m1,
m2: m2
};
})();

求陣列中的最大值 var ary=[1,2,3,5,7,90,3,6];

答:alert(Math.max.apply(null,a))

知識點:將a作為引數傳遞,回傳值為排序后的新陣列,第一個引數為null因為不需要借用物件,此值可以忽略,

var a = [3, 4, 6, 2, 9, 11, 4];
var maxNum = Math.max.apply(null, a);
console.log(maxNum);//11
注意:a被當做引數傳遞進去,因為呼叫物件可以忽略,所以第一個引數為null,

Math.max.apply回傳的是一個新值,

還有一種方法是,先排序,然后頭尾就是我們想要的;


ary.sort(function(a,b){return a-b})
console.log(ary[0]);
console.log(ary[ary.length-1]);

js物件的深度克隆?

答:

function clone (Obj) {
  var buf;
  if (Obj instanceof Array) {
    buf = [];  //創建一個空的陣列 
    var i = Obj.length;
    while (i--) {
      buf[i] = clone(Obj[i]);
    }
    return buf;
  } else if (Obj instanceof Object) {
    buf = {};  //創建一個空物件 
    for (var k in Obj) {  //為這個物件添加新的屬性 
      buf[k] = clone(Obj[k]);
    }
    return buf;
  } else {
    return Obj;
  }
}

網路通信面試題

HTTP狀態碼及其含義?

答:

  • 1XX :資訊狀態碼
    • 100 Continue 繼續,?般在發送 post 請求時,已發送了 http header 之后服務端
    • 將回傳此資訊,表示確認,之后發送具體引數資訊
  • 2XX :成功狀態碼
    • 200 OK 正常回傳資訊
    • 201 Created 請求成功并且服務器創建了新的資源
    • 202 Accepted 服務器已接受請求,但尚未處理
  • 3XX :重定向
    • 301 Moved Permanently 請求的??已永久移動到新位置,
    • 302 Found 臨時性重定向,
    • 303 See Other 臨時性重定向,且總是使? GET 請求新的 URI ,
    • 304 Not Modified ?從上次請求后,請求的??未修改過,
  • 4XX :客戶端錯誤
    • 400 Bad Request 服務器?法理解請求的格式,客戶端不應當嘗試再次使?相同的內
    • 容發起請求,
    • 401 Unauthorized 請求未授權,
    • 403 Forbidden 禁?訪問,
    • 404 Not Found 找不到如何與 URI 相匹配的資源,
  • 5XX: 服務器錯誤
    • 500 Internal Server Error 最常?的服務器端錯誤,
    • 503 Service Unavailable 服務器端暫時?法處理請求(可能是過載或維護),

HTTP的?種請求?法?途?

答:

GET ?法

  • 發送?個請求來取得服務器上的某?資源

POST ?法

  • 向 URL 指定的資源提交資料或附加新的資料

PUT ?法

  • 跟 POST ?法很像,也是想服務器提交資料,但是,它們之間有不同, PUT 指定了資源在服務器上的位置,? POST 沒有

HEAD ?法

  • 只請求??的?部

DELETE ?法

  • 洗掉服務器上的某資源

OPTIONS ?法

  • 它?于獲取當前 URL 所?持的?法,如果請求成功,會有?個 Allow 的頭包含類似 “GET,POST” 這樣的資訊

TRACE ?法

  • TRACE ?法被?于激發?個遠程的,應?層的請求訊息回路

CONNECT ?法

  • 把請求連接轉換到透明的 TCP/IP 通道

談一下 WebSocket?

答:由于 http 存在?個明顯的弊端(訊息只能有客戶端推送到服務器端,?服 務器端不能主動推送到客戶端),導致如果服務器如果有連續的變化,這時只 能使?輪詢,?輪詢效率過低,并不適合,于是 WebSocket 被發明出來

相?與 http 具有以下有點

  • ?持雙向通信,實時性更強;
  • 可以發送?本,也可以?進制?件;
  • 協議識別符號是 ws ,加密后是 wss ;
  • 較少的控制開銷,連接創建后, ws 客戶端、服務端進?資料交換時,協議控制的資料包頭部較?,在不包含頭部的情況下,服務端到客戶端的包頭只有 2~10 位元組(取決于資料包?度),客戶端到服務端的的話,需要加上額外的4位元組的掩碼,? HTTP 協議每次通信都需要攜帶完整的頭部;
  • ?持擴展,ws協議定義了擴展,?戶可以擴展協議,或者實作?定義的?協議,(?如?
  • 持?定義壓縮演算法等)
  • ?跨域問題,

實作?較簡單,服務端庫如 socket.io 、 ws ,可以很好的幫助我們??,

?客戶端也只需要參照 api 實作即可

ajax、axios、fetch區別?

答:jQuery ajax

$.ajax({
type: 'POST',
url: url,
data: data,
dataType: dataType,
success: function () {},
error: function () {}
});

優缺點:

  • 本身是針對 MVC 的編程,不符合現在前端 MVVM 的浪潮
  • 基于原?的 XHR 開發, XHR 本身的架構不清晰,已經有了 fetch 的替代?案
  • JQuery 整個項?太?,單純使? ajax 卻要引?整個 JQuery ?常的不合理(采取個性化打包的?案?不能享受CDN服務

axios

axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

優缺點:

  • 從瀏覽器中創建 XMLHttpRequest
  • 從 node.js 發出 http 請求
  • ?持 Promise API
  • 攔截請求和回應
  • 轉換請求和回應資料
  • 取消請求
  • ?動轉換 JSON 資料
  • 客戶端?持防? CSRF/XSRF

fetch

try {
let response = await fetch(url);
let data = response.json();
console.log(data);
} catch(e) {
console.log("Oops, error", e);
}

優缺點:

  • fetcht 只對?絡請求報錯,對 400 , 500 都當做成功的請求,需要封裝去處理
  • fetch 默認不會帶 cookie ,需要添加配置項
  • fetch 不?持 abort ,不?持超時控制,使? setTimeout 及 Promise.reject 的實作的超時控制并不能阻?請求程序繼續在后臺運?,造成了量的浪費
  • fetch 沒有辦法原?監測請求的進度,?XHR可以

Ajax讀取一個xml檔案并進行決議的實體?

答案:

var xhr = new XMLHttpRequest;//->在IE7以下瀏覽器中是不兼容的
xhr=new ActiveXObject("Microsoft.XMLHTTP");
xhr=new ActiveXObject("Msxml2.XMLHTTP");
xhr=new ActiveXObject("Msxml3.XMLHTTP");
//->惰性思想
var getXHR = (function () {
  //->存放我們需要的幾個獲取Ajax物件的方法
  var ajaxAry = [
    function () {
      return new XMLHttpRequest;
    },
    function () {
      return new ActiveXObject("Microsoft.XMLHTTP");
    },
    function () {
      return new ActiveXObject("Msxml2.XMLHTTP");
    },
    function () {
      return new ActiveXObject("Msxml3.XMLHTTP");
    }
  ];
  //->回圈陣列,把四個方法依次的執行
  var xhr = null;
  for (var i = 0; i < ajaxAry.length; i++) {
    //->標準瀏覽器:i=0,獲取的是第一個函式function(){return new XMLHttpRequest;}(A1),執行的時候沒有報錯,xhr是它的回傳值也是我們的Ajax物件,沒有報錯不會走catch,執行getXHR = A1,這樣把外面的getXHR重寫了,遇到break回圈結束
    //->IE6瀏覽器:i=0,獲取第一個函式執行,IE6不支持XMLHttpRequest,所以會報錯,執行catch中的continue繼續下一次的回圈,i=1,獲取第二個函式 function(){return new ActiveXObject("Microsoft.XMLHTTP");}(A2),執行沒有報錯,那么開始執行getXHR = A2,遇到break結束整個回圈,此時外面的getXHR = A2
    var tempFn = ajaxAry[i];
    try {
      xhr = tempFn();
    } catch (e) {
      continue;
    }
    getXHR = tempFn;
    break;
  }
  if (!xhr) {
    throw new Error("你的瀏覽器版本也太LOW了吧,還能不能愉快的玩耍~~");
  }
  return getXHR;
})();
var xhr = getXHR();
xhr.open("get", "test.txt?_=" + Math.random(), true);
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {
    var val = xhr.responseText;
    console.log(val);
  }
};
xhr.send(null);

避免回傳的資料是亂碼的

->前端頁面是UTF-8編碼,如果我們從服務器請求回來的資料不是UTF-8編碼格式,那么獲取到的內容中,中文漢字會出現亂碼

->需要我們使用”UTF-8到底的原則”:前端頁面、JS、CSS、后臺代碼、資料庫、請求傳遞的資料統一都采用一個編碼UTF-8

->[RESPONSE] Content-Type:text/plain(純文本)、application/json(JSON格式的)…設定回應主體中內容的格式

前端:設定請求頭,獲取回應頭

->在前端的JS中我們可以使用 xhr.setRequestHeader([name],[value]) 設定請求頭的資訊;

可以使用 xhr.getResponseHeader([name])/xhr.getAllResponseHeaders() 獲取回應頭資訊;

服務器端:獲取請求頭,設定回應頭

->在NODE中我們可以使用 response.writeHead(200, {'content-type': 'application/json'}); 設定回應頭資訊

->在NODE中我們可以使用 request 這個物件獲取到請求資訊(起始行、首部、主體) 中的都可以獲取到

多路復用與多路分解?

答:將傳輸層報文段中的資料交付到正確的套接字的作業被稱為多路分解,

在源主機上從不同的套接字中收集資料,封裝頭資訊生成報文段后,將報文段傳遞到網路層,這個程序被稱為多路復用,

無連接的多路復用和多路分解指的是 UDP 套接字的分配程序,一個 UDP 套接字由一個二元組來標識,這個二元組包含了一 個目的地址和一個目的埠號,因此不同源地址和埠號的 UDP 報文段到達主機后,如果它們擁有相同的目的地址和目的端 口號,那么不同的報文段將會轉交到同一個 UDP 套接字中,

面向連接的多路復用和多路分解指的是 TCP 套接字的分配程序,一個 TCP 套接字由一個四元組來標識,這個四元組包含了 源 IP 地址、源埠號、目的地址和目的埠號,因此,一個 TCP 報文段從網路中到達一臺主機上時,該主機使用全部 4 個 值來將報文段定向到相應的套接字,

UDP 協議?

答:UDP 是一種無連接的,不可靠的傳輸層協議,它只提供了傳輸層需要實作的最低限度的功能,除了復用/分解功能和少量的差 錯檢測外,它幾乎沒有對 IP 增加其他的東西,UDP 協議適用于對實時性要求高的應用場景,

特點:

  1. 使用 UDP 時,在發送報文段之前,通信雙方沒有握手的程序,因此 UDP 被稱為是無連接的傳輸層協議,因為沒有握手 程序,相對于 TCP 來說,沒有建立連接的時延,因為沒有連接,所以不需要在端系統中保存連接的狀態,

  2. UDP 提供盡力而為的交付服務,也就是說 UDP 協議不保證資料的可靠交付,

  3. UDP 沒有擁塞控制和流量控制的機制,所以 UDP 報文段的發送速率沒有限制,

  4. 因為一個 UDP 套接字只使用目的地址和目的埠來標識,所以 UDP 可以支持一對一、一對多、多對一和多對多的互動 通信,

  5. UDP 首部小,只有 8 個位元組,

UDP 報文段結構

UDP 報文段由首部和應用資料組成,報文段首部包含四個欄位,分別是源埠號、目的埠號、長度和檢驗和,每個欄位的長 度為兩個位元組,長度欄位指的是整個報文段的長度,包含了首部和應用資料的大小,校驗和是 UDP 提供的一種差錯校驗機制, 雖然提供了差錯校驗的機制,但是 UDP 對于差錯的恢復無能為力,

UDP 報文段結構

TCP 協議?

答:TCP 協議是面向連接的,提供可靠資料傳輸服務的傳輸層協議,

特點:

  1. TCP 協議是面向連接的,在通信雙方進行通信前,需要通過三次握手建立連接,它需要在端系統中維護雙方連接的狀態資訊,

  2. TCP 協議通過序號、確認號、定時重傳、檢驗和等機制,來提供可靠的資料傳輸服務,

  3. TCP 協議提供的是點對點的服務,即它是在單個發送方和單個接收方之間的連接,

  4. TCP 協議提供的是全雙工的服務,也就是說連接的雙方的能夠向對方發送和接收資料,

  5. TCP 提供了擁塞控制機制,在網路擁塞的時候會控制發送資料的速率,有助于減少資料包的丟失和減輕網路中的擁塞程度,

  6. TCP 提供了流量控制機制,保證了通信雙方的發送和接收速率相同,如果接收方可接收的快取很小時,發送方會降低發送 速率,避免因為快取填滿而造成的資料包的丟失,

TCP 報文段結構

TCP 報文段由首部和資料組成,它的首部一般為 20 個位元組,

源埠和目的埠號用于報文段的多路復用和分解,

32 位元的序號和 32 位元的確認號,用與實作可靠資料運輸服務,

16 位元的接收視窗欄位用于實作流量控制,該欄位表示接收方愿意接收的位元組的數量,

4 位元的首部長度欄位,該欄位指示了以 32 位元的字為單位的 TCP 首部的長度,

6 位元的標志欄位,ACK 欄位用于指示確認序號的值是有效的,RST、SYN 和 FIN 位元用于連接建立和拆除,設定 PSH 字 段指示接收方應該立即將資料交給上層,URG 欄位用來指示報文段里存在緊急的資料,

校驗和提供了對資料的差錯檢測,

TCP 報文段結構

TCP 三次握手的程序

第一次握手,客戶端向服務器發送一個 SYN 連接請求報文段,報文段的首部中 SYN 標志位置為 1,序號欄位是一個任選的 亂數,它代表的是客戶端資料的初始序號,

第二次握手,服務器端接收到客戶端發送的 SYN 連接請求報文段后,服務器首先會為該連接分配 TCP 快取和變數,然后向 客戶端發送 SYN ACK 報文段,報文段的首部中 SYN 和 ACK 標志位都被置為 1,代表這是一個對 SYN 連接請求的確認, 同時序號欄位是服務器端產生的一個任選的亂數,它代表的是服務器端資料的初始序號,確認號欄位為客戶端發送的序號加 一,

第三次握手,客戶端接收到服務器的肯定應答后,它也會為這次 TCP 連接分配快取和變數,同時向服務器端發送一個對服務 器端的報文段的確認,第三次握手可以在報文段中攜帶資料,

在我看來,TCP 三次握手的建立連接的程序就是相互確認初始序號的程序,告訴對方,什么樣序號的報文段能夠被正確接收, 第三次握手的作用是客戶端對服務器端的初始序號的確認,如果只使用兩次握手,那么服務器就沒有辦法知道自己的序號是否 已被確認,同時這樣也是為了防止失效的請求報文段被服務器接收,而出現錯誤的情況,

TCP 四次揮手的程序

因為 TCP 連接是全雙工的,也就是說通信的雙方都可以向對方發送和接收訊息,所以斷開連接需要雙方的確認,

第一次揮手,客戶端認為沒有資料要再發送給服務器端,它就向服務器發送一個 FIN 報文段,申請斷開客戶端到服務器端的 連接,發送后客戶端進入 FIN_WAIT_1 狀態,

第二次揮手,服務器端接收到客戶端釋放連接的請求后,向客戶端發送一個確認報文段,表示已經接收到了客戶端釋放連接的 請求,以后不再接收客戶端發送過來的資料,但是因為連接是全雙工的,所以此時,服務器端還可以向客戶端發送資料,服務 器端進入 CLOSE_WAIT 狀態,客戶端收到確認后,進入 FIN_WAIT_2 狀態,

第三次揮手,服務器端發送完所有資料后,向客戶端發送 FIN 報文段,申請斷開服務器端到客戶端的連接,發送后進入 LAS T_ACK 狀態,

第四次揮手,客戶端接收到 FIN 請求后,向服務器端發送一個確認應答,并進入 TIME_WAIT 階段,該階段會持續一段時間, 這個時間為報文段在網路中的最大生存時間,如果該時間內服務端沒有重發請求的話,客戶端進入 CLOSED 的狀態,如果收到 服務器的重發請求就重新發送確認報文段,服務器端收到客戶端的確認報文段后就進入 CLOSED 狀態,這樣全雙工的連接就被 釋放了,

TCP 使用四次揮手的原因是因為 TCP 的連接是全雙工的,所以需要雙方分別釋放到對方的連接,單獨一方的連接釋放,只代 表不能再向對方發送資料,連接處于的是半釋放的狀態,

最后一次揮手中,客戶端會等待一段時間再關閉的原因,是為了防止發送給服務器的確認報文段丟失或者出錯,從而導致服務器 端不能正常關閉,

狀態轉化圖

客戶端狀態圖

服務端狀態圖

ARQ 協議

ARQ 協議指的是自動重傳請求,它通過超時和重傳來保證資料的可靠交付,它是 TCP 協議實作可靠資料傳輸的一個很重要的 機制,

它分為停止等待 ARQ 協議和連續 ARQ 協議,

一、停止等待 ARQ 協議

停止等待 ARQ 協議的基本原理是,對于發送方來說發送方每發送一個分組,就為這個分組設定一個定時器,當發送分組的確認 回答回傳了,則清除定時器,發送下一個分組,如果在規定的時間內沒有收到已發送分組的肯定回答,則重新發送上一個分組,

對于接受方來說,每次接受到一個分組,就回傳對這個分組的肯定應答,當收到冗余的分組時,就直接丟棄,并回傳一個對冗余 分組的確認,當收到分組損壞的情況的時候,直接丟棄,

使用停止等待 ARQ 協議的缺點是每次發送分組必須等到分組確認后才能發送下一個分組,這樣會造成信道的利用率過低,

二、連續 ARQ 協議

連續 ARQ 協議是為了解決停止等待 ARQ 協議對于信道的利用率過低的問題,它通過連續發送一組分組,然后再等待對分組的 確認回答,對于如何處理分組中可能出現的差錯恢復情況,一般可以使用滑動視窗協議和選擇重傳協議來實作,

  1. 滑動視窗協議

使用滑動視窗協議,在發送方維持了一個發送視窗,發送視窗以前的分組是已經發送并確認了的分組,發送視窗中包含了已經發 送但未確認的分組和允許發送但還未發送的分組,發送視窗以后的分組是快取中還不允許發送的分組,當發送方向接收方發送分 組時,會依次發送視窗內的所有分組,并且設定一個定時器,這個定時器可以理解為是最早發送但未收到確認的分組,如果在定 時器的時間內收到某一個分組的確認回答,則滑動視窗,將視窗的首部移動到確認分組的后一個位置,此時如果還有已發送但沒 有確認的分組,則重新設定定時器,如果沒有了則關閉定時器,如果定時器超時,則重新發送所有已經發送但還未收到確認的分 組,

接收方使用的是累計確認的機制,對于所有按序到達的分組,接收方回傳一個分組的肯定回答,如果收到了一個亂序的分組,那 么接方會直接丟棄,并回傳一個最近的按序到達的分組的肯定回答,使用累計確認保證了確認號以前的分組都已經按序到達了, 所以發送視窗可以移動到已確認分組的后面,

滑動視窗協議的缺點是因為使用了累計確認的機制,如果出現了只是視窗中的第一個分組丟失,而后面的分組都按序到達的情況 的話,那么滑動視窗協議會重新發送所有的分組,這樣就造成了大量不必要分組的丟棄和重傳,

  1. 選擇重傳協議

因為滑動視窗使用累計確認的方式,所以會造成很多不必要分組的重傳,使用選擇重傳協議可以解決這個問題,

選擇重傳協議在發送方維護了一個發送視窗,發送視窗的以前是已經發送并確認的分組,視窗內包含了已發送但未被確認的分組, 已確認的亂序分組,和允許發送但還未發送的分組,發送視窗以后的是快取中還不允許發送的分組,選擇重傳協議與滑動視窗協 議最大的不同是,發送方發送分組時,為一個分組都創建了一個定時器,當發送方接受到一個分組的確認應答后,取消該分組的 定時器,并判斷接受該分組后,是否存在由視窗首部為首的連續的確認分組,如果有則向后移動視窗的位置,如果沒有則將該分 組標識為已接收的亂序分組,當某一個分組定時器到時后,則重新傳遞這個分組,

在接收方,它會確認每一個正確接收的分組,不管這個分組是按序的還是亂序的,亂序的分組將被快取下來,直到所有的亂序分 組都到達形成一個有序序列后,再將這一段分組交付給上層,對于不能被正確接收的分組,接收方直接忽略該分組,

TCP 的可靠運輸機制

TCP 的可靠運輸機制是基于連續 ARQ 協議和滑動視窗協議的,

TCP 協議在發送方維持了一個發送視窗,發送視窗以前的報文段是已經發送并確認了的報文段,發送視窗中包含了已經發送但 未確認的報文段和允許發送但還未發送的報文段,發送視窗以后的報文段是快取中還不允許發送的報文段,當發送方向接收方發 送報文時,會依次發送視窗內的所有報文段,并且設定一個定時器,這個定時器可以理解為是最早發送但未收到確認的報文段, 如果在定時器的時間內收到某一個報文段的確認回答,則滑動視窗,將視窗的首部向后滑動到確認報文段的后一個位置,此時如 果還有已發送但沒有確認的報文段,則重新設定定時器,如果沒有了則關閉定時器,如果定時器超時,則重新發送所有已經發送 但還未收到確認的報文段,并將超時的間隔設定為以前的兩倍,當發送方收到接收方的三個冗余的確認應答后,這是一種指示, 說明該報文段以后的報文段很有可能發生丟失了,那么發送方會啟用快速重傳的機制,就是當前定時器結束前,發送所有的已發 送但確認的報文段,

接收方使用的是累計確認的機制,對于所有按序到達的報文段,接收方回傳一個報文段的肯定回答,如果收到了一個亂序的報文 段,那么接方會直接丟棄,并回傳一個最近的按序到達的報文段的肯定回答,使用累計確認保證了回傳的確認號之前的報文段都 已經按序到達了,所以發送視窗可以移動到已確認報文段的后面,

發送視窗的大小是變化的,它是由接收視窗剩余大小和網路中擁塞程度來決定的,TCP 就是通過控制發送視窗的長度來控制報文 段的發送速率,

但是 TCP 協議并不完全和滑動視窗協議相同,因為許多的 TCP 實作會將失序的報文段給快取起來,并且發生重傳時,只會重 傳一個報文段,因此 TCP 協議的可靠傳輸機制更像是視窗滑動協議和選擇重傳協議的一個混合體,

TCP 的流量控制機制

TCP 提供了流量控制的服務,這個服務的主要目的是控制發送方的發送速率,保證接收方來得及接收,因為一旦發送的速率大 于接收方所能接收的速率,就會造成報文段的丟失,接收方主要是通過接收視窗來告訴發送方自己所能接收的大小,發送方根據 接收方的接收視窗的大小來調整發送視窗的大小,以此來達到控制發送速率的目的,

TCP 的擁塞控制機制

TCP 的擁塞控制主要是根據網路中的擁塞情況來控制發送方資料的發送速率,如果網路處于擁塞的狀態,發送方就減小發送的 速率,這樣一方面是為了避免繼續增加網路中的擁塞程度,另一方面也是為了避免網路擁塞可能造成的報文段丟失,

TCP 的擁塞控制主要使用了四個機制,分別是慢啟動、擁塞避免、快速重傳和快速恢復,

慢啟動的基本思想是,因為在發送方剛開始發送資料的時候,并不知道網路中的擁塞程度,所以先以較低的速率發送,進行試探 ,每次收到一個確認報文,就將發動視窗的長度加一,這樣每個 RTT 時間后,發送視窗的長度就會加倍,當發送視窗的大小達 到一個閾值的時候就進入擁塞避免演算法,

擁塞避免演算法是為了避免可能發生的擁塞,將發送視窗的大小由每過一個 RTT 增長一倍,變為每過一個 RTT ,長度只加一, 這樣將視窗的增長速率由指數增長,變為加法線性增長,

快速重傳指的是,當發送方收到三個冗余的確認應答時,因為 TCP 使用的是累計確認的機制,所以很有可能是發生了報文段的 丟失,因此采用立即重傳的機制,在定時器結束前發送所有已發送但還未接收到確認應答的報文段,

快速恢復是對快速重傳的后續處理,因為網路中可能已經出現了擁塞情況,所以會將慢啟動的閥值減小為原來的一半,然后將擁 塞視窗的值置為減半后的閥值,然后開始執行擁塞避免演算法,使得擁塞視窗緩慢地加性增大,簡單來理解就是,乘性減,加性增,

TCP 認為網路擁塞的主要依據是報文段的重傳次數,它會根據網路中的擁塞程度,通過調整慢啟動的閥值,然后交替使用上面四 種機制來達到擁塞控制的目的,

Post 和 Get 的區別?

答:Post 和 Get 是 HTTP 請求的兩種方法,

  • (1)從應用場景上來說,GET 請求是一個冪等的請求,一般 Get 請求用于對服務器資源不會產生影響的場景,比如說請求一個網頁,而 Post 不是一個冪等的請求,一般用于對服務器資源會產生影響的情景,比如注冊用戶這一類的操作,

  • (2)因為不同的應用場景,所以瀏覽器一般會對 Get 請求快取,但很少對 Post 請求快取,

  • (3)從發送的報文格式來說,Get 請求的報文中物體部分為空,Post 請求的報文中物體部分一般為向服務器發送的資料,

  • (4)但是 Get 請求也可以將請求的引數放入 url 中向服務器發送,

    • 這樣的做法相對于 Post 請求來說,一個方面是不太安全,因為請求的 url 會被保留在歷史記錄中,并且瀏覽器由于對 url 有一個長度上的限制,所以會影響 get 請求發送資料時的長度,這個限制是瀏覽器規定的,并不是 RFC 規定的,還有就是 post 的引數傳遞支持更多的資料型別,

DNS 為什么使用 UDP 協議作為傳輸層協議?

答:DNS 使用 UDP 協議作為傳輸層協議的主要原因是為了避免使用 TCP 協議時造成的連接時延,

因為為了得到一個域名的 IP 地址,往往會向多個域名服務器查詢,如果使用 TCP 協議,那么每次請求都會存在連接時延,這樣使 DNS 服務變得很慢,因為大多數的地址查詢請求,都是瀏覽器請求頁面時發出的,這樣會造成網頁的等待時間過長,

使用 UDP 協議作為 DNS 協議會有一個問題,由于歷史原因,物理鏈路的最小MTU = 576,所以為了限制報文長度不超過576,UDP 的報文段的長度被限制在 512 個位元組以內,這樣一旦 DNS 的查詢或者應答報文,超過了 512 位元組,那么基于 UDP 的DNS 協議就會被截斷為 512 位元組,那么有可能用戶得到的 DNS 應答就是不完整的,這里 DNS 報文的長度一旦超過限制,并不會像 TCP 協議那樣被拆分成多個報文段傳輸,因為 UDP 協議不會維護連接狀態,所以我們沒有辦法確定那幾個報文段屬于同一個資料,UDP 只會將多余的資料給截取掉,

為了解決這個問題,我們可以使用 TCP 協議去請求報文,

DNS 還存在的一個問題是安全問題,就是我們沒有辦法確定我們得到的應答,一定是一個安全的應答,因為應答可以被他人偽造,所以現在有了 DNS over HTTPS 來解決這個問題,

談談 CDN 服務?

答:CDN 是一個內容分發網路,通過對源網站資源的快取,利用本身多臺位于不同地域、不同運營商的服務器,向用戶提供資就近訪問的 功能,也就是說,用戶的請求并不是直接發送給源網站,而是發送給 CDN 服務器,由 CND 服務器將請求定位到最近的含有該資源 的服務器上去請求,這樣有利于提高網站的訪問速度,同時通過這種方式也減輕了源服務器的訪問壓力,

什么是正向代理和反向代理?

答:我們常說的代理也就是指正向代理,正向代理的程序,它隱藏了真實的請求客戶端,服務端不知道真實的客戶端是誰,客戶端請求的 服務都被代理服務器代替來請求,

反向代理隱藏了真實的服務端,當我們請求一個網站的時候,背后可能有成千上萬臺服務器為我們服務,但具體是哪一臺,我們不知 道,也不需要知道,我們只需要知道反向代理服務器是誰就好了,反向代理服務器會幫我們把請求轉發到真實的服務器那里去,反向 代理器一般用來實作負載平衡,

負載平衡的兩種實作方式?

答:一種是使用反向代理的方式,用戶的請求都發送到反向代理服務上,然后由反向代理服務器來轉發請求到真實的服務器上,以此來實 現集群的負載平衡,

另一種是 DNS 的方式,DNS 可以用于在冗余的服務器上實作負載平衡,因為現在一般的大型網站使用多臺服務器提供服務,因此一 個域名可能會對應多個服務器地址,當用戶向網站域名請求的時候,DNS 服務器回傳這個域名所對應的服務器 IP 地址的集合,但在 每個回答中,會回圈這些 IP 地址的順序,用戶一般會選擇排在前面的地址發送請求,以此將用戶的請求均衡的分配到各個不同的服 務器上,這樣來實作負載均衡,這種方式有一個缺點就是,由于 DNS 服務器中存在快取,所以有可能一個服務器出現故障后,域名解 析仍然回傳的是那個 IP 地址,就會造成訪問的問題,

即時通訊的實作,短輪詢、長輪詢、SSE 和 WebSocket 間的區別?

答:短輪詢和長輪詢的目的都是用于實作客戶端和服務器端的一個即時通訊,

短輪詢的基本思路就是瀏覽器每隔一段時間向瀏覽器發送 http 請求,服務器端在收到請求后,不論是否有資料更新,都直接進行 回應,這種方式實作的即時通信,本質上還是瀏覽器發送請求,服務器接受請求的一個程序,通過讓客戶端不斷的進行請求,使得客 戶端能夠模擬實時地收到服務器端的資料的變化,這種方式的優點是比較簡單,易于理解,缺點是這種方式由于需要不斷的建立 ht tp 連接,嚴重浪費了服務器端和客戶端的資源,當用戶增加時,服務器端的壓力就會變大,這是很不合理的,

長輪詢的基本思路是,首先由客戶端向服務器發起請求,當服務器收到客戶端發來的請求后,服務器端不會直接進行回應,而是先將 這個請求掛起,然后判斷服務器端資料是否有更新,如果有更新,則進行回應,如果一直沒有資料,則到達一定的時間限制才回傳, 客戶端 JavaScript 回應處理函式會在處理完服務器回傳的資訊后,再次發出請求,重新建立連接,長輪詢和短輪詢比起來,它的 優點是明顯減少了很多不必要的 http 請求次數,相比之下節約了資源,長輪詢的缺點在于,連接掛起也會導致資源的浪費,

SSE 的基本思想是,服務器使用流資訊向服務器推送資訊,嚴格地說,http 協議無法做到服務器主動推送資訊,但是,有一種變通 方法,就是服務器向客戶端宣告,接下來要發送的是流資訊,也就是說,發送的不是一次性的資料包,而是一個資料流,會連續不斷 地發送過來,這時,客戶端不會關閉連接,會一直等著服務器發過來的新的資料流,視頻播放就是這樣的例子,SSE 就是利用這種機 制,使用流資訊向瀏覽器推送資訊,它基于 http 協議,目前除了 IE/Edge,其他瀏覽器都支持,它相對于前面兩種方式來說,不 需要建立過多的 http 請求,相比之下節約了資源,

上面三種方式本質上都是基于 http 協議的,我們還可以使用 WebSocket 協議來實作,WebSocket 是 Html5 定義的一個新協 議,與傳統的 http 協議不同,該協議允許由服務器主動的向客戶端推送資訊,使用 WebSocket 協議的缺點是在服務器端的配置 比較復雜,WebSocket 是一個全雙工的協議,也就是通信雙方是平等的,可以相互發送訊息,而 SSE 的方式是單向通信的,只能 由服務器端向客戶端推送資訊,如果客戶端需要發送資訊就是屬于下一個 http 請求了,

實作一個頁面操作不會整頁重繪的網站,并且能在瀏覽器前進、后退時正確回應,給出你的技術實作方案?

答:相較于不同頁面的跳轉,AJAX可以說大大提高了用戶的瀏覽體驗,不用看到頁面切換之間的白屏是件很愜意的事情,但是很多早先的AJAX應用是不支持瀏覽器的前進后退的,這導致了用戶不管在網站里瀏覽到何處,一旦重繪就會立刻回到起初的位置,并且用戶也無法通過瀏覽器的前進后退按鈕來實作瀏覽歷史的切換,

對于第一個問題,解決還算容易,只要用cookie或者localStorage來記錄應用的狀態即可,重繪頁面時讀取一下這個狀態,然后發送相應ajax請求來改變頁面即可,但是第二個問題就很麻煩了,先說下現代瀏覽器的解決方案,

jQuery 面試題

jquery 特點,聊一聊 jquery?

答:

  • jQuery 是一款輕量級的 js 框架,jQuery 核心 js 檔案才幾十 kb,不會影響頁面加載速度,與 Extjs 相比要輕便的多,
  • jQuery 的選擇器用起來很方便,好比說我要找到某個 dom 物件的相鄰元素 js 可能要寫好幾行代碼,而 jQuery 一行代碼就搞定了,再比如我要將一個表格的隔行變色,jQuery 也是一行代碼搞定,
  • jQuery 的鏈式操作可以把多個操作寫在一行代碼里,
  • jQuery 還簡化了 js 操作 css 的代碼,并且代碼的可讀性也比 js 要強,
  • jQuery 簡化了 AJAX 操作,后臺只需回傳一個 JSON 格式的字串就能完成與前臺的通信,
  • jQuery 基本兼容了現在主流的瀏覽器,不用再為瀏覽器的兼容問題而傷透腦筋,
  • jQuery 有著豐富的第三方的插件,例如:樹形選單、日期控制元件、圖片切換插件、彈出視窗等等基本前臺頁面上的組件都有對應插件,并且用 jQuery 插件做出來的效果很炫,并且可以根據自己需要去改寫和封裝插件,簡單實用,
  • jQuery 可擴展性強,jQuery 提供了擴展介面:jQuery.extend(object), 可以在 jQuery 的命名空間上增加新函式,jQuery 的所有插件都是基于這個擴展介面開發的,

jquery 物件和 dom 物件怎么轉換的?

答:

  • [0] 可以轉化為 DOM 物件
  • $(domObject) 可以轉化為 jQuery 物件

jquery 中如何將陣列轉化為 json 字串,然后再轉化回來?

答:

    $.fn.stringifyArray = function(array) {
    	return JSON.stringify(array)
    }
    $.fn.parseArray = function(array) {
    	return JSON.parse(array)
    }

然后呼叫:

    $("").stringifyArray(array)

jQuery 的原始碼看過嗎?能不能簡單概況一下它的實作原理?

答:

1、閉包機制;

// 以下截取自 jquery 原始碼片段
(function( window, undefined ) {
/* 原始碼內容 */
})( window );

上面這一小段代碼來自于 1.9.0 當中 jquery 的原始碼,它是一個無污染的 JS 插件的標準寫法,專業名詞叫閉包,可以把它簡單的看做是一個函式,與普通函式不同的是,這個函式沒有名字,而且會立即執行

我們將里面的變數變成了局域變數,這不僅可以提高運行速度,更重要的是我們在參考 jquery 的 JS 檔案時,不會因為 jquery 當中的變數太多,而與其它的 JS 框架的變數命名產生沖突;閉包中的變數宣告沒有污染到外面的全域變數;;

2、作為全域的一個屬性;

window.jQuery = window.$ = jQuery;

這一句話將我們在閉包當中定義的 jQuery 物件匯出為全域變數 jQuery 和 $,因此我們才可以在外部直接使用 jQuery 和 $,

window 是默認的 JS 背景關系環境,因此將物件系結到 window 上面,就相當于變成了傳統意義上的全域變數,

3、最核心的功能,就是選擇器;

首先我們進入 jquery 原始碼中,可以很容易的找到 jquery 物件的宣告,看過以后會發現,原來我們的 jquery 物件就是 init 物件,


    jQuery = function( selector, context ) {
    return new jQuery.fn.init( selector, context, rootjQuery );
    }

這里出現了 jQuery.fn 這樣一個東西,它的由來可以在 jquery 的原始碼中找到,它其實代表的就是 jQuery 物件的原型,


    jQuery.fn = jQuery.prototype;
    jQuery.fn.init.prototype = jQuery.fn;

這兩句話,第一句把 jQuery 物件的原型賦給了 fn 屬性,第二句把 jQuery 物件的原型又賦給了 init 物件的原型,也就是說,init 物件和 jQuery 具有相同的原型,因此我們在上面回傳的 init 物件,就與 jQuery 物件有一樣的屬性和方法,

我們不打算深究 init 這個方法的邏輯以及實作,但是我們需要知道的是,jQuery 其實就是將 DOM 物件加了一層包裹,而尋找某個或者若干個 DOM 物件是由 sizzle 選擇器負責的,

jQuery 物件有很多的屬性和方法;對于屬性來說,我們最需要關注的只有一個屬性,就是 [0] 屬性,[0] 其實就是原生的 DOM 物件,

很多時候,我們在 jQuery 和 DOM 物件之間切換時需要用到 [0] 這個屬性,

從截圖也可以看出,jQuery 物件其實主要就是把原生的 DOM 物件存在了 [0] 的位置,并給它加了一系列簡便的方法,

這個索引 0 的屬性我們可以從一小段代碼簡單的看一下它的由來,下面是 init 方法中的一小段對 DOMElement 物件作為選擇器的原始碼,


    // Handle $(DOMElement)
    if ( selector.nodeType ) {
    /* 可以看到,這里將 DOM 物件賦給了 jQuery 物件的 [0] 這個位置 */
    this.context = this[0] = selector;
    this.length = 1;
    return this;
    }

這一小段代碼可以在 jquery 原始碼中找到,它是處理傳入的選擇引數是一個 DOM 物件的情況,

可以看到,里面很明顯的將 jQuery 物件索引 0 的位置以及 context 屬性,都賦予了 DOM 物件

,代碼不僅說明了這一點,也同時說明了,我們使用 $(DOMElement) 可以將一個 DOM 物件轉換為 jQuery 物件,從而通過轉換獲得 jQuery 物件的簡便方法,

4、ready 方法

$(function(){}) 或者是 ready 方法;

實作類似 jquery 的 ready 方法的效果我們是可以簡單做到的,它的實作原理就是,維護一個函式陣列,然后不停的判斷 DOM 是否加載完畢,倘若加載完畢就觸發所有陣列中的函式,

遵循著這一思想,LZ 拿出很久之前寫的一個小例子,來給各位看一下,

(function( window, undefined ) {
var jQuery = {
isReady:false,// 檔案加載是否完成的標識
readyList:[],// 函式序列
//onload 事件實作
ready : function(fn){
// 如果是函式,加入到函式序列
if(fn && typeof fn == 'function' ){
jQuery.readyList.push(fn);
}
// 檔案加載完成,執行函式序列,
if(jQuery.isReady){
for(var i = 0;i < jQuery.readyList.length ;i++){
fn = jQuery.readyList[i];
jQuery.callback(fn);
}
return jQuery;
}
},
// 回呼
callback : function(fn){
fn.call(document,jQuery);
}
};
// 匯出物件
window.$ = window.jQuery = jQuery;
// 判斷加載是否完成
var top = false;
try {
top = window.frameElement == null && document.documentElement;
} catch(e) {}
if ( top && top.doScroll ) {
(function doScrollCheck() {
try {
top.doScroll("left");
jQuery.isReady = true;
jQuery.ready();
} catch(e) {
setTimeout( doScrollCheck, 50 );
}
})();
}
}(window));

這段代碼是 LZ 從之前的例子摘出來的,它的實作邏輯非常簡單,但是可以達到 jQuery 的 ready 方法的效果,

5、extend 方法

簡單說兩個 extend 方法的常用方式,

  • 1、使用 jQuery.fn.extend 可以擴展 jQuery 物件,使用 jQuery.extend 可以擴展 jQuery,前者類似于給類添加普通方法,后者類似于給類添加靜態方法,
  • 2、兩個 extend 方法如果有兩個 object 型別的引數,則會將后面的引數物件屬性擴展到第一個引數物件上面,擴展時可以再添加一個 boolean 引數控制是否深度拷貝,

jquery find,children,filter 的區別?

答:

  • filter 是對自身集合元素的操作,
  • children 是對子元素的檢索,
  • find 是對它的后代元素的檢索操作

.children(selector) 方法是回傳匹配元素集合中每個元素的所有子元素(僅兒子輩),引數可選,添加引數表示通過選擇器進行過濾,對元素進行篩選,

.find(selector) 方法是回傳匹配元素集合中每個元素的后代,引數是必選的,可以為選擇器、jquery 物件可元素來對元素進行篩選,

.find() 與 .children() 方法類似,不同的是后者僅沿著 DOM 樹向下遍歷單一層級,這里的 children,我理解為兒子,只在兒子這一級遍歷,

jQuery 的佇列是如何實作的?佇列可以用在哪些地方?

答:jQuery 核心中,有一組佇列控制方法,這組方法由 queue()/dequeue()/clearQueue() 三個方法組成,它對需要連續按序執行的函式的控制可以說是簡明自如,主要應用于 animate () 方法,ajax 以及其他要按時間順序執行的事件中,

先解釋一下這組方法各自的含義,

queue(name,[callback]): 當只傳入一個引數時,它回傳并指向第一個匹配元素的佇列(將是一個函式陣列,佇列名默認是 fx); 當有兩個引數傳入時,第一個引數還是默認為 fx 的的佇列名,第二個引數又分兩種情況,當第二個引數是一個函式時,它將在匹配的元素的佇列最后添加一個函式,當第二個引數是一個函式陣列時,它將匹配元素的佇列用新的一個佇列來代替(函式陣列). 可能,這個理解起來有點暈,

dequeue(name): 這個好理解,就是從佇列最前端移除一個佇列函式,并執行它,

clearQueue([queueName]): 這是 1.4 新增的方法,清空物件上尚未執行的所有佇列,引數可選,默認為 fx. 但個人覺得這個方法沒多大用,用 queue() 方法傳入兩個引數的第二種引數即可實作 clearQueue 方法,

現在,我們要實作這樣一個效果,有標有 1 至 7 的數字方塊,要求這七個方塊自左到右依次下落;寫好以后

;如果此時,你想調換一個某個的執行順序,比如,你想讓 5 落下后再開始下落 3, 或者新加 8 至 15 八個方塊,怎么辦?

重寫嗎?在里面小心冀冀的改嗎?顯然,我們需要另外一種列簡明便捷的方法來實作這個效果,那就是 jQuery 佇列控制方法,;

這樣一來,看起來是不是簡明多了,如何實作?

  1. 新建一個陣列,把影片函式依次放進去(這樣更改順序,新加影片是不是方便多了?);
  2. 用 queue 將這組影片函式陣列加入到 slideList 佇列中;
  3. 用 dequeue 取出 slideList 佇列中第一個函式,并執行它;
  4. 初始執行第一個函式,

至于 clearQueue() 方法,就不多說了,演示中停止按鈕呼叫的就是 clearQueue() 方法,當然你還可以用 queue() 方法直接將現在的函式列隊替換成 [] 空陣列實作(個人比較推薦空陣列替換,, 更直觀).

jQuery 與 jQuery UI 有啥區別?

答:

  • jQuery 是一個 js 庫,主要提供的功能是選擇器,屬性修改和事件系結等等,
  • jQuery UI 則是在 jQuery 的基礎上,利用 jQuery 的擴展性,設計的插件,
    • 提供了一些常用的界面元素,諸如對話框、拖動行為、改變大小行為等等

jQuery 和 Zepto 的區別?各自的使用場景?

答:zepto 主要用在移動設備上,只支持較新的瀏覽器,好處是代碼量比較小,性能也較好,

jquery 主要是兼容性好,可以跑在各種 pc,移動上,好處是兼容各種瀏覽器,缺點是代碼量大,同時考慮兼容,性能也不夠好,

jq 自身也注意到了這個總是,所有它的 2.x 版本是不支持 ie6 7 8 的

針對 jQuery 性能的優化方法?

答:

  • 基于 Class 的選擇性的性能相對于 Id 選擇器開銷很大,因為需遍歷所有 DOM 元素,
  • 頻繁操作的 DOM,先快取起來再操作,用 jQuery 的鏈式呼叫更好,
    • 比如:var str=$("a").attr("href");
  • for 回圈

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

for 回圈每一次回圈都查找了陣列 (arr) 的 length 屬性,在開始回圈的時候設定一個變數來存盤這個數字,可以讓回圈跑得更快:


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

    }

jQuery 一個物件可以同時系結多個事件,這是如何實作的?

答:


    $ele.on('eventName', handle1);
    $ele.on('eventName', handle2);
    $ele.on('eventName', handle3);

其實 $ele 元素的 eventName 事件有一個處理函式陣列 監聽一次就往里面放一個 handle,陣列是先進后出型的

也就是堆疊, 然后觸發事件的時候一次執行

上面的監聽相當于


    $ele.eventHandle['eventName'] = [];
    $ele.eventHandle['eventName'].push(handle1);
    $ele.eventHandle['eventName'].push(handle2);
    $ele.eventHandle['eventName'].push(handle3);

然后 $ele.trigger('eventName') 觸發的時候, 從堆疊里面取出處理函式執行


    while($ele.eventHandle['eventName'].length) {
    handle = $ele.eventHandle['eventName'].pop();
    handle();
    }

最先監聽的最后執行;

Vue.js 面試題

Vue.js 路由的鉤?函式?

答:首頁可以控制導航跳轉, beforeEach , afterEach 等,?般?于頁? title 的修改,?些需要登錄才能調整??的重定向功能,

  • beforeEach 主要有3個引數 to , from , next ,
  • to : route 即將進?的?標路由物件,
  • from : route 當前導航正要離開的路由,
  • next : function ?定要調?該?法 resolve 這個鉤?,執?效果依賴next ?法的調?引數,可以控制網頁的跳轉

vuex是什么?怎么使??哪種功能場景使?它?

答:

  • 只?來讀取的狀態集中放在 store 中; 改變狀態的?式是提交 mutations ,這是個同步的事物; 異步邏輯應該封裝在 action 中,
  • 在 main.js 引? store ,注?,新建了?個?錄 store , … export
  • 場景有:單?應?中,組件之間的狀態、?樂播放、登錄狀態、加?購物?
  • state : Vuex 使?單?狀態樹,即每個應?將僅僅包含?個 store 實體,但單?狀態
  • 樹和模塊化并不沖突,存放的資料狀態,不可以直接修改??的資料,
  • mutations : mutations 定義的?法動態修改 Vuex 的 store 中的狀態或資料
  • getters :類似 vue 的計算屬性,主要?來過濾?些資料,
  • action : actions 可以理解為通過將 mutations ??處?資料的?法變成可異步的處理資料的?法,簡單的說就是異步操作資料, view 層通過 store.dispath 來分發 action
  • modules :項?特別復雜的時候,可以讓每?個模塊擁有??的 state 、mutation 、 action 、 getters ,使得結構?常清晰,?便管理

Vue.js 如何讓CSS只在當前組件中起作??

答:將當前組件的 <style> 修改為 <style scoped>

Vue.js 指令v-el的作?是什么?

答:提供?個在頁?上已存在的 DOM 元素作為 Vue 實體的掛載?標.可以是 CSS 選擇器,也可以是?個 HTMLElement 實體,

在 Vue.js 中使?插件的步驟?

答:

  • 采? ES6 的 import … from … 語法或 CommonJS 的 require() ?法引?插件
  • 使?全域?法 Vue.use( plugin ) 使?插件,可以傳??個選項物件 Vue.use(MyPlugin, { someOption: true })

Vue.js 路由之間跳轉?

答:宣告式(標簽跳轉)

<router-link :to="index">

編程式( js跳轉)


    router.push('index')

Vue.js 組件中 data 什么時候可以使?物件?

答:組件復?時所有組件實體都會共享 data ,如果 data 是物件的話,就會造成?個組件 修改 data 以后會影響到其他所有組件,所以需要將 data 寫成函式,每次?到就調? ?次函式獲得新的資料,當我們使? new Vue() 的?式的時候,?論我們將 data 設定為物件還是函式都是可 以的,因為 new Vue() 的?式是?成?個根組件,該組件不會復?,也就不存在共享 data 的情況了

Vue-Router 中導航守衛有哪些?

答:完整的導航決議流程

  1. 導航被觸發,
  2. 在失活的組件里呼叫離開守衛,
  3. 呼叫全域的 beforeEach 守衛,
  4. 在重用的組件里呼叫 beforeRouteUpdate 守衛 (2.2+),
  5. 在路由配置里呼叫 beforeEnter
  6. 決議異步路由組件,
  7. 在被激活的組件里呼叫 beforeRouteEnter
  8. 呼叫全域的 beforeResolve 守衛 (2.5+),
  9. 導航被確認,
  10. 呼叫全域的 afterEach 鉤子,
  11. 觸發 DOM 更新,
  12. 用創建好的實體呼叫 beforeRouteEnter 守衛中傳給 next 的回呼函式,

Vue.js ajax 請求放在哪個生命周期中?

答:

  • 在created的時候,視圖中的dom并沒有渲染出來,所以此時如果直接去操dom節點,無法找到相關的元素
  • 在mounted中,由于此時dom已經渲染出來了,所以可以直接操作dom節點

一般情況下都放到mounted中,保證邏輯的統一性,因為生命周期是同步執行的,ajax是異步執行的

Vue.js 中相同邏輯如何抽離?

答:Vue.mixin用法 給組件每個生命周期,函式等都混入一些公共邏輯

    Vue.mixin = function (mixin: Object) {
        this.options = mergeOptions(this.options, mixin); // 將當前定義的屬性合并到每個組件中
        return this
    }
    export function mergeOptions (
      parent: Object,
      child: Object,
      vm?: Component
    ): Object {
      if (!child._base) {
        if (child.extends) { // 遞回合并extends
          parent = mergeOptions(parent, child.extends, vm)
        }
        if (child.mixins) { // 遞回合并mixin
          for (let i = 0, l = child.mixins.length; i < l; i++) {
            parent = mergeOptions(parent, child.mixins[i], vm)
          }
        }
      }
      const options = {} // 屬性及生命周期的合并
      let key
      for (key in parent) {
        mergeField(key)
      }
      for (key in child) {
        if (!hasOwn(parent, key)) {
          mergeField(key)
        }
      }
      function mergeField (key) {
        const strat = strats[key] || defaultStrat
        // 呼叫不同屬性合并策略進行合并
        options[key] = strat(parent[key], child[key], vm, key)
      }
      return options
    }

Vue.js 中常見性能優化?

答:1.編碼優化:

  • 1.不要將所有的資料都放在data中,data中的資料都會增加getter和setter,會收集對應的watcher

  • 2.vue 在 v-for 時給每項元素系結事件需要用事件代理

  • 3.SPA頁面采用keep-alive快取組件

  • 4.拆分組件( 提高復用性、增加代碼的可維護性,減少不必要的渲染 )

  • 5.v-if 當值為false時內部指令不會執行,具有阻斷功能,很多情況下使用v-if替代v-show

  • 6.key保證唯一性 ( 默認vue會采用就地復用策略 )

  • 7.Object.freeze 凍結資料

  • 8.合理使用路由懶加載、異步組件

  • 9.盡量采用runtime運行時版本

  • 10.資料持久化的問題 (防抖、節流)

2.Vue加載性能優化:

  • 第三方模塊按需匯入 (babel-plugin-component)
  • 滾動到可視區域動態加載 ( https://tangbc.github.io/vue-virtual-scroll-list )

  • 圖片懶加載 (https://github.com/hilongjw/vue-lazyload.git)

3.用戶體驗:

  • app-skeleton骨架屏
  • app-shellapp殼
  • pwa

4.SEO優化:

  • 預渲染插件 prerender-spa-plugin
  • 服務端渲染ssr

5.打包優化:

  • 使用cdn的方式加載第三方模塊
  • 多執行緒打包 happypack
  • splitChunks 抽離公共檔案
  • sourceMap生成

6.快取,壓縮

  • 客戶端快取、服務端快取
  • 服務端gzip壓縮

談一下你對 Vue.js 的 MVVM 原理的理解?

答:傳統MVC

傳統的MVC指的是

  • M是指業務模型,Model(模型)
  • V是指用戶界面,View(視圖)
  • C則是控制器,Controller(控制器)

使用MVC的目的是將M和V的實作代碼分離,從而使同一個程式可以使用不同的表現形式,

用戶操作會請求服務端路由,路由會呼叫對應的控制器來處理,控制器會獲取資料,將結果回傳給前端,頁面重新渲染

MVVM

MVVM 是 Model-View-ViewModel 的縮寫

  • Model 代表資料模型,也可以在 Model 中定義資料修改和操作的業務邏輯,
  • View 代表 UI 組件,它負責將資料模型轉化成 UI 展現出來,
  • ViewModel 監聽模型資料的改變和控制視圖?為、處理?戶互動,簡單理解就是?個同步View 和 Model 的物件,連接 Model 和 View

傳統的前端會將資料手動渲染到頁面上,MVVM模式不需要用戶手動操作dom元素;

將資料系結到viewModel層上,會自動將資料渲染到頁面中,視圖變化會通知viewModel層更新資料,

ViewModel就是我們MVVM模式中的橋梁.

在 MVVM 架構下, View 和 Model 之間并沒有直接的聯系,?是通過 ViewModel 進?互動, Model 和 ViewModel 之間的互動是雙向的, 因 此 View 資料的變化會同步到Model中,?Model 資料的變化也會?即反 應到 View 上,

ViewModel 通過雙向資料系結把 View 層和 Model 層連接了起來,? View 和 Model 之間的同步?作完全是?動的,?需?為?涉,因此開 發者只需關注業務邏輯,不需要?動操作DOM,不需要關注資料狀態的同 步問題,復雜的資料狀態維護完全由 MVVM 來統?管理,

請說一下 Vue.js 回應式資料的原理?

答:

  • 1. 核心點:Object.defineProperty
  • 2. 默認Vue在初始化資料時,會給data中的屬性使用Object.defineProperty重新定義所有屬性,當頁面取到對應屬性時,會進行依賴收集(收集當前組件的watcher) 如果屬性發生變化會通知相關依賴進行更新操作,
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
          const value = getter ? getter.call(obj) : val
          if (Dep.target) {
            dep.depend() // ** 收集依賴 ** /
            if (childOb) {
              childOb.dep.depend()
              if (Array.isArray(value)) {
                dependArray(value)
              }
            }
          }
          return value
        },
        set: function reactiveSetter (newVal) {
          const value = getter ? getter.call(obj) : val
          if (newVal === value || (newVal !== newVal && value !== value)) {
            return
          }
          if (process.env.NODE_ENV !== 'production' && customSetter) {
            customSetter()
          }
          val = newVal
          childOb = !shallow && observe(newVal)
          dep.notify() /**通知相關依賴進行更新**/
        }
      })

Vue.js 中模板編譯原理?

答:template轉化成render函式

    function baseCompile (
      template: string,
      options: CompilerOptions
    ) {
      const ast = parse(template.trim(), options) // 1.將模板轉化成ast語法樹
      if (options.optimize !== false) {           // 2.優化樹
        optimize(ast, options)
      }
      const code = generate(ast, options)         // 3.生成樹
      return {
        ast,
        render: code.render,
        staticRenderFns: code.staticRenderFns
      }
    })
    const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`; 
    const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
    const startTagOpen = new RegExp(`^<${qnameCapture}`); // 標簽開頭的正則 捕獲的內容是標簽名
    const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`); // 匹配標簽結尾的  </div>
    const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; // 匹配屬性的
    const startTagClose = /^\s*(\/?)>/; // 匹配標簽結束的  >
    let root;
    let currentParent;
    let stack = []
    function createASTElement(tagName,attrs){
        return {
            tag:tagName,
            type:1,
            children:[],
            attrs,
            parent:null
        }
    }
    function start(tagName,attrs){
        let element = createASTElement(tagName,attrs);
        if(!root){
            root = element;
        }
        currentParent = element;
        stack.push(element);
    }
    function chars(text){
        currentParent.children.push({
            type:3,
            text
        })
    }
    function end(tagName){
        const element = stack[stack.length-1];
        stack.length --; 
        currentParent = stack[stack.length-1];
        if(currentParent){
            element.parent = currentParent;
            currentParent.children.push(element)
        }
    }
    function parseHTML(html){
        while(html){
            let textEnd = html.indexOf('<');
            if(textEnd == 0){
                const startTagMatch = parseStartTag();
                if(startTagMatch){
                    start(startTagMatch.tagName,startTagMatch.attrs);
                    continue;
                }
                const endTagMatch = html.match(endTag);
                if(endTagMatch){
                    advance(endTagMatch[0].length);
                    end(endTagMatch[1])
                }
            }
            let text;
            if(textEnd >=0 ){
                text = html.substring(0,textEnd)
            }
            if(text){
                advance(text.length);
                chars(text);
            }
        }
        function advance(n) {
            html = html.substring(n);
        }
        function parseStartTag(){
            const start = html.match(startTagOpen);
            if(start){
                const match = {
                    tagName:start[1],
                    attrs:[]
                }
                advance(start[0].length);
                let attr,end
                while(!(end = html.match(startTagClose)) && (attr=html.match(attribute))){
                    advance(attr[0].length);
                    match.attrs.push({name:attr[1],value:attr[3]})
                }
                if(end){
                    advance(end[0].length);
                    return match
                }
            }
        }
    }
    // 生成語法樹
    parseHTML(`<div id="container"><p>hello<span>zf</span></p></div>`);
    function gen(node){
        if(node.type == 1){
            return generate(node);
        }else{
            return `_v(${JSON.stringify(node.text)})`
        }
    }
    function genChildren(el){
        const children = el.children;
        if(el.children){
            return `[${children.map(c=>gen(c)).join(',')}]`
        }else{
            return false;
        }
    }
    function genProps(attrs){
        let str = '';
        for(let i = 0; i < attrs.length;i++){
            let attr = attrs[i];
            str+= `${attr.name}:${attr.value},`;
        }
        return `{attrs:{${str.slice(0,-1)}}}`
    }
    function generate(el){
        let children = genChildren(el);
        let code = `_c('${el.tag}'${
            el.attrs.length? `,${genProps(el.attrs)}`:''
        }${
            children? `,${children}`:''
        })`;
        return code;
    }
    // 根據語法樹生成新的代碼
    let code = generate(root);
    let render = `with(this){return ${code}}`;
    // 包裝成函式
    let renderFn = new Function(render);
    console.log(renderFn.toString());

簡述 Vue.js 中 diff 演算法原理?

答:

  • 1.先同級比較,在比較子節點
  • 2.先判斷一方有兒子一方沒兒子的情況
  • 3.比較都有兒子的情況
  • 4.遞回比較子節點
    const oldCh = oldVnode.children // 老的兒子 
    const ch = vnode.children  // 新的兒子
    if (isUndef(vnode.text)) {
        if (isDef(oldCh) && isDef(ch)) {
            // 比較孩子
            if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
        } else if (isDef(ch)) { // 新的兒子有 老的沒有
            if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
            addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
        } else if (isDef(oldCh)) { // 如果老的有新的沒有 就洗掉
            removeVnodes(oldCh, 0, oldCh.length - 1)
        } else if (isDef(oldVnode.text)) {  // 老的有文本 新的沒文本
            nodeOps.setTextContent(elm, '') // 將老的清空
        }
    } else if (oldVnode.text !== vnode.text) { // 文本不相同替換
        nodeOps.setTextContent(elm, vnode.text)
    }
    function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
        let oldStartIdx = 0
        let newStartIdx = 0
        let oldEndIdx = oldCh.length - 1
        let oldStartVnode = oldCh[0]
        let oldEndVnode = oldCh[oldEndIdx]
        let newEndIdx = newCh.length - 1
        let newStartVnode = newCh[0]
        let newEndVnode = newCh[newEndIdx]
        let oldKeyToIdx, idxInOld, vnodeToMove, refElm
        // removeOnly is a special flag used only by <transition-group>
        // to ensure removed elements stay in correct relative positions
        // during leaving transitions
        const canMove = !removeOnly
        if (process.env.NODE_ENV !== 'production') {
          checkDuplicateKeys(newCh)
        }
        while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
          if (isUndef(oldStartVnode)) {
            oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left
          } else if (isUndef(oldEndVnode)) {
            oldEndVnode = oldCh[--oldEndIdx]
          } else if (sameVnode(oldStartVnode, newStartVnode)) {
            patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
            oldStartVnode = oldCh[++oldStartIdx]
            newStartVnode = newCh[++newStartIdx]
          } else if (sameVnode(oldEndVnode, newEndVnode)) {
            patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
            oldEndVnode = oldCh[--oldEndIdx]
            newEndVnode = newCh[--newEndIdx]
          } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
            patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
            canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))
            oldStartVnode = oldCh[++oldStartIdx]
            newEndVnode = newCh[--newEndIdx]
          } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
            patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
            canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)
            oldEndVnode = oldCh[--oldEndIdx]
            newStartVnode = newCh[++newStartIdx]
          } else {
            if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
            idxInOld = isDef(newStartVnode.key)
              ? oldKeyToIdx[newStartVnode.key]
              : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
            if (isUndef(idxInOld)) { // New element
              createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
            } else {
              vnodeToMove = oldCh[idxInOld]
              if (sameVnode(vnodeToMove, newStartVnode)) {
                patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
                oldCh[idxInOld] = undefined
                canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
              } else {
                // same key but different element. treat as new element
                createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
              }
            }
            newStartVnode = newCh[++newStartIdx]
          }
        }
        if (oldStartIdx > oldEndIdx) {
          refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
          addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
        } else if (newStartIdx > newEndIdx) {
          removeVnodes(oldCh, oldStartIdx, oldEndIdx)
        }
      }

Vue.js 的v-model實作原理,及如何自定義v-model?

答:組件的v-modelvalue+input方法的語法糖

    <el-checkbox :value="" @input=""></el-checkbox>
    <el-checkbox v-model="check"></el-checkbox>

可以自己重新定義v-model的含義

    Vue.component('el-checkbox',{
        template:`<input type="checkbox" :checked="check" @change="$emit('change',$event.target.checked)">`,
        model:{
            prop:'check', // 更改默認的value的名字
            event:'change' // 更改默認的方法名
        },
        props: {
            check: Boolean
        },
    })

會將組件的v-model默認轉化成value+input

    const VueTemplateCompiler = require('vue-template-compiler');
    const ele = VueTemplateCompiler.compile('<el-checkbox v-model="check"></el-checkbox>');
    // with(this) {
    //     return _c('el-checkbox', {
    //         model: {
    //             value: (check),
    //             callback: function ($$v) {
    //                 check = $$v
    //             },
    //             expression: "check"
    //         }
    //     })
    // }
    function transformModel (options, data: any) {
      const prop = (options.model && options.model.prop) || 'value'
      const event = (options.model && options.model.event) || 'input'
      ;(data.attrs || (data.attrs = {}))[prop] = data.model.value
      const on = data.on || (data.on = {})
      const existing = on[event]
      const callback = data.model.callback
      if (isDef(existing)) {
        if (
          Array.isArray(existing)
            ? existing.indexOf(callback) === -1
            : existing !== callback
        ) {
          on[event] = [callback].concat(existing)
        }
      } else {
        on[event] = callback
      }
    }

原生的 v-model,會根據標簽的不同生成不同的事件和屬性

    const VueTemplateCompiler = require('vue-template-compiler');
    const ele = VueTemplateCompiler.compile('<input v-model="value"/>');
    /** 
    with(this) {
    return _c('input', {
        directives: [{
            name: "model",
            rawName: "v-model",
            value: (value),
            expression: "value"
        }],
        domProps: {
            "value": (value)
        },
        on: {
            "input": function ($event) {
                if ($event.target.composing) return;
                value = $event.target.value
            }
        }
    })
    }
    */

編譯時:不同的標簽決議出的內容不一樣

    if (el.component) {
        genComponentModel(el, value, modifiers)
        // component v-model doesn't need extra runtime
        return false
      } else if (tag === 'select') {
        genSelect(el, value, modifiers)
      } else if (tag === 'input' && type === 'checkbox') {
        genCheckboxModel(el, value, modifiers)
      } else if (tag === 'input' && type === 'radio') {
        genRadioModel(el, value, modifiers)
      } else if (tag === 'input' || tag === 'textarea') {
        genDefaultModel(el, value, modifiers)
      } else if (!config.isReservedTag(tag)) {
        genComponentModel(el, value, modifiers)
        // component v-model doesn't need extra runtime
        return false
      }

運行時:會對元素處理一些關于輸入法的問題

    inserted (el, binding, vnode, oldVnode) {
        if (vnode.tag === 'select') {
          // #6903
          if (oldVnode.elm && !oldVnode.elm._vOptions) {
            mergeVNodeHook(vnode, 'postpatch', () => {
              directive.componentUpdated(el, binding, vnode)
            })
          } else {
            setSelected(el, binding, vnode.context)
          }
          el._vOptions = [].map.call(el.options, getValue)
        } else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {
          el._vModifiers = binding.modifiers
          if (!binding.modifiers.lazy) {
            el.addEventListener('compositionstart', onCompositionStart)
            el.addEventListener('compositionend', onCompositionEnd)
            // Safari < 10.2 & UIWebView doesn't fire compositionend when
            // switching focus before confirming composition choice
            // this also fixes the issue where some browsers e.g. iOS Chrome
            // fires "change" instead of "input" on autocomplete.
            el.addEventListener('change', onCompositionEnd)
            /* istanbul ignore if */
            if (isIE9) {
              el.vmodel = true
            }
          }
        }
      }

Vue.js 組件的生命周期?

答:總共分為8個階段創建前/后,載?前/后,更新前/后,銷毀前/后

  • 創建前/后: 在 beforeCreate 階段, vue 實體的掛載元素 el 和資料物件 data 都為undefined ,還未初始化,在 created 階段, vue 實體的資料物件 data 有了,el還 沒有
  • 載?前/后:在 beforeMount 階段, vue 實體的 $el 和 data 都初始化了,但還是掛載之前為虛擬的 dom 節點, data.message 還未替換,在 mounted 階段, vue 實體掛載完成, data.message 成功渲染,
  • 更新前/后:當 data 變化時,會觸發 beforeUpdate 和 updated ?法
  • 銷毀前/后:在執? destroy ?法后,對 data 的改變不會再觸發周期函式,說明此時 vue 實體已經解除了事件監聽以及和 dom 的系結,但是 dom 結構依然存在

要掌握每個生命周期什么時候被呼叫

  • beforeCreate 在實體初始化之后,資料觀測(data observer) 之前被呼叫,
  • created 實體已經創建完成之后被呼叫,在這一步,實體已完成以下的配置:資料觀測(data observer),屬性和方法的運算, watch/event 事件回呼,這里沒有$el
  • beforeMount 在掛載開始之前被呼叫:相關的 render 函式首次被呼叫,
  • mounted el 被新創建的 vm.$el 替換,并掛載到實體上去之后呼叫該鉤子,
  • beforeUpdate 資料更新時呼叫,發生在虛擬 DOM 重新渲染和打補丁之前,
  • updated 由于資料更改導致的虛擬 DOM 重新渲染和打補丁,在這之后會呼叫該鉤子,
  • beforeDestroy 實體銷毀之前呼叫,在這一步,實體仍然完全可用,
  • destroyed Vue 實體銷毀后呼叫,呼叫后,Vue 實體指示的所有東西都會解系結,所有的事件監聽器會被移除,所有的子實體也會被銷毀, 該鉤子在服務器端渲染期間不被呼叫,

要掌握每個生命周期內部可以做什么事

  • created 實體已經創建完成,因為它是最早觸發的原因可以進行一些資料,資源的請求,
  • mounted 實體已經掛載完成,可以進行一些DOM操作
  • beforeUpdate 可以在這個鉤子中進一步地更改狀態,這不會觸發附加的重渲染程序,
  • updated 可以執行依賴于 DOM 的操作,然而在大多數情況下,你應該避免在此期間更改狀態,因為這可能會導致更新無限回圈, 該鉤子在服務器端渲染期間不被呼叫,
  • destroyed 可以執行一些優化操作,清空定時器,解除系結事件

什么是vue?命周期?

答: Vue 實體從創建到銷毀的程序,就是?命周期,從開始創建、初始化資料、編譯模板、掛載Dom→渲染、更新→渲染、銷毀等?系列程序,稱之為 Vue 的?命周期,

vue?命周期的作?是什么?

答:它的?命周期中有多個事件鉤?,讓我們在控制整個Vue實體的程序時更容易形成好的 邏輯,

vue?命周期總共有?個階段?

答:它可以總共分為 8 個階段:創建前/后、載?前/后、更新前/后、銷毀前/銷毀后,

第?次頁?加載會觸發哪?個鉤??

答:會觸發下?這?個 beforeCreate 、 created 、 beforeMount 、 mounted ,

DOM 渲染在哪個周期中就已經完成?

答: DOM 渲染在 mounted 中就已經完成了

描述組件渲染和更新程序?

答:渲染組件時,會通過Vue.extend方法構建子組件的建構式,并進行實體化,最終手動呼叫$mount()進行掛載,更新組件時會進行patchVnode流程.核心就是diff演算法

Vue.js 組件如何通信以及有哪些方式?

答:

  • 父子間通信 父->子通過props、子-> 父$on、$emit
  • 獲取父子組件實體的方式$parent、$children
  • 在父組件中提供資料子組件進行消費 Provide、inject
  • Ref獲取實體的方式呼叫組件的屬性或者方法
  • Event Bus 實作跨組件通信
  • Vuex狀態管理實作通信

1. ??通信

?組件通過 props 傳遞資料給?組件,?組件通過 emit 發送事件傳遞資料給?組件, 這兩種?式是最常?的??通信實作辦法,

這種??通信?式也就是典型的單向資料流,?組件通過 props 傳遞資料,?組件不能 直接修改 props ,?是必須通過發送事件的?式告知?組件修改資料,

另外這兩種?式還可以使?語法糖 v-model 來直接實作,因為 v-model 默認會決議成 名為 value 的 prop 和名為 input 的事件,這種語法糖的?式是典型的雙向系結, 常?于 UI 控制元件上,但是究其根本,還是通過事件的?法讓?組件修改資料,

當然我們還可以通過訪問 $parent 或者 $children 物件來訪問組件實體中的?法和數 據,

另外如果你使? Vue 2.3 及以上版本的話還可以使? $listeners 和 .sync 這兩個屬 性,

$listeners 屬性會將?組件中的 (不含 .native 修飾器的) v-on 事件監聽器傳遞給 ?組件,?組件可以通過訪問 $listeners 來?定義監聽器,

.sync 屬性是個語法糖,可以很簡單的實作?組件與?組件通信

<!--?組件中-->
<input :value.sync="value" />
<!--以上寫法等同于-->
<input :value="value" @update:value="v => value = v"></comp>
<!--?組件中-->
<script>
this.$emit('update:value', 1)
</script>

2. 兄弟組件通信

對于這種情況可以通過查找?組件中的?組件實作,也就是 this.$parent.$children ,在 $children 中可以通過組件 name 查詢 到需要的組件實體,然后進?通信,

3. 跨多層次組件通信

對于這種情況可以使? Vue 2.2 新增的 API provide / inject ,雖然? 檔中不推薦直接使?在業務中,但是如果?得好的話還是很有?的,

假設有?組件 A ,然后有?個跨多層級的?組件 B

// ?組件 A
export default {
provide: {
data: 1
}
}
// ?組件 B
export default {
inject: ['data'],
mounted() {
// ?論跨?層都能獲得?組件的 data 屬性
console.log(this.data) // => 1
}
}

終極辦法解決?切通信問題

只要你不怕麻煩,可以使? Vuex 或者 Event Bus 解決上述所有的通信情況,

為何 Vue.js 采用異步渲染?

答:因為如果不采用異步更新,那么每次更新資料都會對當前組件進行重新渲染.

所以為了性能考慮,Vue會在本輪資料更新后,再去異步更新視圖!

有點類似節流的原理

    update () {
        /* istanbul ignore else */
        if (this.lazy) {
          this.dirty = true
        } else if (this.sync) {
          this.run()
        } else {
          queueWatcher(this); // 當資料發生變化時會將watcher放到一個佇列中批量更新
        }
    }
    export function queueWatcher (watcher: Watcher) {
      const id = watcher.id // 會對相同的watcher進行過濾
      if (has[id] == null) {
        has[id] = true
        if (!flushing) {
          queue.push(watcher)
        } else {
          let i = queue.length - 1
          while (i > index && queue[i].id > watcher.id) {
            i--
          }
          queue.splice(i + 1, 0, watcher)
        }
        // queue the flush
        if (!waiting) {
          waiting = true
          if (process.env.NODE_ENV !== 'production' && !config.async) {
            flushSchedulerQueue()
            return
          }
          nextTick(flushSchedulerQueue) // 呼叫nextTick方法 批量的進行更新
        }
      }
    }

Vue.js 中 v-if 和 v-show 的區別?

答:

  • v-if如果條件不成立不會渲染當前指令所在節點的dom元素
  • v-show只是切換當前dom的顯示或者隱藏

v-show 只是在 display: none 和 display: block 之間切換,?論初始條件是什么 都會被渲染出來,后?只需要切換 CSS , DOM 還是?直保留著的,所以總的來說 v- show 在初始渲染時有更?的開銷,但是切換開銷很?,更適合于頻繁切換的場景,

v-if 的話就得說到 Vue 底層的編譯了,當屬性初始為 false 時,組件就不會被渲 染,直到條件為 true ,并且切換條件時會觸發銷毀/掛載組件,所以總的來說在切換時開 銷更?,更適合不經常切換的場景,

并且基于 v-if 的這種惰性渲染機制,可以在必要的時候才去渲染組件,減少整個??的 初始渲染開銷,

總結: v-if 按照條件是否渲染, v-show 是 display 的 block 或 none ;

    const VueTemplateCompiler = require('vue-template-compiler');
    let r1 = VueTemplateCompiler.compile(`<div v-if="true"><span v-for="i in 3">hello</span></div>`);
    /**
    with(this) {
        return (true) ? _c('div', _l((3), function (i) {
            return _c('span', [_v("hello")])
        }), 0) : _e()
    }
    */
    const VueTemplateCompiler = require('vue-template-compiler');
    let r2 = VueTemplateCompiler.compile(`<div v-show="true"></div>`);
    /**
    with(this) {
        return _c('div', {
            directives: [{
                name: "show",
                rawName: "v-show",
                value: (true),
                expression: "true"
            }]
        })
    }
     */
    // v-show 操作的是樣式  定義在platforms/web/runtime/directives/show.js
    bind (el: any, { value }: VNodeDirective, vnode: VNodeWithData) {
        vnode = locateNode(vnode)
        const transition = vnode.data && vnode.data.transition
        const originalDisplay = el.__vOriginalDisplay =
          el.style.display === 'none' ? '' : el.style.display
        if (value && transition) {
          vnode.data.show = true
          enter(vnode, () => {
            el.style.display = originalDisplay
          })
        } else {
          el.style.display = value ? originalDisplay : 'none'
        }
    }

Vue.js中為什么 v-for 和 v-if 不能連用?

答:v-for會比v-if的優先級高一些,如果連用的話會把v-if給每個元素都添加一下,會造成性能問題

    const VueTemplateCompiler = require('vue-template-compiler');
    let r1 = VueTemplateCompiler.compile(`<div v-if="false" v-for="i in 3">hello</div>`);
    /**
    with(this) {
        return _l((3), function (i) {
            return (false) ? _c('div', [_v("hello")]) : _e()
        })
    }
    */
    console.log(r1.render);

Vue.js 中實作 hash路由和history路由?

答:

  • onhashchange
  • history.pushState

  • hash 模式:在瀏覽器中符號 “#” ,#以及#后?的字符稱之為 hash ,? window.location.hash 讀取,特點: hash 雖然在 URL 中,但不被包括在 HTTP 請求中;?來指導瀏覽器動作,對服務端安全??, hash 不會重加載??,

  • history 模式:h istory 采? HTML5 的新特性;且提供了兩個新?法:pushState() , replaceState() 可以對瀏覽器歷史記錄堆疊進?修改,以及 popState 事件的監聽到狀態變更

Vue.js 的 action 和 mutation 區別?

答:

  • mutation是同步更新資料(內部會進行是否為異步方式更新資料的檢測)
  • action 異步操作,可以獲取資料后調傭mutation提交最終資料

Vue.js 和 react 區別?

答:相同點

都?持 ssr ,都有 vdom ,組件化開發,實作 webComponents 規范,資料驅 動等

不同點

vue 是雙向資料流(當然為了實作單資料流?便管理組件狀態, vuex 便出現了), react 是單向資料流, vue 的 vdom 是追蹤每個組件的依賴關系,不會渲染整個組件樹, react 每當應該狀態被改變時,全部?組件都會 re-render

React.js 面試題

React ?命周期函式?

答:在 V16 版本中引?了 Fiber 機制,這個機制?定程度上的影響了部分?命 周期的調?,并且也引?了新的 2 個 API 來解決問題

在之前的版本中,如果你擁有?個很復雜的復合組件,然后改動了最上層組件 的 state ,那么調?堆疊可能會很調?堆疊過,再加上中間進?了復雜的操作,就可能導致?時間阻塞主執行緒,帶來不好的 ?戶體驗, Fiber 就是為了解決該問題??Fiber 本質上是?個虛擬的堆疊幀,新的調度器會按照優先級?由調度這些幀,從?將 之前的同步渲染改成了異步渲染,在不影響體驗的情況下去分段計算更新對于如何區別優先級, React 有??的?套邏輯,對于影片這種實時性很?的東?,也 就是 16 ms 必須渲染?次保證不卡頓的情況下, React 會每 16 ms (以內) 暫停? 下更新,回傳來繼續渲染影片,對于異步渲染,現在渲染有兩個階段: reconciliation 和 commit ,前者程序是可以 打斷的,后者不能暫停,會?直更新界?直到完成,

初始化階段

  • getDefaultProps :獲取實體的默認屬性
  • getInitialState :獲取每個實體的初始化狀態
  • componentWillMount :組件即將被裝載、渲染到??上
  • render :組件在這??成虛擬的 DOM 節點
  • omponentDidMount :組件真正在被裝載之后

運?中狀態

  • componentWillReceiveProps :組件將要接收到屬性的時候調?
  • shouldComponentUpdate :組件接受到新屬性或者新狀態的時候(可以回傳false,接收數
  • 據后不更新,阻? render 調?,后?的函式不會被繼續執?了)
  • componentWillUpdate :組件即將更新不能修改屬性和狀態
  • render :組件重新描繪
  • componentDidUpdate :組件已經更新

銷毀階段

  • componentWillUnmount :組件即將銷毀

因為 Reconciliation 階段是可以被打斷的,所以 Reconciliation 階段 會執?的?命周期函式就可能會出現調?多次的情況,從?引起 Bug ,由此 對于 Reconciliation 階段調?的?個函式,除了 shouldComponentUpdate 以外,其他都應該避免去使?,并且 V16 中也 引?了新的 API 來解決這個問題,

getDerivedStateFromProps ?于替換 componentWillReceiveProps , 該函式會在初始化和 update 時被調?

React 中 keys 的作?是什么?

答:Keys 是 React ?于追蹤哪些串列中元素被修改、被添加或者被移除的輔助標識

在開發程序中,我們需要保證某個元素的 key 在其同級元素中具有唯?性,在 React Diff 演算法中 React 會借助元素的 Key 值來判斷該元素是新近創建的還是被移動?來 的元素,從?減少不必要的元素重渲染,此外,React 還需要借助 Key 值來判斷元素與 本地狀態的關聯關系,因此我們絕不可忽視轉換函式中 Key 的重要性

React 中 refs 的作?是什么?

答:

  • Refs 是 React 提供給我們的安全訪問 DOM 元素或者某個組件實體的句柄
  • 可以為元素添加 ref 屬性然后在回呼函式中接受該元素在 DOM 樹中的句柄,該值會作為回呼函式的第?個引數回傳

React Native相對于原生的ios和Android有哪些優勢?

答:

  • 1.性能媲美原生APP
  • 2.使用JavaScript編碼,只要學習這一種語言
  • 3.絕大部分代碼安卓和IOS都能共用
  • 4.組件式開發,代碼重用性很高
  • 5.跟撰寫網頁一般,修改代碼后即可自動重繪,不需要慢慢編譯,節省很多編譯等待時間
  • 6.支持APP熱更新,更新無需重新安裝APP

缺點:

  • 記憶體占用相對較高
  • 版本還不穩定,一直在更新,現在還沒有推出穩定的1.0版本

props和state相同點和不同點?

答:1.不管是props還是state的改變,都會引發render的重新渲染,

2.都能由自身組件的相應初始化函式設定初始值,

不同點

1.初始值來源:state的初始值來自于自身的getInitalState(constructor)函式;props來自于父組件或者自身getDefaultProps(若key相同前者可覆寫后者),

2.修改方式:state只能在自身組件中setState,不能由父組件修改;props只能由父組件修改,不能在自身組件修改,

3.對子組件:props是一個父組件傳遞給子組件的資料流,這個資料流可以一直傳遞到子孫組件;state代表的是一個組件內部自身的狀態,只能在自身組件中存在,


簡述flux 思想?

答:Flux 的最?特點,就是資料的”單向流動”,

  • ?戶訪問 View
  • View 發出?戶的 Action
  • Dispatcher 收到 Action ,要求 Store 進?相應的更新
  • Store 更新后,發出?個 “change” 事件
  • View 收到 “change” 事件后,更新頁?

說說你?react有什么坑點?

答:

  1. JSX做運算式判斷時候,需要強轉為boolean型別

如果不使? !!b 進?強轉資料型別,會在頁???輸出 0 ,

  1. render() {
  2. const b = 0;
  3. return <div>
  4. {
  5. !!b && <div>這是?段?本</div>
  6. }
  7. </div>
  8. }
  1. 盡量不要在 componentWillReviceProps ?使? setState,如果?定要使?,那么需要判 斷結束條件,不然會出現?限重渲染,導致頁?崩潰

  2. 給組件添加ref時候,盡量不要使?匿名函式,因為當組件更新的時候,匿名函式會被當做 新的prop處理,讓ref屬性接受到新函式的時候,react內部會先清空ref,也就是會以null為回 調引數先執??次ref這個props,然后在以該組件的實體執??次ref,所以?匿名函式做ref 的時候,有的時候去ref賦值后的屬性會取到null

  3. 遍歷?節點的時候,不要? index 作為組件的 key 進?傳?

React 性能優化?案?

答:

  • 重寫 shouldComponentUpdate 來避免不必要的dom操作
  • 使? production 版本的 react.js
  • 使? key 來幫助 React 識別串列中所有?組件的最?變化

在 shouldComponentUpdate 函式中我們可以通過回傳布林值來決定當前組件是否需要更 新,這層代碼邏輯可以是簡單地淺?較?下當前 state 和之前的 state 是否相同,也 可以是判斷某個值更新了才觸發組件更新,?般來說不推薦完整地對?當前 state 和之 前的 state 是否相同,因為組件更新觸發可能會很頻繁,這樣的完整對?性能開銷會有 點?,可能會造成得不償失的情況

當然如果真的想完整對?當前 state 和之前的 state 是否相同,并且不影響性能也是 ?得通的,可以通過 immutable 或者 immer 這些庫來?成不可變物件,這類別庫對于操 作?規模的資料來說會提升不錯的性能,并且?旦改變資料就會?成?個新的物件,對? 前后 state 是否?致也就?便多了,同時也很推薦閱讀下 immer 的原始碼實作

另外如果只是單純的淺?較?下,可以直接使? PureComponent ,底層就是實作了淺?較 state

    class Test extends React.PureComponent {
        render() {
            return (
                <div>
                    PureComponent
                </div>
            )
        }
    }

這時候你可能會考慮到函陣列件就不能使?這種?式了,如果你使?16.6.0 之后的版本的話,可以使? React.memo 來實作相同的功能

    const Test = React.memo(() => (
        <div>
            PureComponent
        </div>
    ))

通過這種?式我們就可以既實作了 shouldComponentUpdate 的淺?較,?能夠使?函陣列件

React 監控?

答:

前端監控?般分為三種,分別為??埋點、性能監控以及例外監控,

這?章節我們將來學習這些監控相關的內容,但是基本不會涉及到代碼,只是讓?家了解下前 端監控該?什么?式實作,畢竟?部分公司都只是使?到了第三?的監控?具,?不是選擇? ?造輪?

1 頁?埋點

頁?埋點應該是?家最常寫的監控了,?般起碼會監控以下?個資料:

  • PV / UV
  • 停留時?
  • 流量來源
  • ?戶互動

對于這?類統計,?般的實作思路?致可以分為兩種,分別為?寫埋點和?埋 點的?式,

相信第?種?式也是?家最常?的?式,可以?主選擇需要監控的資料然后在相應的地?寫? 代碼,這種?式的靈活性很?,但是唯?的缺點就是?作量較?,每個需要監控的地?都得插 ?代碼,

另?種?埋點的?式基本不需要開發者?寫埋點了,?是統計所有的事件并且定時上報,這種 ?式雖然沒有前?種?式繁瑣了,但是因為統計的是所有事件,所以還需要后期過濾出需要的 資料,

2 性能監控

性能監控可以很好的幫助開發者了解在各種真實環境下,??的性能情況是如何的,

對于性能監控來說,我們可以直接使?瀏覽器?帶的 Performance API 來實作這個功 能,

對于性能監控來說,其實我們只需要調?

performance.getEntriesByType(‘navigation’) 這?代碼就?了,對,你沒看錯,? ?代碼我們就可以獲得??中各種詳細的性能相關資訊

我們可以發現這?代碼回傳了?個陣列,內部包含了相當多的資訊,從資料開 始在?絡中傳輸到??加載完成都提供了相應的資料

3 例外監控

對于例外監控來說,以下兩種監控是必不可少的,分別是代碼報錯以及接?例外上報, 對于代碼運?錯誤,通常的辦法是使? window.onerror 攔截報錯,該?法能攔截到? 部分的詳細報錯資訊,但是也有例外

  1. 對于跨域的代碼運?錯誤會顯示 Script error . 對于這種情況我們需要給 script 標簽 添加 crossorigin 屬性

  2. 對于某些瀏覽器可能不會顯示調?堆疊資訊,這種情況可以通過 arguments.callee.caller 來做堆疊遞回

對于異步代碼來說,可以使? catch 的?式捕獲錯誤,?如 Promise 可以直接使? catch 函式, async await 可以使? try catch

但是要注意線上運?的代碼都是壓縮過的,需要在打包時?成 sourceMap ?件便于 debug

對于捕獲的錯誤需要上傳給服務器,通常可以通過 img 標簽的 src 發起?個請求,

另外接?例外就相對來說簡單了,可以列舉出出錯的狀態碼,?旦出現此類的狀態碼就可 以?即上報出錯,接?例外上報可以讓開發?員迅速知道有哪些接?出現了??積的報 錯,以便迅速修復問題,

React.js 通信?

答:1. ??通信

?組件通過 props 傳遞資料給?組件,?組件通過調??組件傳來的函式傳遞資料給? 組件,這兩種?式是最常?的??通信實作辦法,

這種??通信?式也就是典型的單向資料流,?組件通過 props 傳遞資料,?組件不能 直接修改 props , ?是必須通過調??組件函式的?式告知?組件修改資料,

2. 兄弟組件通信

對于這種情況可以通過共同的?組件來管理狀態和事件函式,?如說其中?個 兄弟組件調??組件傳遞過來的事件函式修改?組件中的狀態,然后?組件將 狀態傳遞給另?個兄弟組件

3. 跨多層次組件通信

如果你使? 16.3 以上版本的話,對于這種情況可以使? Context API

// 創建 Context,可以在開始就傳?值
const StateContext = React.createContext()
class Parent extends React.Component {
render () {
return (
// value 就是傳? Context 中的值
<StateContext.Provider value='yck'>
<Child />
</StateContext.Provider>
)
}
}
class Child extends React.Component {
render () {
return (
<ThemeContext.Consumer>
// 取出值
{context => (
name is { context }
)}
</ThemeContext.Consumer>
);
}
}

4. 任意組件

這種?式可以通過 Redux 或者 Event Bus 解決,另外如果你不怕麻煩的 話,可以使?這種?式解決上述所有的通信情況,

React 事件機制?

答:React 其實??實作了?套事件機制,?先我們考慮?下以下代碼

const Test = ({ list, handleClick }) => ({ list.map((item, index) => ( <span onClick={handleClick} key={index}>{index}</span> )) })

以上類似代碼想必?家經常會寫到,但是你是否考慮過點擊事件是否系結在了每?個標簽 上?事實當然不是, JSX 上寫的事件并沒有系結在對應的真實 DOM 上,?是通過事件 代理的?式,將所有的事件都統?系結在了 document 上,這樣的?式不僅減少了記憶體消 耗,還能在組件掛載銷毀時統?訂閱和移除事件,

另外冒泡到 document 上的事件也不是原?瀏覽器事件,?是 React ??實作的合成 事件( SyntheticEvent ),因此我們如果不想要事件冒泡的話,調? event.stopPropagation 是?效的,?應該調? event.preventDefault

那么實作合成事件的?的是什么呢?總的來說在我看來好處有兩點,分別是:

  1. 合成事件?先抹平了瀏覽器之間的兼容問題,另外這是?個跨瀏覽器原?事件包裝器,賦 予了跨瀏覽器開發的能?

  2. 對于原?瀏覽器事件來說,瀏覽器會給監聽器創建?個事件物件,如果你有很多的事件監 聽,那么就需要分配很多的事件物件,造成?額的記憶體分配問題,但是對于合成事件來 說,有?個事件池專?來管理它們的創建和銷毀,當事件需要被使?時,就會從池?中復 ?物件,事件回呼結束后,就會銷毀事件物件上的屬性,從?便于下次復?事件物件,

工具面試題

git 與 svn 的區別在哪里?

答:gitsvn 最大的區別在于 git 是分布式的,而 svn 是集中式的,因此我們不能再離線的情況下使用 svn,如果服務器 出現問題,我們就沒有辦法使用 svn 來提交我們的代碼,

svn 中的分支是整個版本庫的復制的一份完整目錄,而 git 的分支是指標指向某次提交,因此 git 的分支創建更加開銷更小 并且分支上的變化不會影響到其他人,svn 的分支變化會影響到所有的人,

svn 的指令相對于 git 來說要簡單一些,比 git 更容易上手,

經常使用的 git 命令?

答:

    git init                     // 新建 git 代碼庫
    git add                      // 添加指定檔案到暫存區
    git rm                       // 洗掉作業區檔案,并且將這次洗掉放入暫存區
    git commit -m [message]      // 提交暫存區到倉庫區
    git branch                   // 列出所有分支
    git checkout -b [branch]     // 新建一個分支,并切換到該分支
    git status                   // 顯示有變更的檔案

git pull 和 git fetch 的區別?

答:git fetch 只是將遠程倉庫的變化下載下來,并沒有和本地分支合并,

git pull 會將遠程倉庫的變化下載下來,并和當前分支合并,

git rebase 和 git merge 的區別?

答:git mergegit rebase 都是用于分支合并,關鍵在 commit 記錄的處理上不同,

git merge 會新建一個新的 commit 物件,然后兩個分支以前的 commit 記錄都指向這個新 commit 記錄,這種方法會 保留之前每個分支的 commit 歷史,

git rebase 會先找到兩個分支的第一個共同的 commit 祖先記錄,然后將提取當前分支這之后的所有 commit 記錄,然后 將這個 commit 記錄添加到目標分支的最新提交后面,經過這個合并后,兩個分支合并后的 commit 記錄就變為了線性的記 錄了,

演算法面試題

JavaScript 如何求陣列的最大值和最小值?

答:

    var arr = [6, 4, 1, 8, 2, 11, 23];
    console.log(Math.max.apply(null, arr))

如何查找一篇英文文章中出現頻率最高的單詞?

答:

    function findMostWord(article) {
    // 合法性判斷
    if (!article) return;
    // 引數處理
    article = article.trim().toLowerCase();
    let wordList = article.match(/[a-z]+/g),
      visited = [],
      maxNum = 0,
      maxWord = "";
    article = " " + wordList.join("  ") + " ";
    // 遍歷判斷單詞出現次數
    wordList.forEach(function (item) {
      if (visited.indexOf(item) < 0) {
        let word = new RegExp(" " + item + " ", "g"),
          num = article.match(word).length;
        if (num > maxNum) {
          maxNum = num;
          maxWord = item;
        }
      }
    });
    return maxWord + "  " + maxNum;
    }

JavaScript 冒泡排序?

答:冒泡排序的基本思想是,對相鄰的元素進行兩兩比較,順序相反則進行交換,這樣,每一趟會將最小或最大的元素“浮”到頂端,最終達到完全有序,

代碼實作:

    function bubbleSort(arr) {
        if (!Array.isArray(arr) || arr.length <= 1) return;
        let lastIndex = arr.length - 1;
        while (lastIndex > 0) { // 當最后一個交換的元素為第一個時,說明后面全部排序完畢
            let flag = true, k = lastIndex;
            for (let j = 0; j < k; j++) {
                if (arr[j] > arr[j + 1]) {
                    flag = false;
                  	lastIndex = j; // 設定最后一次交換元素的位置
                    [arr[j], arr[j+1]] = [arr[j+1], arr[j]];
                }
            }
          	if (flag) break;
        }
    }

冒泡排序有兩種優化方式,

一種是外層回圈的優化,我們可以記錄當前回圈中是否發生了交換,如果沒有發生交換,則說明該序列已經為有序序列了, 因此我們不需要再執行之后的外層回圈,此時可以直接結束,

一種是內層回圈的優化,我們可以記錄當前回圈中最后一次元素交換的位置,該位置以后的序列都是已排好的序列,因此下 一輪回圈中無需再去比較,

優化后的冒泡排序,當排序序列為已排序序列時,為最好的時間復雜度為 O(n),

冒泡排序的平均時間復雜度為 O(n2) ,最壞時間復雜度為 O(n2) ,空間復雜度為 O(1) ,是穩定排序,

JavaScript 選擇排序?

答:選擇排序的基本思想為每一趟從待排序的資料元素中選擇最小(或最大)的一個元素作為首元素,直到所有元素排完為止,

在演算法實作時,每一趟確定最小元素的時候會通過不斷地比較交換來使得首位置為當前最小,交換是個比較耗時的操作,其實 我們很容易發現,在還未完全確定當前最小元素之前,這些交換都是無意義的,我們可以通過設定一個變數 min,每一次比較 僅存盤較小元素的陣列下標,當輪回圈結束之后,那這個變數存盤的就是當前最小元素的下標,此時再執行交換操作即可,

代碼實作:

    function selectSort(array) {
      let length = array.length;
      // 如果不是陣列或者陣列長度小于等于1,直接回傳,不需要排序 
      if (!Array.isArray(array) || length <= 1) return;
      for (let i = 0; i < length - 1; i++) {
        let minIndex = i; // 設定當前回圈最小元素索引
        for (let j = i + 1; j < length; j++) {
          // 如果當前元素比最小元素索引,則更新最小元素索引
          if (array[minIndex] > array[j]) {
            minIndex = j;
          }
        }
        // 交換最小元素到當前位置
        // [array[i], array[minIndex]] = [array[minIndex], array[i]];
        swap(array, i, minIndex);
      }
      return array;
    }
    // 交換陣列中兩個元素的位置
    function swap(array, left, right) {
      var temp = array[left];
      array[left] = array[right];
      array[right] = temp;
    }

選擇排序不管初始序列是否有序,時間復雜度都為 O(n2),

選擇排序的平均時間復雜度為 O(n2) ,最壞時間復雜度為 O(n2) ,空間復雜度為 O(1) ,不是穩定排序,

JavaScript 插入排序?

答:直接插入排序基本思想是每一步將一個待排序的記錄,插入到前面已經排好序的有序序列中去,直到插完所有元素為止,

插入排序核心–撲克牌思想: 就想著自己在打撲克牌,接起來一張,放哪里無所謂,再接起來一張,比第一張小,放左邊, 繼續接,可能是中間數,就插在中間….依次

代碼實作:

    function insertSort(array) {
      let length = array.length;
      // 如果不是陣列或者陣列長度小于等于1,直接回傳,不需要排序 
      if (!Array.isArray(array) || length <= 1) return;
      // 回圈從 1 開始,0 位置為默認的已排序的序列
      for (let i = 1; i < length; i++) {
        let temp = array[i]; // 保存當前需要排序的元素
        let j = i;
        // 在當前已排序序列中比較,如果比需要排序的元素大,就依次往后移動位置
        while (j -1 >= 0 && array[j - 1] > temp) {
          array[j] = array[j - 1];
          j--;
        }
        // 將找到的位置插入元素
        array[j] = temp;
      }
      return array;
    }

當排序序列為已排序序列時,為最好的時間復雜度 O(n),

插入排序的平均時間復雜度為 O(n2) ,最壞時間復雜度為 O(n2) ,空間復雜度為 O(1) ,是穩定排序,

JavaScript 希爾排序?

答:希爾排序的基本思想是把陣列按下標的一定增量分組,對每組使用直接插入排序演算法排序;隨著增量逐漸減少,每組包含的元 素越來越多,當增量減至1時,整個陣列恰被分成一組,演算法便終止,

    function hillSort(array) {
      let length = array.length;
      // 如果不是陣列或者陣列長度小于等于1,直接回傳,不需要排序 
      if (!Array.isArray(array) || length <= 1) return;
      // 第一層確定增量的大小,每次增量的大小減半
      for (let gap = parseInt(length >> 1); gap >= 1; gap = parseInt(gap >> 1)) {
        // 對每個分組使用插入排序,相當于將插入排序的1換成了 n
        for (let i = gap; i < length; i++) {
          let temp = array[i];
          let j = i;
          while (j - gap >= 0 && array[j - gap] > temp) {
            array[j] = array[j - gap];
            j -= gap;
          }
          array[j] = temp;
        }
      }
      return array;
    }

希爾排序是利用了插入排序對于已排序序列排序效果最好的特點,在一開始序列為無序序列時,將序列分為多個小的分組進行 基數排序,由于排序基數小,每次基數排序的效果較好,然后在逐步增大增量,將分組的大小增大,由于每一次都是基于上一 次排序后的結果,所以每一次都可以看做是一個基本排序的序列,所以能夠最大化插入排序的優點,

簡單來說就是,由于開始時每組只有很少整數,所以排序很快,之后每組含有的整數越來越多,但是由于這些數也越來越有序, 所以排序速度也很快,

希爾排序的時間復雜度根據選擇的增量序列不同而不同,但總的來說時間復雜度是小于 O(n^2) 的,

插入排序是一個穩定排序,但是在希爾排序中,由于相同的元素可能在不同的分組中,所以可能會造成相同元素位置的變化, 所以希爾排序是一個不穩定的排序,

希爾排序的平均時間復雜度為 O(nlogn) ,最壞時間復雜度為 O(n^s) ,空間復雜度為 O(1) ,不是穩定排序,

有一座高度是10級臺階的樓梯,從下往上走,每跨一步只能向上1級或者2級臺階,要求用程式來求出一共有多少種走法?

答:由分析可知,假設我們只差最后一步就能走上第10級階梯,這個時候一共有兩種情況,因為每一步只允許走1級或2級階梯, 因此分別為從8級階梯和從9九級階梯走上去的情況,因此從0到10級階梯的走法數量就等于從0到9級階梯的走法數量加上 從0到8級階梯的走法數量,依次類推,我們可以得到一個遞回關系,遞回結束的標志為從0到1級階梯的走法數量和從0到 2級階梯的走法數量,

    function getClimbingWays(n) {
      if (n < 1) {
        return 0;
      }
      if (n === 1) {
        return 1;
      }
      if (n === 2) {
        return 2;
      }
      return getClimbingWays(n - 1) + getClimbingWays(n - 2);
    }

綜合面試題

談?談let與var的區別?

答:

  • let 命令不存在變數提升,如果在 let 前使?,會導致報錯
  • 如果塊區中存在 let 和 const 命令,就會形成封閉作?域
  • 不允許重復宣告,因此,不能在函式內部重新宣告參數

如何通過JS判斷?個陣列?

答:instanceof ?法

instanceof 運算子是?來測驗?個物件是否在其原型鏈原型建構式的屬性

var arr = [];
arr instanceof Array; // true

constructor ?法

constructor 屬性回傳對創建此物件的陣列函式的引?,就是回傳物件相對應的構造 函式

var arr = [];
arr.constructor == Array; //true

最簡單的?法

這種寫法,是 jQuery 正在使?的


    Object.prototype.toString.call(value) == '[object Array]'
    // 利?這個?法,可以寫?個回傳資料型別的?法
    var isType = function (obj) {
    return Object.prototype.toString.call(obj).slice(8,-1);
    }

ES5 新增?法 isArray()

var a = new Array(123);
var b = new Date();
console.log(Array.isArray(a)); //true
console.log(Array.isArray(b)); //false

項?做過哪些性能優化?

答:

  • 減少 HTTP 請求數
  • 減少 DNS 查詢
  • 使? CDN
  • 避免重定向
  • 圖?懶加載
  • 減少 DOM 元素數量
  • 減少 DOM 操作
  • 使?外部 JavaScript 和 CSS
  • 壓縮 JavaScript 、 CSS 、字體、圖?等
  • 優化 CSS Sprite
  • 使? iconfont
  • 字體裁剪
  • 多域名分發劃分內容到不同域名
  • 盡量減少 iframe 使?
  • 避免圖? src 為空
  • 把樣式表放在 link 中
  • 把 JavaScript 放在頁?底部

瀏覽器快取?

答:瀏覽器快取分為強快取和協商快取,當客戶端請求某個資源時,獲取快取的流 程如下

先根據這個資源的?些 http header 判斷它是否命中強快取,如果命中,則直接從本地 獲取快取資源,不會發請求到服務器;

當強快取沒有命中時,客戶端會發送請求到服務器,服務器通過另?些 request header 驗證這個資源是否命中協商快取,稱為 http 再驗證,如果命中,服務器將請求回傳,但 不回傳資源,?是告訴客戶端直接從快取中獲取,客戶端收到回傳后就會從快取中獲取資 源;

強快取和協商快取共同之處在于,如果命中快取,服務器都不會回傳資源; 區別是,強緩 存不對發送請求到服務器,但協商快取會,

當協商快取也沒命中時,服務器就會將資源發送回客戶端,

當 ctrl+f5 強制重繪網頁時,直接從服務器加載,跳過強快取和協商快取;

當 f5 重繪網頁時,跳過強快取,但是會檢查協商快取;

強快取

Expires (該欄位是 http1.0 時的規范,值為?個絕對時間的 GMT 格式的時間字符 串,代表快取資源的過期時間)

Cache-Control:max-age (該欄位是 http1.1 的規范,強快取利?其 max-age 值來 判斷快取資源的最??命周期,它的值單位為秒)

協商快取

Last-Modified (值為資源最后更新時間,隨服務器response回傳)

If-Modified-Since (通過?較兩個時間來判斷資源在兩次請求期間是否有過修改,如 果沒有修改,則命中協商快取)

ETag (表示資源內容的唯?標識,隨服務器 response 回傳)

If-None-Match (服務器通過?較請求頭部的 If-None-Match 與當前資源的 ETag 是 否?致來判斷資源是否在兩次請求之間有過修改,如果沒有修改,則命中協商快取

前端需要注意哪些SEO?

答:

  • 合理的 title 、 description 、 keywords :搜索對著三項的權重逐個減?, title
  • 值強調重點即可,重要關鍵詞出現不要超過2次,?且要靠前,不同?? title 要有所不
  • 同; description 把??內容?度概括,?度合適,不可過分堆砌關鍵詞,不同??
  • description 有所不同; keywords 列舉出重要關鍵詞即可
  • 語意化的 HTML 代碼,符合W3C規范:語意化代碼讓搜索引擎容易理解??
  • 重要內容 HTML 代碼放在最前:搜索引擎抓取 HTML 順序是從上到下,有的搜索引擎對抓
  • 取?度有限制,保證重要內容?定會被抓取
  • 重要內容不要? js 輸出:爬?不會執?js獲取內容
  • 少? iframe :搜索引擎不會抓取 iframe 中的內容
  • ?裝飾性圖?必須加 alt
  • 提??站速度:?站速度是搜索引擎排序的?個重要指標

瀏覽器渲染原理?

答:1. 瀏覽器接收到 HTML ?件并轉換為 DOM 樹

當我們打開?個??時,瀏覽器都會去請求對應的 HTML ?件,雖然平時我 們寫代碼時都會分為 JS 、 CSS 、 HTML ?件,也就是字串,但是計算機 硬體是不理解這些字串的,所以在?絡中傳輸的內容其實都是 0 和 1 這些位元組資料,當瀏覽器接收到這些位元組資料以后,它會將這些位元組資料轉換為 字串,也就是我們寫的代碼,

當資料轉換為字串以后,瀏覽器會先將這些字串通過詞法分析轉換為標記 ( token ),這?程序在詞法分析中叫做標記化( tokenization )

那么什么是標記呢?這其實屬于編譯原理這?塊的內容了,簡單來說,標記還 是字串,是構成代碼的最?單位,這?程序會將代碼分拆成?塊塊,并給這 些內容打上標記,便于理解這些最?單位的代碼是什么意思

當結束標記化后,這些標記會緊接著轉換為 Node ,最后這些 Node 會根據 不同 Node 之前的聯系構建為?顆 DOM 樹

以上就是瀏覽器從?絡中接收到 HTML ?件然后?系列的轉換程序 當然,在決議 HTML ?件的時候,瀏覽器還會遇到 CSS 和 JS ?件,這時 候瀏覽器也會去下載并決議這些?件,接下來就讓我們先來學習瀏覽器如何解 析 CSS ?件

2. 將 CSS ?件轉換為 CSSOM 樹

其實轉換 CSS 到 CSSOM 樹的程序和上??節的程序是極其類似的

在這?程序中,瀏覽器會確定下每?個節點的樣式到底是什么,并且這?程序其實是很消 耗資源的,因為樣式你可以??設定給某個節點,也可以通過繼承獲得,在這?程序中, 瀏覽器得遞回 CSSOM 樹,然后確定具體的元素到底是什么樣式,

如果你有點不理解為什么會消耗資源的話,我這?舉個例?

<div>
<a> <span></span> </a>
</div>
<style>
span {
color: red;
}
div > a > span {
color: red;
}
</style>

對于第?種設定樣式的?式來說,瀏覽器只需要找到??中所有的 span 標 簽然后設定顏?,但是對于第?種設定樣式的?式來說,瀏覽器?先需要找到 所有的 span 標簽,然后找到 span 標簽上的 a 標簽,最后再去找到 div 標簽,然后給符合這種條件的 span 標簽設定顏?,這樣的遞回程序 就很復雜,所以我們應該盡可能的避免寫過于具體的 CSS 選擇器,然后對于 HTML 來說也盡量少的添加?意義標簽,保證層級扁平

3. ?成渲染樹

當我們?成 DOM 樹和 CSSOM 樹以后,就需要將這兩棵樹組合為渲染樹

在這?程序中,不是簡單的將兩者合并就?了,渲染樹只會包括需要顯示的節點和這些節 點的樣式資訊,如果某個節點是 display: none 的,那么就不會在渲染樹中顯示,

當瀏覽器?成渲染樹以后,就會根據渲染樹來進?布局(也可以叫做回流),然后調? GPU 繪制,合成圖層,顯示在螢屏上,對于這?部分的內容因為過于底層,還涉及到了硬 件相關的知識,這?就不再繼續展開內容了,

21.2 為什么操作 DOM 慢

想必?家都聽過操作 DOM 性能很差,但是這其中的原因是什么呢?

因為 DOM 是屬于渲染引擎中的東?,? JS ?是 JS 引擎中的東?,當我們通過 JS 操作 DOM 的時候,其實這個操作涉及到了兩個執行緒之間的通信,那么勢必會帶來?些性 能上的損耗,操作 DOM 次數?多,也就等同于?直在進?執行緒之間的通信,并且操作 DOM 可能還會帶來重繪回流的情況,所以也就導致了性能上的問題,

經典?試題:插??萬個 DOM,如何實作??不卡頓?

對于這道題?來說,?先我們肯定不能?次性把?萬個 DOM 全部插?,這樣肯定會造成 卡頓,所以解決問題的重點應該是如何分批次部分渲染 DOM ,?部分?應該可以想到通 過 requestAnimationFrame 的?式去回圈的插? DOM ,其實還有種?式去解決這個問 題:虛擬滾動( virtualized scroller ),

這種技術的原理就是只渲染可視區域內的內容,?可?區域的那就完全不渲染了,當?戶 在滾動的時候就實時去替換渲染的內容

從上圖中我們可以發現,即使串列很?,但是渲染的 DOM 元素永遠只有那么 ?個,當我們滾動??的時候就會實時去更新 DOM ,這個技術就能順利解決 這道經典?試題

21.3 什么情況阻塞渲染

?先渲染的前提是?成渲染樹,所以 HTML 和 CSS 肯定會阻塞渲染,如果你想渲染的越 快,你越應該降低?開始需要渲染的?件??,并且扁平層級,優化選擇器,

然后當瀏覽器在決議到 script 標簽時,會暫停構建 DOM ,完成后才會從暫停的地?重 新開始,也就是說,如果你想?屏渲染的越快,就越不應該在?屏就加載 JS ?件,這也 是都建議將 script 標簽放在 body 標簽底部的原因,

當然在當下,并不是說 script 標簽必須放在底部,因為你可以給 script 標簽添加 defer 或者 async 屬性,

當 script 標簽加上 defer 屬性以后,表示該 JS ?件會并?下載,但是會放到 HTML 決議完成后順序執?,所以對于這種情況你可以把 script 標簽放在任意位置,

對于沒有任何依賴的 JS ?件可以加上 async 屬性,表示 JS ?件下載和決議不會阻 塞渲染,

21.4 重繪(Repaint)和回流(Reflow)

重繪和回流會在我們設定節點樣式時頻繁出現,同時也會很?程度上影響性 能,

  • 重繪是當節點需要更改外觀?不會影響布局的,?如改變 color 就叫稱為重繪
  • 回流是布局或者?何屬性需要改變就稱為回流,
  • 回流必定會發?重繪,重繪不?定會引發回流,回流所需的成本?重繪?的多,改變?節點?的?節點很可能會導致?節點的?系列回流,

以下?個動作可能會導致性能問題:

  • 改變 window ??
  • 改變字體
  • 添加或洗掉樣式
  • ?字改變
  • 定位或者浮動
  • 盒模型

并且很多?不知道的是,重繪和回流其實也和 Eventloop 有關,

  • 當 Eventloop 執?完 Microtasks 后,會判斷 document 是否需要更新,因為瀏覽器是 60Hz 的重繪率,每 16.6ms 才會更新?次,
  • 然后判斷是否有 resize 或者 scroll 事件,有的話會去觸發事件,所以 resize 和scroll 事件也是?少 16ms 才會觸發?次,并且?帶節流功能,
  • 判斷是否觸發了 media query
  • 更新影片并且發送事件
  • 判斷是否有全屏操作事件
  • 執? requestAnimationFrame 回呼
  • 執? IntersectionObserver 回呼,該?法?于判斷元素是否可?,可以?于懶加載上,但是兼容性不好 更新界?
  • 以上就是?幀中可能會做的事情,如果在?幀中有空閑時間,就會去執?requestIdleCallback 回呼

21.5 減少重繪和回流

  1. 使? transform 替代 top

<div class="test"></div>
<style>
.test {
position: absolute;
top: 10px;
width: 100px;
height: 100px;
background: red;
}
</style>
<script>
setTimeout(() => {
// 引起回流
document.querySelector('.test').style.top = '100px'
}, 1000)
</script>
  1. 使? visibility 替換 display: none ,因為前者只會引起重繪,后者會引發回流 (改變了布局)

  2. 不要把節點的屬性值放在?個回圈?當成回圈?的變數

    
        for(let i = 0; i < 1000; i++) {
        // 獲取 offsetTop 會導致回流,因為需要去獲取正確的值
        console.log(document.querySelector('.test').style.offsetTop)
        }
  3. 不要使? table 布局,可能很?的?個?改動會造成整個 table 的重新布局

  4. 影片實作的速度的選擇,影片速度越快,回流次數越多,也可以選擇使?requestAnimationFrame

  5. CSS 選擇符從右往左匹配查找,避免節點層級過多

  6. 將頻繁重繪或者回流的節點設定為圖層,圖層能夠阻?該節點的渲染?為影響別的節點,?如對于 video 標簽來說,瀏覽器會?動將該節點變為圖層,

設定節點為圖層的?式有很多,我們可以通過以下?個常?屬性可以?成新圖層

will-change

video 、 iframe 標簽

?個頁?上有?量的圖?(?型電商?站),加載很慢,你有哪 些?法優化這些圖?的加載,給?戶更好的體驗,

答:

  • 圖?懶加載,在頁?上的未可視區域可以添加?個滾動事件,判斷圖?位置與瀏覽器頂端的距離與頁?的距離,如果前者?于后者,優先加載,
  • 如果為幻燈?、相冊等,可以使?圖?預加載技術,將當前展示圖?的前?張和后?張優先下載,
  • 如果圖?為css圖?,可以使? CSSsprite , SVGsprite , Iconfont 、 Base64 等技術,
  • 如果圖?過?,可以使?特殊編碼的圖?,加載時會先加載?張壓縮的特別厲害的縮略圖,以提??戶體驗,
  • 如果圖?展示區域?于圖?的真實??,則因在服務器端根據業務需要先?進?圖?壓縮,圖?壓縮后??與展示?致,

網頁驗證碼是?嘛的,是為了解決什么安全問題?

答:

  • 區分?戶是計算機還是?的公共全?動程式,可以防?惡意破解密碼、刷票、論壇灌?
  • 有效防??客對某?個特定注冊?戶?特定程式暴?破解?式進?不斷的登陸嘗試

我比較喜歡的面試者

  • 基礎扎實
    • 從多年的經驗看,那些發展好的同學都具備扎實的基礎知識
    • 比如只懂 jQuery 不懂 JavaScript 是不行的哦
    • 如果了解計算機基礎會更好,因為我們將面臨很多非前端技術的問題
  • 主動思考
    • 被動完成任務的同學在這里進步會很慢
    • 你需要有自己的想法,而不是僅僅完成任務
  • 愛學習
    • 前端領域知識淘汰速度很快,所以最好能經常學習和接觸新東西
  • 有深度
    • 遇到問題時多研究背后深層次的原因,而不是想辦法先繞過去
    • 比如追蹤某個 Bug 一直了解它本質的原因
  • 有視野
    • 創新往往來自于不同學科的交集,如果你了解的領域越多,就越有可能有新想法

好啦,本期前端面試題就分享到這里,我們下期見!最后祝愿大家都能順利拿到大廠offer!

孫叫獸 CSDN認證博客專家 HTTPS Node.js JavaScript
多年的開發經驗,開發過APP,小程式,網站及系統,以前從事java相關系統研發,目前從事保險,證券,金融相關的前端全堆疊開發作業,微信公眾號:電商程式員.QQ交流群:426360778

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/239691.html

標籤:其他

上一篇:2020-12-23

下一篇:用js來實作淘寶網競拍的效果圖,怎么實作呢?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more