前端面試題整合(JS進階篇)(二)

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.    須要注意瀏覽器邊界的狀況



數組

gitee上的案例

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操做符具體幹了什麼

  1. 建立一個空對象,而且 this 變量引用該對象,同時還繼承了該函數的原型。
  2. 屬性和方法被加入到 this 引用的對象中。
  3. 新建立的對象由 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;
    			}
相關文章
相關標籤/搜索