<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                除 ES 基礎之外,Web 前端經常會用到一些跟瀏覽器相關的 API,接下來我們一起梳理一下。 </br> ## 知識點梳理 * BOM 操作 * DOM 操作 * 事件綁定 * Ajax * 存儲 </br> ## BOM BOM(瀏覽器對象模型)是瀏覽器本身的一些信息的設置和獲取,例如獲取瀏覽器的寬度、高度,設置讓瀏覽器跳轉到哪個地址。 * `navigator` * `screen` * `location` * `history` 這些對象就是一堆非常簡單粗暴的 API,沒任何技術含量,講起來一點意思都沒有,大家去 MDN 或者 w3school 這種網站一查就都明白了。面試的時候,面試官基本不會出太多這方面的題目,因為只要基礎知識過關了,這些 API 即便你記不住,上網一查也都知道了。下面列舉一下常用功能的代碼示例 獲取瀏覽器特性(即俗稱的`UA`)然后識別客戶端,例如判斷是不是 Chrome 瀏覽器 ``` var ua = navigator.userAgent var isChrome = ua.indexOf('Chrome') console.log(isChrome) ``` 獲取屏幕的寬度和高度 ``` console.log(screen.width) console.log(screen.height) ``` 獲取網址、協議、path、參數、hash 等 ``` // 例如當前網址是 https://juejin.im/timeline/frontend?a=10&b=10#some console.log(location.href) // https://juejin.im/timeline/frontend?a=10&b=10#some console.log(location.protocol) // https: console.log(location.pathname) // /timeline/frontend console.log(location.search) // ?a=10&b=10 console.log(location.hash) // #some ``` 另外,還有調用瀏覽器的前進、后退功能等 ``` history.back() history.forward() ``` </br> ## DOM > 題目:DOM 和 HTML 區別和聯系 ### 什么是 DOM 講 DOM 先從 HTML 講起,講 HTML 先從 XML 講起。XML 是一種可擴展的標記語言,所謂可擴展就是它可以描述任何結構化的數據,它是一棵樹! ``` <?xml version="1.0" encoding="UTF-8"?> <note> <to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body>Don't forget me this weekend!</body> <other> <a></a> <b></b> </other> </note> ``` HTML 是一個有既定標簽標準的 XML 格式,標簽的名字、層級關系和屬性,都被標準化(否則瀏覽器無法解析)。同樣,它也是一棵樹。 ``` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div> <p>this is p</p> </div> </body> </html> ``` 我們開發完的 HTML 代碼會保存到一個文檔中(一般以`.html`或者`.htm`結尾),文檔放在服務器上,瀏覽器請求服務器,這個文檔被返回。因此,最終瀏覽器拿到的是一個文檔而已,文檔的內容就是 HTML 格式的代碼。 但是瀏覽器要把這個文檔中的 HTML 按照標準渲染成一個頁面,此時瀏覽器就需要將這堆代碼處理成自己能理解的東西,也得處理成 JS 能理解的東西,因為還得允許 JS 修改頁面內容呢。 基于以上需求,瀏覽器就需要把 HTML 轉變成 DOM,HTML 是一棵樹,DOM 也是一棵樹。對 DOM 的理解,可以暫時先拋開瀏覽器的內部因素,先從 JS 著手,即可以認為 DOM 就是 JS 能識別的 HTML 結構,一個普通的 JS 對象或者數組。 ![DOM圖示](https://user-gold-cdn.xitu.io/2018/2/23/161c1050cf00d5bd?w=2102&h=1612&f=png&s=806057) ### 獲取 DOM 節點 最常用的 DOM API 就是獲取節點,其中常用的獲取方法如下面代碼示例: ``` // 通過 id 獲取 var div1 = document.getElementById('div1') // 元素 // 通過 tagname 獲取 var divList = document.getElementsByTagName('div') // 集合 console.log(divList.length) console.log(divList[0]) // 通過 class 獲取 var containerList = document.getElementsByClassName('container') // 集合 // 通過 CSS 選擇器獲取 var pList = document.querySelectorAll('p') // 集合 ``` > 題目:property 和 attribute 的區別是什么? ### property DOM 節點就是一個 JS 對象,它符合之前講述的對象的特征 —— 可擴展屬性,因為 DOM 節點本質上也是一個 JS 對象。因此,如下代碼所示,`p`可以有`style`屬性,有`className` `nodeName` `nodeType`屬性。注意,**這些都是 JS 范疇的屬性,符合 JS 語法標準的**。 ``` var pList = document.querySelectorAll('p') var p = pList[0] console.log(p.style.width) // 獲取樣式 p.style.width = '100px' // 修改樣式 console.log(p.className) // 獲取 class p.className = 'p1' // 修改 class // 獲取 nodeName 和 nodeType console.log(p.nodeName) console.log(p.nodeType) ``` ### attribute property 的獲取和修改,是直接改變 JS 對象,而 attribute 是直接改變 HTML 的屬性,兩種有很大的區別。attribute 就是對 HTML 屬性的 get 和 set,和 DOM 節點的 JS 范疇的 property 沒有關系。 ``` var pList = document.querySelectorAll('p') var p = pList[0] p.getAttribute('data-name') p.setAttribute('data-name', 'juejin') p.getAttribute('style') p.setAttribute('style', 'font-size:30px;') ``` 而且,get 和 set attribute 時,還會觸發 DOM 的查詢或者重繪、重排,頻繁操作會影響頁面性能。 > 題目:DOM 操作的基本 API 有哪些? ### DOM 樹操作 新增節點 ``` var div1 = document.getElementById('div1') // 添加新節點 var p1 = document.createElement('p') p1.innerHTML = 'this is p1' div1.appendChild(p1) // 添加新創建的元素 // 移動已有節點。注意,這里是“移動”,并不是拷貝 var p2 = document.getElementById('p2') div1.appendChild(p2) ``` 獲取父元素 ``` var div1 = document.getElementById('div1') var parent = div1.parentElement ``` 獲取子元素 ``` var div1 = document.getElementById('div1') var child = div1.childNodes ``` 刪除節點 ``` var div1 = document.getElementById('div1') var child = div1.childNodes div1.removeChild(child[0]) ``` 還有其他操作的API,例如獲取前一個節點、獲取后一個節點等,但是面試過程中經常考到的就是上面幾個。 </br> ## 事件 ### 事件綁定 普通的事件綁定寫法如下: ``` var btn = document.getElementById('btn1') btn.addEventListener('click', function (event) { // event.preventDefault() // 阻止默認行為 // event.stopPropagation() // 阻止冒泡 console.log('clicked') }) ``` 為了編寫簡單的事件綁定,可以編寫通用的事件綁定函數。這里雖然比較簡單,但是會隨著后文的講解,來繼續完善和豐富這個函數。 ``` // 通用的事件綁定函數 function bindEvent(elem, type, fn) { elem.addEventListener(type, fn) } var a = document.getElementById('link1') // 寫起來更加簡單了 bindEvent(a, 'click', function(e) { e.preventDefault() // 阻止默認行為 alert('clicked') }) ``` 最后,**如果面試被問到 IE 低版本兼容性問題,我勸你果斷放棄這份工作機會**。現在互聯網流量都在 App 上, IE 占比越來越少,再去為 IE 浪費青春不值得,要盡量去做 App 相關的工作。 > 題目:什么是事件冒泡? ### 事件冒泡 ``` <body> <div id="div1"> <p id="p1">激活</p> <p id="p2">取消</p> <p id="p3">取消</p> <p id="p4">取消</p> </div> <div id="div2"> <p id="p5">取消</p> <p id="p6">取消</p> </div> </body> ``` 對于以上 HTML 代碼結構,要求點擊`p1`時候進入激活狀態,點擊其他任何`<p>`都取消激活狀態,如何實現?代碼如下,注意看注釋: ``` var body = document.body bindEvent(body, 'click', function (e) { // 所有 p 的點擊都會冒泡到 body 上,因為 DOM 結構中 body 是 p 的上級節點,事件會沿著 DOM 樹向上冒泡 alert('取消') }) var p1 = document.getElementById('p1') bindEvent(p1, 'click', function (e) { e.stopPropagation() // 阻止冒泡 alert('激活') }) ``` 如果我們在`p1` `div1` `body`中都綁定了事件,它是會根據 DOM 的結構來冒泡,從下到上挨個執行的。但是我們使用`e.stopPropagation()`就可以阻止冒泡 > 題目:如何使用事件代理?有何好處? ### 事件代理 我們設定一種場景,如下代碼,一個`<div>`中包含了若干個`<a>`,而且還能繼續增加。那如何快捷方便地為所有`<a>`綁定事件呢? ``` <div id="div1"> <a href="#">a1</a> <a href="#">a2</a> <a href="#">a3</a> <a href="#">a4</a> </div> <button>點擊增加一個 a 標簽</button> ``` 這里就會用到事件代理。我們要監聽`<a>`的事件,但要把具體的事件綁定到`<div>`上,然后看事件的觸發點是不是`<a>`。 ``` var div1 = document.getElementById('div1') div1.addEventListener('click', function (e) { // e.target 可以監聽到觸發點擊事件的元素是哪一個 var target = e.target if (e.nodeName === 'A') { // 點擊的是 <a> 元素 alert(target.innerHTML) } }) ``` 我們現在完善一下之前寫的通用事件綁定函數,加上事件代理。 ``` function bindEvent(elem, type, selector, fn) { // 這樣處理,可接收兩種調用方式 bindEvent(div1, 'click', 'a', function () {...}) 和 bindEvent(div1, 'click', function () {...}) 這兩種 if (fn == null) { fn = selector selector = null } // 綁定事件 elem.addEventListener(type, function (e) { var target if (selector) { // 有 selector 說明需要做事件代理 // 獲取觸發時間的元素,即 e.target target = e.target // 看是否符合 selector 這個條件 if (target.matches(selector)) { fn.call(target, e) } } else { // 無 selector ,說明不需要事件代理 fn(e) } }) } ``` 然后這樣使用,簡單很多。 ``` // 使用代理,bindEvent 多一個 'a' 參數 var div1 = document.getElementById('div1') bindEvent(div1, 'click', 'a', function (e) { console.log(this.innerHTML) }) // 不使用代理 var a = document.getElementById('a1') bindEvent(div1, 'click', function (e) { console.log(a.innerHTML) }) ``` 最后,使用代理的優點如下: * 使代碼簡潔 * 減少瀏覽器的內存占用 </br> ## Ajax ### XMLHttpRequest > 題目:手寫 XMLHttpRequest 不借助任何庫 這是很多奇葩的、個性的面試官經常用的手段。這種考查方式存在很多爭議,但是你不能完全說它是錯誤的,畢竟也是考查對最基礎知識的掌握情況。 ``` var xhr = new XMLHttpRequest() xhr.onreadystatechange = function () { // 這里的函數異步執行,可參考之前 JS 基礎中的異步模塊 if (xhr.readyState == 4) { if (xhr.status == 200) { alert(xhr.responseText) } } } xhr.open("GET", "/api", false) xhr.send(null) ``` 當然,使用 jQuery、Zepto 或 Fetch 等庫來寫就更加簡單了,這里不再贅述。 ### 狀態碼說明 上述代碼中,有兩處狀態碼需要說明。`xhr.readyState`是瀏覽器判斷請求過程中各個階段的,`xhr.status`是 HTTP 協議中規定的不同結果的返回狀態說明。 `xhr.readyState`的狀態碼說明: * 0 -代理被創建,但尚未調用 `open()` 方法。 * 1 -`open()` 方法已經被調用。 * 2 -`send()` 方法已經被調用,并且頭部和狀態已經可獲得。 * 3 -下載中, `responseText` 屬性已經包含部分數據。 * 4 -下載操作已完成 > 題目:HTTP 協議中,response 的狀態碼,常見的有哪些? `xhr.status`即 HTTP 狀態碼,有 `2xx` `3xx` `4xx` `5xx` 這幾種,比較常用的有以下幾種: * `200` 正常 * `3xx` * `301` 永久重定向。如`http://xxx.com`這個 GET 請求(最后沒有`/`),就會被`301`到`http://xxx.com/`(最后是`/`) * `302` 臨時重定向。臨時的,不是永久的 * `304` 資源找到但是不符合請求條件,不會返回任何主體。如發送 GET 請求時,head 中有`If-Modified-Since: xxx`(要求返回更新時間是`xxx`時間之后的資源),如果此時服務器 端資源未更新,則會返回`304`,即不符合要求 * `404` 找不到資源 * `5xx` 服務器端出錯了 看完要明白,為何上述代碼中要同時滿足`xhr.readyState == 4`和`xhr.status == 200`。 ### Fetch API 目前已經有一個獲取 HTTP 請求更加方便的 API:`Fetch`,通過`Fetch`提供的`fetch()`這個全局函數方法可以很簡單地發起異步請求,并且支持`Promise`的回調。但是 Fetch API 是比較新的 API,具體使用的時候還需要查查 [caniuse](https://caniuse.com/),看下其瀏覽器兼容情況。 看一個簡單的例子: ``` fetch('some/api/data.json', { method:'POST', //請求類型 GET、POST headers:{}, // 請求的頭信息,形式為 Headers 對象或 ByteString body:{}, //請求發送的數據 blob、BufferSource、FormData、URLSearchParams(get 或head 方法中不能包含 body) mode:'', //請求的模式,是否跨域等,如 cors、 no-cors 或 same-origin credentials:'', //cookie 的跨域策略,如 omit、same-origin 或 include cache:'', //請求的 cache 模式: default、no-store、reload、no-cache、 force-cache 或 only-if-cached }).then(function(response) { ... }); ``` `Fetch` 支持`headers`定義,通過`headers`自定義可以方便地實現多種請求方法( PUT、GET、POST 等)、請求頭(包括跨域)和`cache`策略等;除此之外還支持 response(返回數據)多種類型,比如支持二進制文件、字符串和`formData`等。 ### 跨域 > 題目:如何實現跨域? 瀏覽器中有 **同源策略** ,即一個域下的頁面中,無法通過 Ajax 獲取到其他域的接口。例如有一個接口`http://m.juejin.com/course/ajaxcourserecom?cid=459`,你自己的一個頁面`http://www.yourname.com/page1.html`中的 Ajax 無法獲取這個接口。這正是命中了“同源策略”。如果瀏覽器哪些地方忽略了同源策略,那就是瀏覽器的安全漏洞,需要緊急修復。 url 哪些地方不同算作跨域? * 協議 * 域名 * 端口 但是 HTML 中幾個標簽能逃避過同源策略——`<script src="xxx">`、`<img src="xxxx"/>`、`<link href="xxxx">`,這三個標簽的`src/href`可以加載其他域的資源,不受同源策略限制。 因此,這使得這三個標簽可以做一些特殊的事情。 * `<img>`可以做打點統計,因為統計方并不一定是同域的,在講解 JS 基礎知識異步的時候有過代碼示例。除了能跨域之外,`<img>`幾乎沒有瀏覽器兼容問題,它是一個非常古老的標簽。 * `<script>`和`<link>`可以使用 CDN,CDN 基本都是其他域的鏈接。 * 另外`<script>`還可以實現 JSONP,能獲取其他域接口的信息,接下來馬上講解。 但是請注意,所有的跨域請求方式,最終都需要信息提供方來做出相應的支持和改動,也就是要經過信息提供方的同意才行,否則接收方是無法得到它們的信息的,瀏覽器是不允許的。 ### 解決跨域 - JSONP 首先,有一個概念你要明白,例如訪問`http://coding.m.juejin.com/classindex.html`的時候,服務器端就一定有一個`classindex.html`文件嗎?—— 不一定,服務器可以拿到這個請求,動態生成一個文件,然后返回。 同理,`<script src="http://coding.m.juejin.com/api.js">`也不一定加載一個服務器端的靜態文件,服務器也可以動態生成文件并返回。OK,接下來正式開始。 例如我們的網站和掘金網,肯定不是一個域。我們需要掘金網提供一個接口,供我們來獲取。首先,我們在自己的頁面這樣定義 ``` <script> window.callback = function (data) { // 這是我們跨域得到信息 console.log(data) } </script> ``` 然后掘金網給我提供了一個`http://coding.m.juejin.com/api.js`,內容如下(之前說過,服務器可動態生成內容) ``` callback({x:100, y:200}) ``` 最后我們在頁面中加入`<script src="http://coding.m.juejin.com/api.js"></script>`,那么這個js加載之后,就會執行內容,我們就得到內容了。 ### 解決跨域 - 服務器端設置 http header 這是需要在服務器端設置的,作為前端工程師我們不用詳細掌握,但是要知道有這么個解決方案。而且,現在推崇的跨域解決方案是這一種,比 JSONP 簡單許多。 ``` response.setHeader("Access-Control-Allow-Origin", "http://m.juejin.com/"); // 第二個參數填寫允許跨域的域名稱,不建議直接寫 "*" response.setHeader("Access-Control-Allow-Headers", "X-Requested-With"); response.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS"); // 接收跨域的cookie response.setHeader("Access-Control-Allow-Credentials", "true"); ``` </br> ## 存儲 > 題目:cookie 和 localStorage 有何區別? ### cookie cookie 本身不是用來做服務器端存儲的(計算機領域有很多這種“狗拿耗子”的例子,例如 CSS 中的 float),它是設計用來在服務器和客戶端進行信息傳遞的,因此我們的每個 HTTP 請求都帶著 cookie。但是 cookie 也具備瀏覽器端存儲的能力(例如記住用戶名和密碼),因此就被開發者用上了。 使用起來也非常簡單,`document.cookie = ....`即可。 但是 cookie 有它致命的缺點: * 存儲量太小,只有 4KB * 所有 HTTP 請求都帶著,會影響獲取資源的效率 * API 簡單,需要封裝才能用 ### localStorage 和 sessionStorage 后來,HTML5 標準就帶來了`sessionStorage`和`localStorage`,先拿`localStorage`來說,它是專門為了瀏覽器端緩存而設計的。其優點有: * 存儲量增大到 5MB * 不會帶到 HTTP 請求中 * API 適用于數據存儲 `localStorage.setItem(key, value)` `localStorage.getItem(key)` `sessionStorage`的區別就在于它是根據 session 過去時間而實現,而`localStorage`會永久有效,應用場景不同。例如,一些需要及時失效的重要信息放在`sessionStorage`中,一些不重要但是不經常設置的信息,放在`localStorage`中。 另外告訴大家一個小技巧,針對`localStorage.setItem`,使用時盡量加入到`try-catch`中,某些瀏覽器是禁用這個 API 的,要注意。 </br> ## 小結 本小節總結了 W3C 標準中 Web-API 部分,面試中常考的知識點,這些也是日常開發中最常用的 API 和知識。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看