console.log(a) // undefined
var a = 1;
var getNum = function() { a = 2; } function getNum() { a = 3; } console.log(a) // 1 getNum() console.log(a) // 2
這道題主要考查聲明提高和函數聲明先於變量聲明這兩個知識點。在解析這個js片斷的時候是按照這樣的順序:javascript
// 聲明提高
function getNum() { a = 3; } var a; var getNum; // console.log(a); a = 1; getNum = function() { a = 2; } console.log(a) getNum() console.log(a)
這樣就很清晰了,第一次輸出a的時候僅僅是聲明瞭尚未賦值,因此是undefined
。第二次的不用說了。第三次輸出2是由於,變量聲明是沒法覆蓋函數聲明的,一開始getNum
指向一個函數,後來賦值成了一個函數表達式,指向了另外一個函數。css
// 每隔一秒輸出一次i值
for(var i = 0; i < 5; i++){ // TODO }
此題主要考查閉包和js執行機制。如下幾種解法:html
大概1秒輸出一次的版本:java
// 利用當即執行函數造成閉包
(function(i){ setTimeout(function() { console.log(i) }, i * 1000)})(i)
// 利用setTimeout的第三個參數造成閉包 setTimeout(function(i) { console.log(i) }, i * 1000, i)
// 若是不是題目中指定了var,利用ES6的let就簡單多了 for(let i = 1; i < 5; i++) { setTimeout(function(){ console.log(i) }, i * 1000) }
// 再看ES7版本
const sleep = (time) =>
new Promise((resolve, reject) =>
setTimeout(resolve, time)); (async function(){ for(var i = 0; i < 5; i++){ await sleep(1000); console.log(i); } })()
之因此是說是大概,是由於setTimeout
並非延時多少秒後執行函數,而是多少秒後把函數扔進事件隊列中等待執行,若是此時隊列裏有其餘任務 的話那就不是精確的1秒了。node
再看比較精確的1秒版本:react
for(var i =0; i < 5; i++) { var time = new Date(); while(new Date() - time < 1000) { } console.log(i) }
直接阻塞線程一秒鐘,簡單粗暴有木有~web
var a = {}
var b = {
key: "a" } var c = { key: "c" } a[b] = "123"; a[c] = "456"; console.log(a[b]) // 456
這題主要考查對象。其實這裏a[b]
和a[c]
中的b、c都會調用object.prototype.toString()
,都變成了[object Object]
,這樣就與對象中的key值無關了。因此a[b]
和a[c]
的指向是相同的。ajax
var f = function() { var c = "ccc"; return { a: function() { return c; }, b: function(d) { c = d; } } }() console.warn(f.a()) // ccc console.warn(f.c) // undefined console.warn(f.b("www")) // undefined console.warn(f.a()) // www
這題主要考查的是執行上下文中的做用域鏈。咱們要注意到函數表達式後的那個函數執行符——(),它是個當即執行函數,也就是說f是個包含a、b屬性的對象。算法
console.warn(f.a())
當a()的執行上下文被激活時,做用域和變量對象被肯定,c是個自由變量,須要在做用域鏈中向上查找,然受在父級做用域中找到,因此輸出「ccc」。數組
console.warn(f.c)
這個就不用說啦,f中沒有c這個屬性,取不到固然返回undefined
。
console.warn(f.b("www"))
同第一行同樣,修改的是父級做用域中的c,但因爲沒有返回值因此輸出的是undefined。
函數會順着做用域鏈查找當前執行環境不存在的變量,對象則從原型鏈上查找!!!
position: absolute; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%);
display: flex; align-items: center; justify-content: center;
display: table;(父) display: table-cell;(子) text-align: center; vertical-align: middle;
position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: auto;
javascript函數的new關鍵字究竟是幹什麼的
不用建立臨時對象,由於new會幫你作
不用綁定原型,由於new會幫你作
不用return臨時對象,由於new會幫你作
不要給原型想名字了,由於new指定名字爲prototype
對象與原型鏈(__proto__和prototype)
每一個JS對象必定對應一個原型對象,並從原型對象繼承屬性和方法
對象__proto__屬性的值就是他所對應的原型對象
只有函數纔有prototype屬性,當你建立函數時,JS會爲這個函數自動添加prototype屬性,值是一個有constructor屬性的對象,不是空對象,而一旦你把這個函數看成構造函數調用時,那麼JS就會幫你建立該構造函數的實例,實例繼承構造函數prototype的全部屬性和方法
對象的__proto__指向本身構造函數的prototype
Object.prototype是原型鏈的頂端,Object自己是構造函數,繼承了Function.prototype,Function也是對象,繼承了Object.prototype
Object.prototype.proto === null,說明原型鏈到Object.prototype終止
null表示‘沒有對象’,即此處不應有值
Function自己就是函數,Function.__proto__是標準的內置對象Function.prototype,而Function.prototype.__proto__是標準的內置對象Object.prototype
構造函數和原型
原型:每個JS對象(除null外)在建立的時候就會與之關聯另外一個對象,這個對象就是咱們說的原型,每一個對象都會從原型繼承屬性
proto:每個JS對象(除null外)都具備的一個屬性,叫__proto__,這個屬性會指向該對象的原型
constructor:每個原型都有一個constructor屬性指向關聯的構造函數
html中直接綁定
html中綁定事件叫作內聯綁定事件,不利於分離
js中直接綁定
js中直接綁定稱爲賦值綁定函數,缺點是隻能綁定一次
addEventListener
target.addEventListener(type, listener[, useCapture])
target表示要監聽事件的目標對象,能夠是一個文檔上的元素DOM自己,Window或者XMLHttpRequest
type表示事件類型的字符串
listener爲當指定的事件類型發生時被通知到的一個對象
useCapture爲設置事件的捕獲或者冒泡
true爲捕獲,false爲冒泡(默認)
addEventListener能夠給同一個dom元素綁定多個函數,而且執行順序按照綁定順序執行,且執行順序與useCapture無關
給一個dom元素綁定同一個函數,最多隻能綁定useCapture類型不一樣的兩次
addEventListener只支持到IE9,爲兼容性考慮,在兼容IE8及一下瀏覽器能夠用attachEvent函數,和addEventListener函數表現同樣,但它綁定函數的this會指向全局
事件的解綁
經過dom的on***屬性設置的事件,能夠用dom.onclick = null
來解綁
經過addEventListener綁定的事件可使用removeEventListener來解綁,接受參數同樣
對於使用removeEventListener函數解綁事件,須要傳入的listener,useCapture和addEventListener徹底一致才能夠解綁事件
事件冒泡
事件開始時由最具體的元素接受,而後逐級向上傳播到較爲不具體的節點
事件捕獲
事件捕獲的思想是不太具體的DOM節點應該更早接收到事件,而最具體的節點應該最後接收到事件,與事件冒泡順序相反
DOM事件流
DOM事件流包括三個階段,事件捕獲階段,處於目標階段,事件冒泡階段,首先發生的是事件捕獲,爲截獲事件提供機會,而後是實際的目標接受事件,最後一個階段是事件冒泡階段,能夠在這個階段對事件做出響應
stopPropagation()和stopImmediatePropagation()
stopPropagation()既能夠阻止事件冒泡,也能夠阻止事件捕獲,也能夠阻止處於目標階段
stopImmediatePropagation()既能夠阻止事件冒泡,也能夠阻止事件捕獲,還會阻止該元素其餘事件的發生
http2.0和http1.1的區別
由於全部的http2的請求都在一個TCP鏈接上,因此在http1中的自動化合並文件和Sprite合圖等資源合併減小請求的優化手段對於http2來講是沒有效果的
二進制分幀
http2在應用層和傳輸層之間增長一個二進制分幀層,http2會將全部傳輸的信息分割成更小的消息和幀,並對他們採用二進制格式的編碼,其中http1的首部信息會被封裝成Headers幀,而咱們的request body則封裝到Data幀裏面
首部壓縮
http請求和響應都是由狀態行,請求/響應頭部,消息主題三部分組成,通常而言,消息主題都會通過gzip壓縮,或者自己傳輸的就是壓縮後的二進制文件,但狀態行和頭部卻沒有通過任何壓縮,直接以純文本傳輸,浪費流量資源
原理:頭部壓縮須要在支持http2的瀏覽器和服務端之間,維護一份相同的靜態字典,包含常見的頭部名稱與值的組合,維護一份相同的動態字典,能夠動態的添加內容,支持基於靜態哈夫曼碼錶的哈夫曼編碼
http2支持服務器推送
服務端推送是一種在客戶端請求以前發送數據的機制,當代網頁使用了許多資源:html,樣式表,腳本等,在http1.x中這些資源每個都必須明確的請求,這多是一個很慢的過程,由於服務器必須等待瀏覽器作的每個請求,網絡常常是空閒和未充分使用的
爲了改善延遲,http2引入了server push,它容許服務端推送資源給瀏覽器,在瀏覽器明確請求以前,一個服務器常常知道一個頁面須要更多的附加資源,在他響應瀏覽器第一個請求時,能夠開始推送這些資源,這容許服務端去徹底充分利用一個可能空閒的網絡,改善頁面加載的時間
有了http2的服務端推送,http1時代的內嵌資源的優化手段也變得沒有意義了,使用服務端推送更高效,由於客戶端能夠緩存起來,甚至能夠不一樣頁面之間共享
並行雙向字節流的請求和響應
在http2上,客戶端和服務端能夠把http消息分解成回不依賴的幀,而後亂序發送,最後再在另外一端把她們從新組合起來,同一連接上能夠有多個不一樣方向上的數據在傳輸,客戶端能夠一邊亂序發送stream,也能夠一邊接收着服務端的響應,在服務端同理
把http消息分解爲獨立的幀,交錯發送,而後在另外一端從新組裝是http2最重要的一項加強,這個機制會在整個web技術棧中引起一系列的連鎖反應,從而帶來巨大的性能提高,由於
http2的請求優先級
每一個http2流裏面有個優先值,這個優先值肯定着客戶端和服務端處理不一樣的流採起不一樣的優先級策略,高優先級的流應該優先發送,但又不是絕對的準守,可能又會引入首隊阻塞的問題,高優先級的請求慢致使阻塞其餘文件的交付,分配處理資源和客戶端與服務器間的帶寬,不一樣優先級的混合是必須的
https
http協議傳輸的數據都是未加密的,也就是明文的,所以使用http協議傳輸隱私信息很是不安全,爲了保證這些隱私數據能加密傳輸,因而網景公司設計了SSL協議用於對http協議傳輸的數據進行加密,從而誕生了https,如今的https使用的都是TSL協議
https在傳輸數據以前須要客戶端和服務端之間進行一次握手,在握手的過程當中將確立雙方加密傳輸數據的密碼信息,TSL/SSL協議不只僅是一套加密傳輸的協議,TSL/SSL中使用了非對稱加密,對稱加密以及hash算法
握手過程:
瀏覽器將本身支持的一套加密規則發送給網站
網站從中選出一組加密算法和hash算法,並將本身的身份信息以證書的形式發回給瀏覽器,證書裏面包含了網站地址,加密公鑰,以及證書的頒發機構等信息
得到網站證書後瀏覽器要作如下工做
網站接收瀏覽器發來的數據以後要作如下工做
瀏覽器解密並計算握手信息的hash,若是與服務端發來的hash一致,此時握手過程結束,以後全部的通訊數據將由以前瀏覽器生成的隨機密碼並利用對稱加密算法進行加密
這裏瀏覽器與網站互相發送加密的握手信息並驗證,目的是爲了保證雙發都得到了一致的密碼,而且能夠正常的加密解密數據
其中非對稱加密算法用於在握手過程當中加密生成的密碼,對稱加密算法用於對真正傳輸的數據進行加密,而hash算法用於驗證數據的完整性
因爲瀏覽器生成的密碼是整個數據加密的關鍵,所以在傳輸的時候使用了非對稱加密算法對其進行加密,非對稱加密算法會生成公鑰和私鑰,公鑰只能用於加密數據,所以能夠隨意傳輸,而網站的私鑰用於對數據進行解密,因此網站都會很是當心的保管本身的私鑰,防止泄漏
TSL握手的過程當中若是有任何錯誤,都會使加密鏈接斷開,從而阻止了隱私數據的傳輸,正是因爲https很是的安全,攻擊者沒法從中找到下手的地方,因而更多的是採用了假證書的手法來欺騙客戶端,從而獲取明文信息
webSocket概述
http協議是一種無狀態的協議,要實現有狀態的會話必須藉助一些外部機制如session和cookie,這或多或少或帶來一些不便,尤爲是服務端和客戶端須要實時交換數據的時候
webSocket容許服務器和客戶端進行全雙工通訊,傳統的http是單工通訊的,它只容許客戶端向服務端發出請求,服務端被動返回數據,而不能主動向客戶端傳遞數據
webSocket的請求頭部
Connection: Upgrade //通訊協議提高 Upgrade: websocket //傳輸協議升級爲websocket Sec-WebSocket-Key: ********** //握手協議密鑰,base64位編碼的16字節的隨機字符串
webSocket的響應頭部
Connection: Upgrade //通訊協議提高
Upgrade: websocket //傳輸協議升級爲websocket
Sec-WebSocket-Accept: ********** //將客戶上報的Sec-WebSocket-Key和一段GUID(258EAFA5-E914-47DA-95CA-C5AB0DC85B11)進行拼接,再將這個拼接的字符串作SHA-1 hash計算,而後再把獲得的結果經過base64加密,最後再返回給客戶端
WebSocket,ajax輪詢和long poll
說說對洗牌算法的理解和如何驗證其正確性
洗牌算法以前沒了解過,剛面到的時候好蒙,閒話很少說,這裏說下洗牌算法的js實現
Fisher-Yates
這是最經典的洗牌算法,其算法思想是從原數組中隨機抽取一個新的元素到新數組中
從還沒處理的數組(假如還剩n個)中,產生一個[0,n]之間的隨機數random
從剩下的n個元素中把第random個元素取出到新數組中
刪除原數組第random個元素
重複第2 3步直到全部的元素取完
最終返回一個新的打亂的數組
代碼實現
function shufle(arr){ var result = [], random; while(arr.length > 0){ random = Math.floor(Math.random() * arr.length); result.push(arr[random]) arr.splice(random, 1) } return result; }
這種算法的時間複雜度是O(n2)
說一下你對事件委託和事件代理的理解?
什麼是事件委託?它還有一個名字叫事件代理,JavaScript高級程序設計上講:事件委託就是利用事件冒泡,只指定一個事件處理程序,就能夠管理某一類型的全部事件,當咱們須要對不少元素添加事件的時候,能夠經過事件添加到他們的父節點二將時間委託給父節點來觸發處理函數
爲何要使用事件委託?
通常來講,dom須要有事件處理程序,咱們都會直接給它設置事件處理程序就行了,那若是是不少的dom須要添加事件處理呢?好比咱們這裏有100個li,每一個li都有相同的click事件,那麼咱們會用for循環的方法來遍歷全部的li,而後給他們添加事件,那麼這樣會存在什麼問題呢?
在JavaScript中,添加到頁面上的事件處理程序的數量將直接關聯到頁面總體的運行性能,由於須要不斷的與dom節點進行交互,訪問dom的次數越多,引發瀏覽器重繪與重排的次數就越多,就會延長整個頁面交互就緒時間,這就是爲何性能優化的主要思想是減小dom操做的緣由,若是使用事件委託,就會將全部的操做放到js程序裏面,與dom的操做就只須要交互一次,這樣就能大大的減小與dom的交互次數,提升性能
每一個函數都是一個對象,是對象就會佔用內存,內存佔用率就越大,天然性能就差了,好比上面的100個li,就要佔用100個內存空間,若是是1000個,10000個呢,若是使用事件委託,那麼咱們就能夠只對它的父級這一個對象(若是隻有一個父級)進行操做,這樣咱們就須要一個內存空間就夠了,是否是省了不少,天然性能就會更好
事件委託的原理?
事件委託是利用事件的冒泡機制來實現的,何爲事件冒泡呢?這裏介紹下瀏覽器dom事件處理的過程,dom2.0模型將事件流程分爲三個階段:事件捕獲階段,事件目標階段,事件冒泡階段。
事件捕獲:當某個元素觸發某個事件,頂層對象document就會發出一個事件流,隨着dom樹的節點向目標元素節點流去,直到到達事件真正發生的目標元素,在這個過程當中,事件相應的監聽函數是不會被觸發的
事件目標:當到達目標元素以後,執行目標元素該事件相應的處理函數,若是沒有綁定監聽函數,那就不執行
事件冒泡:從目標元素開始,往頂層元素傳播,途中若是有節點綁定了相應的事件處理函數,這些函數都會被一次觸發,若是想阻止事件冒泡,可使用event.stopPropgation()或者event.cancelBubble=true來阻止事件的冒泡傳播
事件委託怎麼實現:
Event對象提供了一個屬性叫target,能夠返回事件的目標節點,咱們稱爲事件源,也就是說,target就能夠表示爲當前事件操做的dom,可是不是真正操做的dom,固然,這個是有兼容性的,標準瀏覽器用event.target,IE瀏覽器用event.srcElement,此時知識獲取了當前節點的位置,並不知道是什麼節點名稱,這裏咱們用nodeName來獲取具體是什麼標籤名,這個返回的是一個大寫的,通常轉化爲小寫再進行比較
若是你想將事件委託給父元素來處理,但每一個子元素的事件內容又不相同時,這裏咱們能夠給每一個子元素添加一個惟一的key來做標識,而後在父元素中對其進行分別的處理
const list = document.querySelector('#list) const lists = list.querySelector('#list > li') for(let i=0; i<lists.length; i++){ lists[i].dataset.key = 'list-' + i } list.addEventListener('click',function(e){ const event = e || window.event const target = event.target || event.srcElement if(target.nodeName.toLocaleLowerCase() === 'li'){ switch(target.dataset.key){ case 'list-1': do something-1 break case 'list-2': do something-2 break ... default: do something-3 break } } })
說一下你對css重繪和重排的理解,兩個有什麼不一樣?
這一題考的實際上是網頁性能的問題,咱們先理解下對網頁性能產生影響到因素,瞭解下網頁是怎麼生成的
網頁生成的過程,大體能夠分爲五步:
在這五步裏面,第一步和第三步都很是快耗時的是第四步和第五步
其中生成佈局flow和繪製paint這兩步,合稱爲渲染render
重繪和重排
網頁生成的時候,至少會渲染一次,用戶訪問的過程當中,還會不斷的從新渲染
如下三種狀況,會致使網頁從新渲染
從新渲染,就須要從新生成佈局和從新繪製,前者叫作重排reflow,後者交貨重繪repaint
須要注意的是,重繪不必定須要重排,好比改變某個網頁元素的顏色,就只會觸發重繪,不會觸發重排,由於佈局沒有改變,可是,重排必定會致使重繪,好比改變一個網頁元素的位置,就會同時觸發重排和重繪,由於佈局改變了
對於性能的影響
重繪和重排會不斷觸發,這是不可避免的,可是,他們是很是耗費資源的,是致使網頁性能低下根本緣由
要提升網頁性能,就是要下降重排和重繪的頻率和成本,儘可能少觸發從新渲染
提升性能的九個技巧