一: 什么是防抖和節(jié)流?有什么區(qū)別?
防抖: 觸發(fā)高頻事件n秒后函數(shù)只會執(zhí)行一次,但在n秒內(nèi)高頻事件如果再次觸發(fā),則重新計時(一般在input獲取輸入值之類的會使用到)
節(jié)流:高頻事件觸發(fā)在n秒內(nèi),無論觸發(fā)多少次,都只執(zhí)行一次(一般用在用戶獲取驗證碼,滾動請求數(shù)據(jù)之類會使用到, 比如在頁面的無限加載場景下,我們需要用戶在滾動頁面時,每n秒發(fā)一次 Ajax 請求,而不是在用戶停下滾動頁面操作時才去請求數(shù)據(jù)。)
二:JS 異步解決方案的發(fā)展歷程以及優(yōu)缺點?
- 回調(diào)函數(shù)(callback)
優(yōu)點:解決了同步的問題(只要有一個任務(wù)耗時很長,后面的任務(wù)都必須排隊等著,會拖延整個程序的執(zhí)行。)
缺點:回調(diào)地獄,不能用 try catch 捕獲錯誤,不能 return
setTimeout(() => {
// callback 函數(shù)體
}, 1000)
2.Promise
優(yōu)點:解決了回調(diào)地獄的問題
缺點:無法取消 Promise ,錯誤需要通過回調(diào)函數(shù)來捕獲
Generator
優(yōu)點:可以控制函數(shù)的執(zhí)行Async/await
優(yōu)點是:代碼清晰,不用像 Promise 寫一大堆 then 鏈,處理了回調(diào)地獄的問題
缺點:await 將異步代碼改造成同步代碼,如果多個異步操作沒有依賴性而使用 await 會導(dǎo)致性能上的降低。
三:http2 的多路復(fù)用?
1.HTTP2采用二進制格式傳輸,取代了HTTP1.x的文本格式,二進制格式解析更高效。同域名下所有通信都在單個連接上完成,消除了因多個 TCP 連接而帶來的延時和內(nèi)存消耗。
2.在 HTTP/2 中,有兩個非常重要的概念,分別是幀(frame)和流(stream)。
幀代表著最小的數(shù)據(jù)單位,每個幀會標識出該幀屬于哪個流,流也就是多個幀組成的數(shù)據(jù)流。
多路復(fù)用,就是在一個 TCP 連接中可以存在多條流。換句話說,也就是可以發(fā)送多個請求,對端可以通過幀中的標識知道屬于哪個請求。通過這個技術(shù),可以避免 HTTP 舊版本中的隊頭阻塞問題,極大的提高傳輸性能。
四: TCP 三次握手和四次揮手的理解?
TCP三次握手:
1、客戶端發(fā)送syn(在連接建立時用來同步序號)包到服務(wù)器,等待服務(wù)器確認接收。
2、服務(wù)器確認接收syn包并確認客戶的syn,并發(fā)送回來一個syn+ack(TCP協(xié)議規(guī)定,只有ACK=1時有效)的包給客戶端。
3、客戶端確認接收服務(wù)器的syn+ack包,并向服務(wù)器發(fā)送確認包ack,二者相互建立聯(lián)系后,完成tcp三次握手。
三次握手之所以是三次是保證client(客戶端)和server(服務(wù)器)均讓對方知道自己的接收和發(fā)送能力沒問題而保證的最小次數(shù)。
四次揮手:
TCP連接是雙向傳輸?shù)膶Φ鹊哪J剑褪钦f雙方都可以同時向?qū)Ψ桨l(fā)送或接收數(shù)據(jù)。當有一方要關(guān)閉連接時,會發(fā)送指令告知對方,我要關(guān)閉連接了。這時對方會回一個ACK,此時一個方向的連接關(guān)閉。但是另一個方向仍然可以繼續(xù)傳輸數(shù)據(jù),等到發(fā)送完了所有的數(shù)據(jù)后,會發(fā)送一個FIN段來關(guān)閉此方向上的連接。接收方發(fā)送ACK確認關(guān)閉連接。。所以關(guān)閉雙通道的時候就是這樣:
客戶端:我要關(guān)閉輸入通道了。
服務(wù)端:稍等,還有一些數(shù)據(jù)給你
服務(wù)端:好了,我也要關(guān)閉輸入通道了。
客戶端:好的你關(guān)閉吧,我也把這個通道關(guān)閉。
五: HTTP和 HTTPS有什么區(qū)別?
1.HTTP(超文本傳輸協(xié)議):
是一個應(yīng)用層協(xié)議,由請求和響應(yīng)構(gòu)成,基于TCP/IP通信協(xié)議來傳遞數(shù)據(jù)(HTML 文件, 圖片文件, 查詢結(jié)果等),HTTP默認的端口號為80,明文傳輸,連接簡單,但是不安全,容易造成信息泄露
2.HTTPS(超文本傳輸安全協(xié)議):
HTTPS經(jīng)由HTTP進行通信,但利用SSL/TLS來加密數(shù)據(jù)包。HTTPS開發(fā)的主要目的,是提供對網(wǎng)站服務(wù)器的身份認證,保護交換數(shù)據(jù)的隱私與完整性。默認端口號為443,需要到CA申請證書,不免費的
詳情:https://juejin.cn/post/6844903599303032845
六:瀏覽器輸入URL發(fā)生了什么?
1.當用戶按下回車鍵,瀏覽器開始檢查URL是否符合規(guī)范,然后組裝協(xié)議,構(gòu)成完整的URL
2.瀏覽器進程通過進程間通信(IPC)把url請求發(fā)送給網(wǎng)絡(luò)進程,檢查是否有本地緩存該請求資源,如果有,則返回給瀏覽器進程,
3.沒有則向web服務(wù)器發(fā)送HTTP請求,過程:
-進行DNS解析,獲取服務(wù)器IP地址和端口號;
-通過IP地址和服務(wù)器建立TCP連接;
-構(gòu)建請求頭,發(fā)送請求頭;
-服務(wù)器響應(yīng)后,網(wǎng)絡(luò)進程接收響應(yīng)頭和響應(yīng)信息,并解析響應(yīng)內(nèi)容;
4.網(wǎng)絡(luò)進程解析響應(yīng)流程:
-檢查狀態(tài)碼,-200狀態(tài)碼:檢查響應(yīng)類型Content-Type,如果是字節(jié)流類型,則將該請求提交給下載管理器,該導(dǎo)航流程結(jié)束,不再進行,后續(xù)的渲染,如果是html則通知瀏覽器進程準備渲染進程準備進行渲染。
常見狀態(tài)碼
6.渲染進程準備好后,接收完整數(shù)據(jù),向瀏覽器發(fā)送“確認提交”,瀏覽器進程接收到確認消息后更新瀏覽器界面狀態(tài):安全、地址欄url、前進后退的歷史狀態(tài)、更新web頁面。
詳情:https://time.geekbang.org/column/article/117637
七:GET與POST的區(qū)別?
1.GET:傳參方式是通過URL地址欄傳參,請求數(shù)據(jù)放在URL?后面,通過&符號進行參數(shù)分割,傳遞的參數(shù)是可見的,對傳遞數(shù)據(jù)大小有限制,最大2048字符.回退不會影響,請求可以被緩存,請求記錄會被保存在歷史記錄中.
2.POST:傳參方式URL不可見,數(shù)據(jù)存放在HTTP包體內(nèi),數(shù)據(jù)大小是沒有限制的,回退會重新提交,數(shù)據(jù)不能被緩存,請求也不會有歷史記錄.
八:瀏覽器請求跨域?
https://blog.csdn.net/ashleyjun/article/details/98849400
url構(gòu)成: 協(xié)議://服務(wù) 域名( IP:端口)/URI?key1=value1&key2=value2#xxxx;
協(xié)議:http、https、ftp…
服務(wù):萬維網(wǎng)(World Wide Web )
域名 = 標識串(baidu 、google、sina…)+網(wǎng)站類型(com、gov、edu…)
端口號:以數(shù)字形式表示,若協(xié)議為HTTP,默認端口號為80,可省略不寫,https為443;
查詢:以“?”為起點,每個參數(shù)以“&”隔開,以“=”分開參數(shù)名稱與數(shù)據(jù)
片段:以“#”字符開頭
跨域: 就是相對同源策略而言,域名,端口號,協(xié)議都相同就是同源,否則就是跨域;
解決一: JSONP, 讓網(wǎng)頁從跨域地址那獲取資料,及跨越數(shù)據(jù);
原理: 一個HTML中可以有多個script標簽,數(shù)據(jù)間可以互相訪問,src不僅能導(dǎo)入本地資源,也能導(dǎo)入遠程資源,src沒有同源限制,所以可以跨域請求數(shù)據(jù);
解決二: 服務(wù)器代理,瀏覽器有跨域限制,但是服務(wù)器不存在跨域問題,所以可以由服務(wù)器請求所要域的資源再返回給客戶端。
https://zhuanlan.zhihu.com/p/81809258
九:js單線程運行的宏任務(wù),微任務(wù),eventLoop?
eventLoop:是解決JS單線程運行阻塞的一種機制,在JS的異步運行機制中,我們需要知道:
1.所有的「同步任務(wù)」都在主線程進行,
2.「異步任務(wù)」進入任務(wù)隊列,任務(wù)隊列會通知主線程,哪個異步任務(wù)可以執(zhí)行,這個異步任務(wù)就會進入主線程。異步任務(wù)必須指定回調(diào)函數(shù),當主線程開始執(zhí)行異步任務(wù),其實就是在執(zhí)行對應(yīng)的回調(diào)函數(shù)。
3.如果主線程的所有同步任務(wù)都執(zhí)行完,系統(tǒng)就會去讀取「任務(wù)隊列」上的異步任務(wù),如果有可以執(zhí)行的,就會結(jié)束等待狀態(tài),進入主線程,開始執(zhí)行。
4.主線程不斷的執(zhí)行第3步
這就是JS的運行機制,也稱為「Event Loop」事件循環(huán)。
1.宏任務(wù)(macrotask):創(chuàng)建主文檔對象,解析HTML,執(zhí)行主線或全局js代碼,及更改url各種事件,屬于宏任務(wù)的事件有:script(整體代碼)、setTimeout、setInterval、setImmediate、I/O、UI rendering.
2.微任務(wù)(microtasks):更新程序狀態(tài),但是必須在瀏覽器任務(wù)繼續(xù)執(zhí)行其他任務(wù)前執(zhí)行的任務(wù),簡言之就是代碼自上而下執(zhí)行,在開始新一輪宏任務(wù)前,必須執(zhí)行完微任務(wù)才能開始新一輪的宏任務(wù),微任務(wù)事件:Promise.then,catch,finally,object.observe等.
3.Promise是立即執(zhí)行函數(shù),是個同步函數(shù)也就是在宏任務(wù)里執(zhí)行,Promise后面的.then是微任務(wù)(.then里面有Promise也是立即執(zhí)行,比此輪宏任務(wù)的setTimeout函數(shù)還先執(zhí)行的那種立即執(zhí)行,因為代碼執(zhí)行到setTimeout時只是將事件放入任務(wù)列表,得下輪任務(wù)開始才執(zhí)行; .then后面跟著的.then是放進微任務(wù)棧中,立即執(zhí)行,因為本質(zhì)返回的是個Promise)
console.log('1')
setTimeout(() => {
console.log('2')
}, 0)
Promise.resolve().then(() => {
console.log('3')
}).then(() => {
console.log('4')
})
console.log('5')
//運行結(jié)果
//1
//5
//3
//4
//2
過程分析:
1、任務(wù)隊列中只有script,則將script中所有函數(shù)放進函數(shù)執(zhí)行棧按順序執(zhí)行
2、接下來遇到setTimeout,這里的setTimeout只是將回調(diào)函數(shù)在0毫秒后放入任務(wù)隊列(其實是4ms,html5標準中規(guī)定中要求setTimeout中低于4ms的時間間隔算為4ms),也就是說回調(diào)函數(shù)在下一個事件循環(huán)執(zhí)行,setTimeout在這里將回調(diào)函數(shù)放至任務(wù)列表后就結(jié)束了(ps:下一輪宏任務(wù)開始才執(zhí)行)。
3、遇到Promise,.then屬于「microtasks」,所以將第一個then的回調(diào)放到microtasks隊列 4、執(zhí)行完所有script代碼后,檢查microtasks隊列,發(fā)現(xiàn)隊列不為空,所以執(zhí)行第一個回調(diào)函數(shù)輸出'3',由于then的返回仍然是Promise,因此將第二個then的回調(diào)放至microtasks隊列并執(zhí)行輸出'4'。
5、此時microtasks隊列為空,開始執(zhí)行下一個事件循環(huán)。檢查task隊列發(fā)現(xiàn)setTimeout的回調(diào)函數(shù),因此執(zhí)行輸出'setTimeout'
十: js深拷貝與淺拷貝?
基本數(shù)據(jù)類型: String,Number,Null,Undefined,Symbol,Boolean;
1.它們保存在棧內(nèi)存內(nèi),因為棧比堆速度快,基本數(shù)據(jù)類型比較穩(wěn)定相對占用內(nèi)存小;
2.可以使用typeof可以檢測基本數(shù)據(jù)類型是什么,但null返回的是Object,因此null表示的是空對象的指針,undefined是null的衍生,所以 undefined==null // true;
引用數(shù)據(jù)類型:Object(包括Array,Date,RegExp,Function);
1.引用類型是動態(tài)的,保存在堆內(nèi)存中(只是在棧內(nèi)存了一個對象的引用或者說是指針),因為堆比棧更大,放棧內(nèi)存中會降低查找速度;
2.堆內(nèi)存是無序的,可根據(jù)引用(指針)直接獲取;
3.引用類型檢查數(shù)據(jù)類型用 instanceof ,使用typeof都是返回object
深淺拷貝只針對引用類型(ps:其實基本數(shù)據(jù)類型改變值算深拷貝,因為改變不會影響原來的值)
- 淺拷貝: 只復(fù)制指向?qū)ο蟮闹羔?不復(fù)制本身,新舊對象共享同一內(nèi)存,所以修改新對象會改變舊對象的值;
2.深拷貝:創(chuàng)建一個一模一樣的對象,不共享內(nèi)存,所以修改新對象不會改變舊對象的值;
js自帶深拷貝的方法:
array: slice()、concat、Array.from()、... 操作符:只能實現(xiàn)一維數(shù)組的深拷貝;
Object: Object.assign():只能實現(xiàn)一維對象的深拷貝;JSON.parse(JSON.stringify(obj)):可實現(xiàn)多維對象的深拷貝,但會忽略undefined、任意的函數(shù)、symbol 值;
FUnction: bind(),eval, new Function(ary1,ary2...,function-body) // 需將參數(shù)與函數(shù)體提取出來;
(JS 提供的自有方法并不能徹底解決Array、Object的深拷貝,只能自己實現(xiàn))
十一: 原型鏈?
(--proto--中劃線其實為兩條下劃線,markDown無法轉(zhuǎn)義只能這樣寫)
原型: 構(gòu)造函數(shù)的 prototype 指向原型對象(只有函數(shù)有prototype,對象沒有),原型對象有一個 constructor 屬性指回構(gòu)造函數(shù),每個構(gòu)造函數(shù)生成的實例對象都有一個 --proto-- 屬性,這個屬性指向創(chuàng)建那個構(gòu)造函數(shù)的原型對象。isprototypeof()方法可以確定對象間是否有關(guān)系.
原型鏈: 簡單理解就是原型組成的鏈,當需讀取某個對象的屬性時,首先會從對象實例本身開始,如果實例中找到該屬性則返回值,沒有則繼續(xù)通過--proto--向上搜索指向的構(gòu)造函數(shù),沒有則通過prototype搜索指向的原型對象,層層遞進(原型對象的proto屬性指向null); 假如我們讓原型對象(A)等于另一個類型的實例呢?此時原型對象將包含另一個原型(B)的指針,相應(yīng)的B中也包含著指向自己構(gòu)造函數(shù)的指針(constructor),可以向下訪問,這就構(gòu)成了實例與原型的鏈條.
image.png
function pig(name) {this.name=name} //pig的構(gòu)造函數(shù)
let sonPig=new pig('lucy') //pig的實例
pig.prototype===sonPig.__proto__ // true
pig.__proto__===Function.prototype// true
pig.prototype.constructor===pig // true
pig.prototype.isPrototypeOf(sonPig) //true
https://blog.csdn.net/u012468376/article/details/53121081;
https://zhuanlan.zhihu.com/p/62903507
十二: new 一個對象的過程?
1.創(chuàng)建一個空對象;
2.讓構(gòu)造函數(shù)的this指向新對象;
3.設(shè)置新對象的--proto--屬性指向構(gòu)造函數(shù)的原型對象;
4.判斷返回數(shù)據(jù)類型,如果是值類型,返回新對象,是引用類型則返回這個引用類型的對象.
十三: 組件,模塊化編程?
組件: 將js,HTML,css包裝在一起,提供方法和效果;
模塊化: 將相同功能抽取出來,存放在一個位置進行編程;
十四: let 與const?
- 不存在變量提升,須在聲明后使用,否則會報錯;
- 存在暫時性死區(qū),區(qū)塊作用域內(nèi)存在let,const命令,這個區(qū)塊對這些命令聲明的變量是形成封閉作用域,凡在聲明前使用就會報錯,無論作用域外是否有同名變量;
- 不允許相同作用域內(nèi)重復(fù)聲明一個變量,會報錯;
//{ }->這叫塊級作用域,ES6允許塊級作用域任意嵌套;
{{
let a="hello"
console.log(a) //hello
}}
{
{let a="hello"}
console.log(a) //Uncaught ReferenceError: a is not defined (因為沒有變量提升,外層作用域無法讀取內(nèi)層變量)
}
十五: 下面的代碼打印什么內(nèi)容,為什么?
let b = 10;
(function b(){
b = 20;
console.log(b);
})();
打印結(jié)果內(nèi)容如下:
? b() {
b = 20;
console.log(b)
}
原因:
作用域:執(zhí)行上下文中包含作用域鏈:
在理解作用域鏈之前,先介紹一下作用域,作用域可以理解為執(zhí)行上下文中申明的變量和作用的范圍;包括塊級作用域/函數(shù)作用域;
特性:聲明提前:一個聲明在函數(shù)體內(nèi)都是可見的,函數(shù)聲明優(yōu)先于變量聲明;
在非匿名自執(zhí)行函數(shù)中,函數(shù)變量為只讀狀態(tài)無法修改;js作用域中先從最近的開始向上尋找,b=20沒有被聲明,所以在非嚴格模式下打印出函數(shù)體.
十六: 實現(xiàn)一個sleep函數(shù),從Promis,Async/Await 等角度實現(xiàn)?
//Promise:
function sleep(time) {
return new Promise((resolve => setTimeout(resolve, time)))
}
sleep(1000).then(() => {
console.log("睡了一覺啦~")
})
//Async/Await :
function sleep(time) {
return new Promise(resolve => setTimeout(resolve, time))
}
async function res() {
await sleep(1000)
console.log("我睡了一覺啦~")
}
res()
十七: Cookie、SessionStorage、LocalStorage區(qū)別?
Cookie: 主要用來判斷用戶是否登錄,默認是關(guān)閉瀏覽器后失效, 但是也可以設(shè)置過期時間,每次請求都會攜帶在HTTP請求頭中,如果使用cookie保存過多數(shù)據(jù)會帶來性能問題;
localStorage: 主要用于本地儲存,長期存儲數(shù)據(jù),比如:購物車,瀏覽器關(guān)閉后數(shù)據(jù)不丟失;
sessionStorage: 主要用于會話存儲,數(shù)據(jù)在瀏覽器關(guān)閉后自動刪除。
Cookie容量限制: 大小(4KB左右)和個數(shù)(20~50);
SessionStorage和LocalStorage容量限制: 大小(5M左右)
十八: js 中強制轉(zhuǎn)型?
在js中兩個參數(shù)的內(nèi)置類型間的轉(zhuǎn)換被稱為強制轉(zhuǎn)型;
顯示強制轉(zhuǎn)換: parseInt,parseFloat,Number;
隱式強制轉(zhuǎn)換: == ===
//顯式強制轉(zhuǎn)換:
let a = "123"
let b = Number(a)
console.log(a, b, typeof a, typeof b) // 123 123 string number
//隱式強制轉(zhuǎn)換:
let a="123"
let b=a*1 //這樣a的值"123"會被隱式轉(zhuǎn)換成123
console.log(a, b, typeof a, typeof b) //123 123 string number
十九: undefined 和 not defined的區(qū)別?
let a; //聲明了一個變量a,但未被賦值
console.log(a) //undefined
console.log(c) // 試圖使用一個不存在且尚未聲明的變量,js拋出錯誤“c is not defined”
二十: undefined 和 null 的區(qū)別?
undefiend: 已經(jīng)聲明,尚未被初始化;
null: 空值,表示 "什么都沒有",是一個只有一個值的特殊類型,表示一個空對象引用;
null 和 undefined 的值相等,但類型不等:
typeof undefined // undefined (typeof 一個沒有值的變量會返回 undefined)
typeof null // object
null === undefined // false
null == undefined // true
二十一: Vue 的SPA單頁面的優(yōu)缺點?
優(yōu)點: 用戶體驗好,快,切換頁面不用重新加載整個頁面,避免了不必要的跳轉(zhuǎn)和渲染;相對服務(wù)器壓力較小,前后端分工明確,架構(gòu)清晰,前端負責邏輯交互,后端負責數(shù)據(jù)處理;
缺點:
- 初次加載耗時多: 因為需要將html,js,css統(tǒng)一進行加載,只有部分頁面按需加載;
- 前進后退路由需自己建立堆棧管理,因為不能使用瀏覽器自帶的前進后退功能;
- SEO難度較大: 因為所有的內(nèi)容都在一個頁面中動態(tài)替換顯示,所以在 SEO 上其有著天然的弱勢。
如何解決:
1.首屏加載時間過長: 加載過多的問題可以通過webpack優(yōu)化 ,盡可能減少加載項,或者預(yù)渲染 prerendering(適用于靜態(tài)站點),但終極方案還是ssr(適合動態(tài)渲染,但配置繁瑣,Nuxt可以幫助簡化配置),
路由用vue-router進行管理
如何SEO(搜索引擎優(yōu)化): 1. 內(nèi)容建設(shè):是不是當前廣大用戶需求的內(nèi)容?2. 競爭對手:對手都是怎么在做的?我們?nèi)绾尾町惢?. 協(xié)調(diào)資源:我們該協(xié)調(diào)那些資源來促成這次seo修改?4. 站內(nèi)優(yōu)化:把你的seo細節(jié)做到極致5. 站外優(yōu)化:網(wǎng)站上線,如何推廣、鏈接建設(shè)促進收錄和排名6. 迭代優(yōu)化:數(shù)據(jù)分析促進頁面體驗的不斷完善修改
https://www.zhihu.com/question/19760381/answer/21614135
二十二: 加法操作?
let a = true
console.log(true==1,false==0) // true true
console.log(a + true) // 2
console.log(a + false) // 1
console.log(a + 1) // 2
console.log(a + "bcd") // truebcd
所以不同類型執(zhí)行加法操作是這樣的:
Number + Number 執(zhí)行加法; (例: 2 +2 = 4)
Boolean + Number 執(zhí)行加法; (例: true + 2 = 3)
Boolean + Boolean 執(zhí)行加法; (例: true + true = 2)
Number + String 執(zhí)行連接; (例: 2 + "abc" = "2abc" ; 2 + "2" = "22")
String + Boolean 執(zhí)行連接; (例:"2" + true = "2true")
String + String 執(zhí)行連接; (例: "2" + "2" = "22")