Ajax 是什麼? 如何建立一個Ajax?javascript
AJAX全稱是Asychronous JavaScript And Xml(異步的 JavaScript 和 XML)
它的做用是用來實現客戶端與服務器端的異步通訊效果,實現頁面的局部刷新,早期的瀏覽器並不能原生支持ajax,可使用隱藏幀(iframe)方式變相實現異步效果,後來的瀏覽器提供了對ajax的原生支持
其主要經過XMLHttpRequest(標準瀏覽器)、ActiveXObject(IE瀏覽器)對象實現異步通訊效果
實現方式(gitee上的案例):
css
var xhr =null;//建立對象 if(window.XMLHttpRequest){ xhr = new XMLHttpRequest(); }else{ xhr = new ActiveXObject("Microsoft.XMLHTTP"); } xhr.open(「方式」,」地址」,」標誌位」);//初始化請求 xhr.setRequestHeader(「」,」」);//設置http頭信息 xhr.onreadystatechange =function(){}//指定回調函數 xhr.send();//發送請求
Ajax的優缺點html
優勢:前端
- 經過異步模式,提高了用戶體驗
- 優化了瀏覽器和服務器之間的傳輸,按需獲取數據,減小沒必要要的數據往返,減小了帶寬佔用
- Ajax在客戶端運行,承擔了一部分原本由服務器承擔的工做,減小了大用戶量下的服務器負載。
缺點:java
- ajax不支持瀏覽器back按鈕
- 安全問題 AJAX暴露了與服務器交互的細節
- 對搜索引擎的支持比較弱
- 破壞了程序的異常機制。
一個頁面從輸入 URL 到頁面加載顯示完成,發生了什麼?git
- 當發送一個 URL 請求時,無論這個 URL 是 Web 頁面的 URL 仍是 Web 頁面上每一個資源的 URL,瀏覽器都會開啓一個線程來處理這個請求,同時在遠程 DNS 服務器上啓動一個 DNS 查詢。這能使瀏覽器得到請求對應的 IP 地址。
- 瀏覽器與遠程 Web 服務器經過 TCP 三次握手協商來創建一個 TCP/IP 鏈接。該握手包括一個同步報文,一個同步-應答報文和一個應答報文,這三個報文在 瀏覽器和服務器之間傳遞。該握手首先由客戶端嘗試創建起通訊,然後服務器應答並接受客戶端的請求,最後由客戶端發出該請求已經被接受的報文。
- 一旦 TCP/IP 鏈接創建,瀏覽器會經過該鏈接向遠程服務器發送 HTTP 的 GET 請求。遠程服務器找到資源並使用 HTTP 響應返回該資源,值爲 200 的 HTTP 響應狀態表示一個正確的響應。
- 此時,Web 服務器提供資源服務,客戶端開始下載資源。
- 後續HTML頁面解析參照前端面試題整合(JS進階篇)(一)的 「html頁面怎麼解析的?它加載順序是什麼?」
JQuery一個對象爲什麼能夠同時綁定多個事件web
低層實現方式是使用addEventListner或attachEvent兼容不一樣的瀏覽器實現事件的綁定,這樣能夠給同一個對象註冊多個事件面試
對頁面某個節點的拖曳ajax
1. 給須要拖拽的節點綁定mousedown, mousemove, mouseup事件
2. mousedown事件觸發後,開始拖拽
3. mousemove時,須要經過event.clientX和clientY獲取拖拽位置,並實時更新位置
4. mouseup時,拖拽結束
5. 須要注意瀏覽器邊界的狀況
數組
function mouseMove(ele, parent) { ele.addEventListener('mousedown', moveHandler); ele.style.position = 'absolute' function moveHandler(e) { if (e.type === 'mousedown') { parent.ele = this; parent.point = { x: e.offsetX, y: e.offsetY } parent.addEventListener('mousemove', moveHandler); parent.addEventListener('mouseup', moveHandler); } else if (e.type === 'mousemove') { this.ele.style.left = e.x - this.point.x + "px"; this.ele.style.top = e.y - this.point.y + "px"; } else if (e.type === 'mouseup') { parent.removeEventListener("mousemove", moveHandler); parent.ele = null; parent.point = null; } } }
new操做符具體幹了什麼
- 建立一個空對象,而且 this 變量引用該對象,同時還繼承了該函數的原型。
- 屬性和方法被加入到 this 引用的對象中。
- 新建立的對象由 this 所引用,而且最後隱式的返回 this
如下是模擬操做:
new TestObj('str')=function(){ let obj={}; //建立一個空對象 obj.__proto__=TestObj.prototype; //把該對象的原型指向構造函數的原型對象,就創建起原型了:obj->Animal.prototype->Object.prototype->null return TestObj.call(obj,arguments);// 綁定this到實例化的對象上 }
前端開發的優化問題
(1) 減小http請求次數:CSS Sprites, JS、CSS源碼壓縮、圖片大小控制合適;網頁Gzip,CDN託管,data緩存 ,圖片服務器。
(2) 前端模板 JS+數據,減小因爲HTML標籤致使的帶寬浪費,前端用變量保存AJAX請求結果,每次操做本地變量,不用請求,減小請求次數
(3) 用innerHTML代替DOM操做,減小DOM操做次數,優化javascript性能。
(4) 當須要設置的樣式不少時設置className而不是直接操做style。
(5) 少用全局變量、緩存DOM節點查找的結果。減小IO讀取操做。
(6) 避免使用CSS Expression(css表達式)又稱Dynamic properties(動態屬性)。
(7) 圖片預加載,將樣式表放在頂部,將腳本放在底部 加上時間戳。
(8) 避免在頁面的主體佈局中使用table,table要等其中的內容徹底下載以後纔會顯示出來,顯示比div+css佈局慢。
fetch和Ajax有什麼不一樣
- XMLHttpRequest 是一個設計粗糙的 API,不符合關注分離(Separation of Concerns)的原則,配置和調用方式很是混亂,並且基於事件的異步模型寫起來也沒有現代的 Promise,generator/yield,async/await 友好
- fetch 是瀏覽器提供的一個新的 web API,它用來代替 Ajax(XMLHttpRequest),其提供了更優雅的接口,更靈活強大的功能。
- Fetch 優勢主要有:語法簡潔,更加語義化,基於標準 Promise 實現,支持 async/await
如何編寫高性能的Javascript
- 使用 DocumentFragment 優化屢次 append
- 經過模板元素 clone,替代 createElement
- 使用一次 innerHTML 賦值代替構建 dom 元素
- 使用 firstChild 和 nextSibling 代替 childNodes 遍歷 dom 元素
- 使用 Array 作爲 StringBuffer ,代替字符串拼接的操做
- 將循環控制量保存到局部變量
- 順序無關的遍歷時,用 while 替代 for
- 將條件分支,按可能性順序從高到低排列
- 在同一條件子的多( >2 )條件分支時,使用 switch 優於 if
- 使用三目運算符替代條件分支
- 須要不斷執行的時候,優先考慮使用 setInterval
定時器setInterval有一個有名函數fn,setInterval(fn,500)與setInterval(fn(),500)有什麼區別?
第一個是重複執行每500毫秒執行一次,後面一個只執行一次。
簡述一下瀏覽器內核
瀏覽器內核又能夠分紅兩部分:渲染引擎和 JS 引擎。它負責取得網頁的內容(HTML、XML、圖像等等)、整理訊息(例如加入 CSS 等),以及計算網頁的顯示方式,而後會輸出至顯示器或打印機。JS 引擎則是解析 Javascript 語言,執行 javascript 語言來實現網頁的動態效果。
JavaScript的數據對象有哪些屬性值?
writable:這個屬性的值是否能夠改
configurable:這個屬性的配置是否能夠刪除,修改
enumerable:這個屬性是否能在for…in循環中遍歷出來或在Object.keys中列舉出來
value:屬性值。
懶加載(瀑布流)的實現原理
意義:懶加載的主要目的是做爲服務器前端優化,減小請求數或延遲請求數實現原理:先加載一部分數據,當觸發某個條件時利用異步加載剩餘的數據,新獲得的數據,不會影響有數據的顯示,同時最大程度上減小服務器的資源消耗
實現方式:
(1)延遲加載,使用setTimeOut或setInterval進行加載延遲
(2)符合某些條件,或觸發了某些事件纔開始異步下載
(3)可視區加載
js實現數組去重
雙層循環,外層循環元素,內層循環時比較值,若是有相同的值則跳過,不相同則push進數組
class MyArray extends Array { constructor() { super(...arguments) } distinct() { var myArr = this, list = [] for (var i = 0; i < myArr.length; i++) { for (var j = i + 1; j < myArr.length; j++) { if (myArr[i] === myArr[j]) { j = ++i; } } list.push(myArr[i]); } return list; } } var _arr = new MyArray(4, 5, 6, 7, 7, 7, 1, 1, 1, 2, 2, 2, 5, 8, 5, 2, 4, 4, 4, 6, 9); console.log(_arr.distinct()); //[7, 1, 8, 5, 2, 4, 6, 9]
利用對象的屬性不能相同的特色進行去重
class MyArray extends Array { constructor() { super(...arguments) } distinct() { var myArr = this, list = [], obj = {} for (var i = 0; i < myArr.length; i++) { obj[myArr[i]] || (obj[myArr[i]] = 1, list.push(myArr[i])) //若是能查找到,證實數組元素重複了 } return list; } } var _arr = new MyArray(4, 5, 6, 7, 7, 7, 1, 1, 1, 2, 2, 2, 5, 8, 5, 2, 4, 4, 4, 6, 9); console.log(_arr.distinct()); //[4, 5, 6, 7, 1, 2, 8, 9]
Set數據結構,它相似於數組,其成員的值都是惟一的
function dedupe(array) { return Array.from(new Set(array)); } console.log(dedupe([1, 1, 2, 3])) //[1,2,3]
實現快速排序和冒泡排序
快速排序:選取位置在數組中間的一個數,而後比它小的放在left[]的一個新數組裏面,比他大的放在right[]的一個新數組裏面,以此類推,重複執行這個過程,利用遞歸的思想,直至執行到left[]和right[]裏面都只有一個數
冒泡排序:兩兩比較,前面的比後面的大,則換位。第一輪list.length-1次,挑出最大的;第二輪list.length-1-1次,挑出第二大的。以此往復
class MyArray extends Array { constructor() { super(...arguments) } quickSort(list) { //快速排序 var myArr = this, listConfig = { midItem: myArr[parseInt(myArr.length / 2)], leftList: new MyArray(), rightList: new MyArray() } if (myArr.length <= 1) { return myArr }; for (var i = 0; i < myArr.length; i++) { myArr[i] < listConfig.midItem ? listConfig.leftList.push(myArr[i]) : myArr[i] > listConfig .midItem ? listConfig.rightList.push(myArr[i]) : ''; } return listConfig.leftList.quickSort().concat([listConfig.midItem], listConfig.rightList .quickSort()); //遞歸 } bubbleSort() { //冒泡排序 for (var i = 0; i < this.length - 1; i++) { for (var j = 0; j < this.length - 1 - i; j++) { if (this[j] > this[j + 1]) { var item = this[j]; this[j] = this[j + 1]; this[j + 1] = item; } } } return this } } var quickSortArray = new MyArray(19, 15, 18, 17, 11, 21, 14, 61, 13, 10, 25); var bubbleSortArray = new MyArray(9, 5, 8, 7, 1, 2, 4, 6, 3, 10, 25); console.log(quickSortArray.quickSort()); //[10, 11, 13, 14, 15, 17, 18, 19, 21, 25, 61] console.log(bubbleSortArray.bubbleSort()); //[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 25]
談談節流和防抖,如何實現
節流:使頻繁執行的函數,定時執行,高頻率函數執行時,使執行率減小,每n秒才能執行一次,打個比方:每隔1秒鐘,會執行5次滾動條滾動事件,我只讓它每一秒執行一次(案例:網站中的返回頂部)
防抖:使頻繁執行的函數,延時執行,高頻率函數執行時,n秒內只執行一次,在事件內屢次執行會延時,打個比方:用戶在輸入框中輸入字符,當用戶一直在輸入時,咱們作個延時,當用戶輸入完畢後會有一段時間停頓,若這個停頓時間大於咱們的咱們延時時間,咱們就進行下一步操做,反之則不進行而且一直延時(案例:搜索引擎搜索輸入框)
區別:對於高頻率執行函數,節流是每隔規定時間都會執行一次,防抖是隻在規定時間外的最後一次執行
實現過程:
var count = 0 class OptimizeEvent { constructor() {} throttle(fn, time) { //節流 var canDo = true return function (e) { if (!canDo) { return false } canDo = false setTimeout(() => { fn.call(this) canDo = true }, time) } } debounce(fn, time) { //防抖 var _timer = null return function () { if (_timer) { clearTimeout(_timer) _timer = null } _timer = setTimeout(fn, time) } } } var _event = new OptimizeEvent() inputBox.addEventListener('input', _event.debounce(function () { showBox.textContent = inputBox.value }, 1000)) document.addEventListener('scroll', _event.throttle(function () { console.log(count++) }, 1000))
談談深拷貝的實現
深拷貝相對淺拷貝不一樣的是,深拷貝內全部引用類型屬性值都是在新開闢的內存地址,被拷貝的原數據發生改變時不會影響複製後的對象。
常見方法
- JSON.parse(),JSON.stringify()
- jQury的$.extend(true,{},obj)
- lodash的_.cloneDeep
- 個人深複製文章
function deepClone(org, tag) { var tag = tag || {}; //初始化要複製的對象 var name = Object.getOwnPropertyNames(org); //獲取該對象的屬性名,以字符串數組返回 for (var i = 0; i < name.length; i++) { //遍歷對象 var desc = Object.getOwnPropertyDescriptor(org, name[i]); //獲取對象的屬性描述對象,無引用關係,返回另外一個對象,改變時原對象不發生變化(複製的關鍵) if (typeof desc.value === 'object' && desc.value !== null) { //若遍歷的每一項非空且爲對象,則爲引用值,則進行下一步 var obj = desc.value.toString() === '[object Object]' ? {} : []; //判斷是數組仍是對象 Object.defineProperty(tag, name[i], { //設置對象屬性值,前三個的值是返回true或false configurable: desc.configurable, //是否可刪除可替換 enumerable: desc.enumerable, //是否可枚舉可遍歷 writable: desc.writable, //是否可寫入 value: obj //對象的值 }); copyObj(desc.value, obj); //再次執行函數 } else { Object.defineProperty(tag, name[i], desc); //不然直接將該對象的屬性值進行復制(原始值) } } return tag; }