innerHTML和document.write()區(qū)別
dom元素屬性
document方法,寫(xiě)入頁(yè)面的內(nèi)容流會(huì)導(dǎo)致頁(yè)面重寫(xiě)
瀏覽器渲染網(wǎng)頁(yè)的具體流程(關(guān)鍵渲染路徑)
用戶(hù)看到頁(yè)面實(shí)際上可以分為兩個(gè)階段:頁(yè)面內(nèi)容加載完成和頁(yè)面資源加載完成,分別對(duì)應(yīng)于DOMContentLoaded和Load。
DOMContentLoaded事件觸發(fā)時(shí),僅當(dāng)DOM加載完成,不包括樣式表,圖片等
load事件觸發(fā)時(shí),頁(yè)面上所有的DOM,樣式表,腳本,圖片都已加載完成
1、HTML解析,構(gòu)建DOM樹(shù)
??瀏覽器從網(wǎng)絡(luò)或硬盤(pán)中獲得HTML字節(jié)數(shù)據(jù)后會(huì)經(jīng)過(guò)以下流程將字節(jié)解析為DOM樹(shù):
字符編碼:先將HTML的原始字節(jié)數(shù)據(jù)轉(zhuǎn)換為文件指定編碼的字符。
令牌化:然后瀏覽器會(huì)根據(jù)HTML規(guī)范來(lái)將字符串轉(zhuǎn)換成各種令牌(如<html>、<body>、<p>這樣的標(biāo)簽以及標(biāo)簽中的字符串和屬性等都會(huì)被轉(zhuǎn)化為令牌,每個(gè)令牌具有特殊含義和規(guī)則)。
生成節(jié)點(diǎn)對(duì)象:接著每個(gè)令牌都會(huì)被轉(zhuǎn)換成定義其屬性和規(guī)則的對(duì)象,即節(jié)點(diǎn)對(duì)象。
構(gòu)建DOM樹(shù):最后將節(jié)點(diǎn)對(duì)象構(gòu)建成樹(shù)形結(jié)構(gòu),即DOM樹(shù)。HTML標(biāo)簽之間有復(fù)雜的父子關(guān)系,樹(shù)形結(jié)構(gòu)剛好可以詮釋這樣的關(guān)系。

2、CSS解析,構(gòu)建CSSOM樹(shù)
瀏覽器解析CSS文件并生成CSSOM,每個(gè)CSS文件都被分析成一個(gè)StyleSheet對(duì)象,每個(gè)對(duì)象都包含CSS規(guī)則。CSS規(guī)則對(duì)象包含對(duì)應(yīng)于CSS語(yǔ)法的選擇器和聲明對(duì)象以及其他對(duì)象。
在這個(gè)過(guò)程需要注意的是:
CSS解析可以與DOM解析同時(shí)進(jìn)行。
CSS解析與script的執(zhí)行互斥 。
在Webkit內(nèi)核中進(jìn)行了script執(zhí)行優(yōu)化,只有在JS訪問(wèn)CSS時(shí)才會(huì)發(fā)生互斥。
3、Render Tree
??在構(gòu)建了DOM樹(shù)和CSSOM樹(shù)之后,瀏覽器只是擁有2個(gè)相互獨(dú)立的對(duì)象集合,DOM樹(shù)描述的文檔結(jié)構(gòu)和內(nèi)容,CSSOM樹(shù)描述了對(duì)應(yīng)文檔的樣式規(guī)則,想要渲染出頁(yè)面,就需要將DOM樹(shù)、CSSOM樹(shù)結(jié)合在一起,構(gòu)建渲染樹(shù)。
4、Layout
渲染樹(shù)構(gòu)建好后,瀏覽器得到了每個(gè)節(jié)點(diǎn)的內(nèi)容與樣式,下一步就是需要計(jì)算每個(gè)節(jié)點(diǎn)在瀏覽器窗口的確切位置與大小,即layout布局。
布局階段,從渲染樹(shù)的根節(jié)點(diǎn)開(kāi)始遍歷,采用盒子模型的模式來(lái)表示每個(gè)節(jié)點(diǎn)與其他元素之間的距離,從而確定每個(gè)元素在屏幕內(nèi)的位置與大小。
布局階段會(huì)從渲染樹(shù)的根節(jié)點(diǎn)開(kāi)始遍歷,由于渲染樹(shù)的每個(gè)節(jié)點(diǎn)都是一個(gè)Render Object對(duì)象,包含寬高,位置,背景色等樣式信息。所以瀏覽器就可以通過(guò)這些樣式信息來(lái)確定每個(gè)節(jié)點(diǎn)對(duì)象在頁(yè)面上的確切大小和位置,布局階段的輸出就是我們常說(shuō)的盒子模型,它會(huì)精確地捕獲每個(gè)元素在屏幕內(nèi)的確切位置與大小。需要注意的是:
float元素,absoulte元素,fixed元素會(huì)發(fā)生位置偏移。
我們常說(shuō)的脫離文檔流,其實(shí)就是脫離Render Tree。
5、Paint繪制頁(yè)面
??當(dāng)Layout布局完成后,瀏覽器會(huì)立即發(fā)出Paint事件,開(kāi)始講渲染樹(shù)繪制成像素,繪制所需要的時(shí)間跟CSS樣式的復(fù)雜度成正比,繪制完成后,用戶(hù)才能看到頁(yè)面在屏幕中的最終呈現(xiàn)效果。
渲染優(yōu)化方案
1、優(yōu)化關(guān)鍵渲染路徑方案
通過(guò)優(yōu)化關(guān)鍵渲染路徑,可以?xún)?yōu)化頁(yè)面渲染性能,減少頁(yè)面白屏?xí)r間。
優(yōu)化JS:JavaScript文件加載會(huì)阻塞DOM樹(shù)的構(gòu)建,可以給<script>標(biāo)簽添加異步屬性async,這樣瀏覽器的HTML解析就不會(huì)被js文件阻塞。
優(yōu)化CSS:瀏覽器每次遇到<link>標(biāo)簽時(shí),瀏覽器就需要向服務(wù)器發(fā)出請(qǐng)求獲得CSS文件,然后才繼續(xù)構(gòu)建DOM樹(shù)和CSSOM樹(shù),可以合并所有CSS成一個(gè)文件,減少HTTP請(qǐng)求,減少關(guān)鍵資源往返加載的時(shí)間,優(yōu)化渲染速度。
2、其他優(yōu)化方案
加載部分HTML
瀏覽器先加載主要HTML初始化靜態(tài)部分,動(dòng)態(tài)變化的HTML內(nèi)容通過(guò)Ajax請(qǐng)求加載。這樣可以減少瀏覽器構(gòu)建DOM樹(shù)的工作量,讓用戶(hù)感覺(jué)頁(yè)面加載速度很快。
壓縮
對(duì)HTML、CSS、JavaScript這些文件去除冗余字符(例如不必要的注釋、空格符和換行符等),再進(jìn)行壓縮,減小文件數(shù)據(jù)大小,加快瀏覽器解析文件編碼。
圖片加載優(yōu)化
1)小圖標(biāo)合并成雪碧圖,進(jìn)而減少img的HTTP請(qǐng)求次數(shù);
2)圖片加載較多時(shí),采用懶加載的方案,用戶(hù)滾動(dòng)頁(yè)面可視區(qū)時(shí)再加載渲染圖片。
圖片懶加載
優(yōu)點(diǎn):提升用戶(hù)體驗(yàn)、減輕服務(wù)器壓力
方法一:
clientHeight:瀏覽器視口的高度;
scrollTop:滾動(dòng)軸滾動(dòng)的距離;
offsetTop:圖片的頭部距離瀏覽器頂部的高度(注意不是距離視口頂部的高度);
offsetTop:直接通過(guò)img.offsetTop就可以獲??;
scrollTop:通過(guò)document.documentElement.scrollTop獲??;
clientHeight:通過(guò)document.documentElement.clientHeight獲??;
當(dāng)offsetTop?<=?視口高度clientHeight?+ 滾動(dòng)條的長(zhǎng)度scrollTop加載圖片
方法二:
getBoundClientRect介紹:
Element.getBoundingClientRect() 方法返回元素的大小及其相對(duì)于視口的位置。我們可以取得它的top值,它的top值就是元素左上角到視口頂部的距離。
加載的條件
當(dāng)Element.getBoundingClientRect().top< 視口高度時(shí)觸發(fā)加載;
方法三:
IntersectionObserver介紹
IntersectionObserver可以異步觀察目標(biāo)元素與其祖先元素或頂級(jí)文檔視窗(viewport)交叉狀態(tài)的方法。也就是說(shuō)它可以幫助我們?nèi)ヅ袛嘁粋€(gè)元素是否出現(xiàn)在視口上。這里只介紹用到的兩個(gè)屬性:
IntersectionObserver.observe():使IntersectionObserver開(kāi)始監(jiān)聽(tīng)一個(gè)目標(biāo)元素;
isIntersecting屬性:可以判斷該元素是否出現(xiàn)在視口內(nèi);
HTTP緩存
瀏覽器自帶了HTTP緩存的功能,只需要確保每個(gè)服務(wù)器響應(yīng)的頭部都包含了以下的屬性:
1)ETag: ETag是一個(gè)傳遞驗(yàn)證令牌,它對(duì)資源的更新進(jìn)行檢查,如果資源未發(fā)生變化時(shí)不會(huì)傳送任何數(shù)據(jù)。當(dāng)瀏覽器發(fā)送一個(gè)請(qǐng)求時(shí),會(huì)把ETag一起發(fā)送到服務(wù)器,服務(wù)器會(huì)根據(jù)當(dāng)前資源核對(duì)令牌(ETag通常是對(duì)內(nèi)容進(jìn)行Hash后得出的一個(gè)指紋),如果資源未發(fā)生變化,服務(wù)器將返回304 Not Modified響應(yīng),這時(shí)瀏覽器不必再次下載資源,而是繼續(xù)復(fù)用緩存。
2)Cache-Control: Cache-Control定義了緩存的策略,它規(guī)定在什么條件下可以緩存響應(yīng)以及可以緩存多久。
a、no-cache: no-cache表示必須先與服務(wù)器確認(rèn)返回的響應(yīng)是否發(fā)生了變化,然后才能使用該響應(yīng)來(lái)滿(mǎn)足后續(xù)對(duì)同一網(wǎng)址的請(qǐng)求(每次都會(huì)根據(jù)ETag對(duì)服務(wù)器發(fā)送請(qǐng)求來(lái)確認(rèn)變化,如果未發(fā)生變化,瀏覽器不會(huì)下載資源)。no-store直接禁止瀏覽器以及所有中間緩存存儲(chǔ)任何版本的返回響應(yīng)。簡(jiǎn)單的說(shuō),該策略會(huì)禁止任何緩存,每次發(fā)送請(qǐng)求時(shí),都會(huì)完整地下載服務(wù)器的響應(yīng)。
b、public&private:如果響應(yīng)被標(biāo)記為public,則即使它有關(guān)聯(lián)的HTTP身份驗(yàn)證,甚至響應(yīng)狀態(tài)代碼通常無(wú)法緩存,瀏覽器也可以緩存響應(yīng)。如果響應(yīng)被標(biāo)記為private,那么這個(gè)響應(yīng)通常只為單個(gè)用戶(hù)緩存,因此不允許任何中間緩存(CDN)對(duì)其進(jìn)行緩存,private一般用在緩存用戶(hù)私人信息頁(yè)面。
c、max-age: max-age定義了從請(qǐng)求時(shí)間開(kāi)始,緩存的最長(zhǎng)時(shí)間,單位為秒。
Promise 是做什么的,有哪些API
Promise用法
> Promise構(gòu)造函數(shù)接受一個(gè)函數(shù)作為參數(shù),該函數(shù)的兩個(gè)參數(shù)分別是resolve和reject。它們是兩個(gè)函數(shù),由 JavaScript 引擎提供,不用自己部署。
> resolve函數(shù)的作用是,將Promise對(duì)象的狀態(tài)從“未完成”變?yōu)椤俺晒Α保磸?pending 變?yōu)?resolved),在異步操作成功時(shí)調(diào)用,并將異步操作的結(jié)果,作為參數(shù)傳遞出去;reject函數(shù)的作用是,將Promise對(duì)象的狀態(tài)從“未完成”變?yōu)椤笆 保磸?pending 變?yōu)?rejected),在異步操作失敗時(shí)調(diào)用,并將異步操作報(bào)出的錯(cuò)誤,作為參數(shù)傳遞出去。
Promise.prototype.then()
> Promise 實(shí)例具有 then 方法,也就是說(shuō),then 方法是定義在原型對(duì)象 Promise.prototype 上的。它的作用是為 Promise 實(shí)例添加狀態(tài)改變時(shí)的回調(diào)函數(shù)。前面說(shuō)過(guò),then 方法的第一個(gè)參數(shù)是 resolved 狀態(tài)的回調(diào)函數(shù),第二個(gè)參數(shù)(可選)是 rejected 狀態(tài)的回調(diào)函數(shù)。
> then 方法返回的是一個(gè)新的 Promise 實(shí)例**(注意,不是原來(lái)那個(gè) Promise 實(shí)例)。因此可以采用鏈?zhǔn)綄?xiě)法,即 then 方法后面再調(diào)用另一個(gè) then 方法。
Promise.prototype.catch()
> Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的別名,用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)。
> 上面代碼中,getJSON()方法返回一個(gè) Promise 對(duì)象,如果該對(duì)象狀態(tài)變?yōu)閞esolved,則會(huì)調(diào)用then()方法指定的回調(diào)函數(shù);如果異步操作拋出錯(cuò)誤,狀態(tài)就會(huì)變?yōu)閞ejected,就會(huì)調(diào)用catch()方法指定的回調(diào)函數(shù),處理這個(gè)錯(cuò)誤。另外,then()方法指定的回調(diào)函數(shù),如果運(yùn)行中拋出錯(cuò)誤,也會(huì)被catch()方法捕獲。
Promise.all()
> Promise.all()方法用于將多個(gè) Promise 實(shí)例,包裝成一個(gè)新的 Promise 實(shí)例。
> 上面代碼中,Promise.all()方法接受一個(gè)數(shù)組作為參數(shù),p1、p2、p3都是 Promise 實(shí)例,如果不是,就會(huì)先調(diào)用下面講到的Promise.resolve方法,將參數(shù)轉(zhuǎn)為 Promise 實(shí)例,再進(jìn)一步處理。
> 另外,Promise.all()方法的參數(shù)可以不是數(shù)組,但必須具有 Iterator 接口,且返回的每個(gè)成員都是 Promise 實(shí)例。
> p的狀態(tài)由p1、p2、p3決定,分成兩種情況:
> (1)只有p1、p2、p3的狀態(tài)都變成fulfilled,p的狀態(tài)才會(huì)變成fulfilled,此時(shí)p1、p2、p3的返回值組成一個(gè)數(shù)組,傳遞給p的回調(diào)函數(shù)。
> (2)只要p1、p2、p3之中有一個(gè)被rejected,p的狀態(tài)就變成rejected,此時(shí)第一個(gè)被reject的實(shí)例的返回值,會(huì)傳遞給p的回調(diào)函數(shù)。
Promise.race()
> Promise.race()方法同樣是將多個(gè) Promise 實(shí)例,包裝成一個(gè)新的 Promise 實(shí)例。
> 上面代碼中,只要p1、p2、p3之中有一個(gè)實(shí)例率先改變狀態(tài),p的狀態(tài)就跟著改變。那個(gè)率先改變的 Promise 實(shí)例的返回值,就傳遞給p的回調(diào)函數(shù)。
Promise.resolve()
> 有時(shí)需要將現(xiàn)有對(duì)象轉(zhuǎn)為 Promise 對(duì)象,Promise.resolve()方法就起到這個(gè)作用。
Promise.reject()
> Promise.reject(reason)方法也會(huì)返回一個(gè)新的 Promise 實(shí)例,該實(shí)例的狀態(tài)為rejected。
callback和Promise的區(qū)別是什么?
callback
回調(diào)函數(shù)本身是我們約定俗成的一種叫法。
優(yōu)點(diǎn):比較容易理解;
缺點(diǎn):1.高耦合,維護(hù)困難,回調(diào)地獄;2.每個(gè)任務(wù)只能指定一個(gè)回調(diào)函數(shù);3.如果幾個(gè)異步操作之間并沒(méi)有順序之分,同樣也要等待上一個(gè)操作執(zhí)行結(jié)束再進(jìn)行下一個(gè)操作。
Promise
ES6給我們提供了一個(gè)原生的構(gòu)造函數(shù)Promise,Promise代表了一個(gè)異步操作,可以將異步對(duì)象和回調(diào)函數(shù)脫離開(kāi)來(lái),通過(guò).then方法在這個(gè)異步操作上綁定回調(diào)函數(shù),Promise可以讓我們通過(guò)鏈?zhǔn)秸{(diào)用的方法去解決回調(diào)嵌套的問(wèn)題,而且由于promise.all這樣的方法存在,可以讓同時(shí)執(zhí)行多個(gè)操作變得簡(jiǎn)單。
promise對(duì)象存在三種狀態(tài):
1)Fulfilled:成功狀態(tài)
2)Rejected:失敗狀態(tài)
3)Pending:既不是成功也不是失敗狀態(tài),可以理解為進(jìn)行中狀態(tài)
Promise的缺點(diǎn):
1.當(dāng)處于未完成狀態(tài)時(shí),無(wú)法確定目前處于哪一階段。
2.如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部的錯(cuò)誤不會(huì)反映到外部。
3.無(wú)法取消Promise,一旦新建它就會(huì)立即執(zhí)行,無(wú)法中途取消。
聽(tīng)說(shuō)你還不知道Promise的allSettled()和all()的區(qū)別? - 云+社區(qū) - 騰訊云 (tencent.com)
Event Loop 事件循環(huán)
js擴(kuò)展運(yùn)算符(...)
用于將一個(gè)數(shù)組或類(lèi)數(shù)組對(duì)象轉(zhuǎn)換為用逗號(hào)分隔的值序列。它的基本用法是拆解數(shù)組和字符串。
①代替apply()函數(shù)
②代替concat()函數(shù)合并數(shù)組
③轉(zhuǎn)換Set,得到去重的數(shù)組
let arr=[1,2,4,6,2,7,4];
console.log([...newSet(arr)]);// [ 1, 2, 4, 6, 7 ]
④用于對(duì)象克隆
使用擴(kuò)展運(yùn)算符對(duì)數(shù)組或?qū)ο筮M(jìn)行克隆時(shí),如果數(shù)組的元素或者對(duì)象的屬性是基本數(shù)據(jù)類(lèi)型,則支持深克隆;
如果是引用數(shù)據(jù)類(lèi)型,則不支持深克隆。歸根結(jié)底是因?yàn)橐脭?shù)據(jù)類(lèi)型的克隆只是復(fù)制了引用的地址,克隆后的對(duì)象仍然共享同一個(gè)引用地址。
ES6 Set和Map
ES6 提供了新的數(shù)據(jù)結(jié)構(gòu) Set。它類(lèi)似于數(shù)組,但是成員的值都是唯一的,沒(méi)有重復(fù)的值。
Set本身是一個(gè)構(gòu)造函數(shù),用來(lái)生成 Set 數(shù)據(jù)結(jié)構(gòu)。向Set加入值時(shí),不會(huì)發(fā)送類(lèi)型轉(zhuǎn)換。
可用于去除數(shù)組重復(fù)成員或去除字符串中重復(fù)字符。
Set 結(jié)構(gòu)的實(shí)例有以下屬性。
Set.prototype.constructor:構(gòu)造函數(shù),默認(rèn)就是Set函數(shù)。
Set.prototype.size:返回Set實(shí)例的成員總數(shù)。
Set 實(shí)例的方法分為兩大類(lèi):操作方法(用于操作數(shù)據(jù))和遍歷方法(用于遍歷成員)。
操作方法
Set.prototype.add(value):添加某個(gè)值,返回 Set 結(jié)構(gòu)本身。
Set.prototype.delete(value):刪除某個(gè)值,返回一個(gè)布爾值,表示刪除是否成功。
Set.prototype.has(value):返回一個(gè)布爾值,表示該值是否為Set的成員。
Set.prototype.clear():清除所有成員,沒(méi)有返回值。
遍歷操作?
Set 結(jié)構(gòu)的實(shí)例有四個(gè)遍歷方法,可以用于遍歷成員。
Set.prototype.keys():返回鍵名的遍歷器
Set.prototype.values():返回鍵值的遍歷器
Set.prototype.entries():返回鍵值對(duì)的遍歷器
Set.prototype.forEach():使用回調(diào)函數(shù)遍歷每個(gè)成員
如果想在遍歷操作中,同步改變原來(lái)的 Set 結(jié)構(gòu),目前沒(méi)有直接的方法,但有兩種變通方法。一種是利用原 Set 結(jié)構(gòu)映射出一個(gè)新的結(jié)構(gòu),然后賦值給原來(lái)的 Set 結(jié)構(gòu);另一種是利用Array.from方法。
JavaScript 的對(duì)象(Object),本質(zhì)上是鍵值對(duì)的集合(Hash 結(jié)構(gòu)),但是傳統(tǒng)上只能用字符串當(dāng)作鍵。這給它的使用帶來(lái)了很大的限制。ES6 提供了 Map 數(shù)據(jù)結(jié)構(gòu)。它類(lèi)似于對(duì)象,也是鍵值對(duì)的集合,但是“鍵”的范圍不限于字符串,各種類(lèi)型的值(包括對(duì)象)都可以當(dāng)作鍵。
Ajax 基本流程
異步 JavaScript 和 XML,是指一種創(chuàng)建交互式網(wǎng)頁(yè)應(yīng)用的網(wǎng)頁(yè)開(kāi)發(fā)技術(shù)。是一種異步通信的方法,通過(guò)直接由 js 腳本向服務(wù)器發(fā)起 http 通信,然后根據(jù)服務(wù)器返回的數(shù)據(jù),更新網(wǎng)頁(yè)的相應(yīng)部分,而不用刷新整個(gè)頁(yè)面的一種方法。
創(chuàng)建一個(gè) ajax 有這樣幾個(gè)步驟:
- 首先是創(chuàng)建一個(gè) XMLHttpRequest 對(duì)象。
- 然后在這個(gè)對(duì)象上使用 open 方法創(chuàng)建一個(gè) http 請(qǐng)求,open 方法所需要的參數(shù)是請(qǐng)求的方法、請(qǐng)求的地址、是否異步和用戶(hù)的認(rèn)證信息。
- 在發(fā)起請(qǐng)求前,我們可以為這個(gè)對(duì)象添加一些信息和監(jiān)聽(tīng)函數(shù)。比如說(shuō)我們可以通過(guò) setRequestHeader 方法來(lái)為請(qǐng)求添加頭信息。我們還可以為這個(gè)對(duì)象添加一個(gè)狀態(tài)監(jiān)聽(tīng)函數(shù)。一個(gè) XMLHttpRequest 對(duì)象一共有 5 個(gè)狀態(tài),當(dāng)它的狀態(tài)變化時(shí)會(huì)觸發(fā) onreadystatechange 事件,我們可以通過(guò)設(shè)置監(jiān)聽(tīng)函數(shù),來(lái)處理請(qǐng)求成功后的結(jié)果。當(dāng)對(duì)象的 readyState 變?yōu)?4 的時(shí)候,代表服務(wù)器返回的數(shù)據(jù)接收完成,這個(gè)時(shí)候我們可以通過(guò)判斷請(qǐng)求的狀態(tài),如果狀態(tài)是 2xx 或者 304 的話(huà)則代表返回正常。這個(gè)時(shí)候我們就可以通過(guò) response 中的數(shù)據(jù)來(lái)對(duì)頁(yè)面進(jìn)行更新了。
- 當(dāng)對(duì)象的屬性和監(jiān)聽(tīng)函數(shù)設(shè)置完成后,最后我們調(diào)用 sent 方法來(lái)向服務(wù)器發(fā)起請(qǐng)求,可以傳入?yún)?shù)作為發(fā)送的數(shù)據(jù)體。
閉包
能夠訪問(wèn)另一個(gè)函數(shù)作用域的變量的函數(shù),參數(shù)和變量不會(huì)被垃圾回收機(jī)制回收。
創(chuàng)建私有變量
延長(zhǎng)變量的生命周期
閉包的缺點(diǎn):
- 常駐內(nèi)存,增加內(nèi)存使用量;
- 使用不當(dāng)造成內(nèi)存泄漏。
內(nèi)存泄漏
由于疏忽或錯(cuò)誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存
Javascript 具有自動(dòng)垃圾回收機(jī)制(GC:Garbage Collecation),也就是說(shuō),執(zhí)行環(huán)境會(huì)負(fù)責(zé)管理代碼執(zhí)行過(guò)程中使用的內(nèi)存
原理:垃圾收集器會(huì)定期(周期性)找出那些不在繼續(xù)使用的變量,然后釋放其內(nèi)存
通常情況下有兩種實(shí)現(xiàn)方式:
標(biāo)記清除
引用計(jì)數(shù)
箭頭函數(shù)與普通函數(shù)的區(qū)別
ES6 中箭頭函數(shù) VS 普通函數(shù)的 this 指向
普通函數(shù)中 this
1. 查看函數(shù)在哪被調(diào)用。
2. 點(diǎn)左側(cè)有沒(méi)有對(duì)象?如果有,它就是 “this” 的引用。如果沒(méi)有,繼續(xù)第 3 步。
3. 該函數(shù)是不是用 “call”、“apply” 或者 “bind” 調(diào)用的?如果是,它會(huì)顯式地指明 “this” 的引用。如果不是,繼續(xù)第 4 步。
4. 該函數(shù)是不是用 “new” 調(diào)用的?如果是,“this” 指向的就是 JavaScript 解釋器新創(chuàng)建的對(duì)象。如果不是,繼續(xù)第 5 步。
5. 是否在“嚴(yán)格模式”下?如果是,“this” 就是 undefined,如果不是,繼續(xù)第 6 步。
6. JavaScript 很奇怪,“this” 會(huì)指向 “window” 對(duì)象。
ES6 箭頭函數(shù)中 this
1. 默認(rèn)指向定義它時(shí),所處上下文的對(duì)象的 this 指向;
? 偶爾沒(méi)有上下文對(duì)象,this 就指向 window
ES6 class 和 ES5 函數(shù)的區(qū)別
與ES5不同,ES6 類(lèi)和模塊的內(nèi)部默認(rèn)就是嚴(yán)格模式,不存在遍歷提升
?es5函數(shù)內(nèi)的函數(shù)聲明會(huì)提升到函數(shù)作用域起始處,但函數(shù)體本身只會(huì)提升到塊級(jí)作用域起始處。
作用域鏈
當(dāng)在Javascript中使用一個(gè)變量的時(shí)候,首先Javascript引擎會(huì)嘗試在當(dāng)前作用域下去尋找該變量,如果沒(méi)找到,再到它的上層作用域?qū)ふ遥源祟?lèi)推直到找到該變量或是已經(jīng)到了全局作用域
如果在全局作用域里仍然找不到該變量,它就會(huì)在全局范圍內(nèi)隱式聲明該變量(非嚴(yán)格模式下)或是直接報(bào)錯(cuò)
作用域,即變量(變量作用域又稱(chēng)上下文)和函數(shù)生效(能被訪問(wèn))的區(qū)域或集合
全局作用域
任何不在函數(shù)中或是大括號(hào)中聲明的變量,都是在全局作用域下,全局作用域下聲明的變量可以在程序的任意位置訪問(wèn)
函數(shù)作用域
函數(shù)作用域也叫局部作用域,如果一個(gè)變量是在函數(shù)內(nèi)部聲明的它就在一個(gè)函數(shù)作用域下面。這些變量只能在函數(shù)內(nèi)部訪問(wèn),不能在函數(shù)以外去訪問(wèn)
JavaScript?遵循的就是詞法作用域,變量被創(chuàng)建時(shí)就確定好了,而非執(zhí)行階段確定的。也就是說(shuō)我們寫(xiě)好代碼時(shí)它的作用域就確定了
- 判斷數(shù)據(jù)類(lèi)型的方式
1. typeof 缺陷:可以判斷null以外的基礎(chǔ)類(lèi)型,復(fù)雜類(lèi)型除funiton以外均判斷為object
? // typeof(null)===‘’object’;typeof (()=>{} ))===‘function’;
2. instanceof 可以準(zhǔn)確判斷復(fù)雜引用類(lèi)型,但不能判斷基礎(chǔ)類(lèi)型
作用: instanceof 運(yùn)算符用于判斷構(gòu)造函數(shù)的 prototype 屬性是否出現(xiàn)在某個(gè)實(shí)例對(duì)象的原型鏈中的任何位置。
3.Object.prototype.toString.call() ;可以判斷所有類(lèi)型,返回示例 ''[object Array]"
? 注意后面的類(lèi)型一定是大寫(xiě)字母開(kāi)頭。
常用數(shù)組,字符串方法
增
下面前三種是對(duì)原數(shù)組產(chǎn)生影響的增添方法,第四種則不會(huì)對(duì)原數(shù)組產(chǎn)生影響
push()方法接收任意數(shù)量的參數(shù),并將它們添加到數(shù)組末尾,返回?cái)?shù)組的最新長(zhǎng)度
unshift()在數(shù)組開(kāi)頭添加任意多個(gè)值,然后返回新的數(shù)組長(zhǎng)度
splice()傳入三個(gè)參數(shù),分別是開(kāi)始位置、0(要?jiǎng)h除的元素?cái)?shù)量)、插入的元素,返回空數(shù)組
concat()首先會(huì)創(chuàng)建一個(gè)當(dāng)前數(shù)組的副本,然后再把它的參數(shù)添加到副本末尾,最后返回這個(gè)新構(gòu)建的數(shù)組,不會(huì)影響原始數(shù)組
刪
下面三種都會(huì)影響原數(shù)組,最后一項(xiàng)不影響原數(shù)組:
pop()方法用于刪除數(shù)組的最后一項(xiàng),同時(shí)減少數(shù)組的length?值,返回被刪除的項(xiàng)
shift()方法用于刪除數(shù)組的第一項(xiàng),同時(shí)減少數(shù)組的length?值,返回被刪除的項(xiàng)
splice()傳入兩個(gè)參數(shù),分別是開(kāi)始位置,刪除元素的數(shù)量,返回包含刪除元素的數(shù)組
slice()?用于創(chuàng)建一個(gè)包含原有數(shù)組中一個(gè)或多個(gè)元素的新數(shù)組,不會(huì)影響原始數(shù)組。傳入兩個(gè)參數(shù),分別是開(kāi)始位置和結(jié)束位置(不包含結(jié)束位置的元素)。
改?即修改原來(lái)數(shù)組的內(nèi)容,常用splice
查
查找元素,返回元素坐標(biāo)或者元素值
indexOf()返回要查找的元素在數(shù)組中的位置,如果沒(méi)找到則返回 -1
includes()返回要查找的元素在數(shù)組中的位置,找到返回true,否則false
find()返回第一個(gè)匹配的元素
排序方法
數(shù)組有兩個(gè)方法可以用來(lái)對(duì)元素重新排序:
reverse(),將數(shù)組元素方向反轉(zhuǎn)
sort()方法接受一個(gè)比較函數(shù),用于判斷哪個(gè)值應(yīng)該排在前面
轉(zhuǎn)換方法
數(shù)組轉(zhuǎn)字符串
join()方法接收一個(gè)參數(shù),即字符串分隔符,返回包含所有項(xiàng)的字符串
toString()將數(shù)組轉(zhuǎn)換成一個(gè)字符串
字符串轉(zhuǎn)數(shù)組:
str.split(分隔符,留下的個(gè)數(shù))
迭代方法
some()對(duì)數(shù)組每一項(xiàng)都運(yùn)行傳入的測(cè)試函數(shù),如果至少有1個(gè)元素返回 true ,則這個(gè)方法返回 true
every()對(duì)數(shù)組每一項(xiàng)都運(yùn)行傳入的測(cè)試函數(shù),如果所有元素都返回 true ,則這個(gè)方法返回 true
forEach()對(duì)數(shù)組每一項(xiàng)都運(yùn)行傳入的函數(shù),沒(méi)有返回值
filter()對(duì)數(shù)組每一項(xiàng)都運(yùn)行傳入的函數(shù),函數(shù)返回?true?的項(xiàng)會(huì)組成數(shù)組之后返回
map()對(duì)數(shù)組每一項(xiàng)都運(yùn)行傳入的函數(shù),返回由每次函數(shù)調(diào)用的結(jié)果構(gòu)成的數(shù)組
map和foreach區(qū)別
forEach()方法不會(huì)返回執(zhí)行結(jié)果,而是undefined。也就是說(shuō),forEach()會(huì)修改原來(lái)的數(shù)組。而map()方法會(huì)得到一個(gè)新的數(shù)組并返回。
forEach()的執(zhí)行速度 < map()的執(zhí)行速度
JSON.parse(JSON.stringify(obj)) 實(shí)現(xiàn)深拷貝需要注意的問(wèn)題
1. 如果obj里面有時(shí)間對(duì)象,則JSON.stringify后再JSON.parse的結(jié)果,只是字符串,不是時(shí)間對(duì)象;
2. 如果obj里有RegExp、Error對(duì)象,則序列化的結(jié)果將只得到空對(duì)象;
3. 如果obj里有函數(shù),undefined,則序列化的結(jié)果會(huì)把函數(shù)或 undefined丟失;
4. 如果obj里有NaN、Infinity和-Infinity,則序列化的結(jié)果會(huì)變成null
5. JSON.stringify()只能序列化對(duì)象的可枚舉的自有屬性,例如 如果obj中的對(duì)象是有構(gòu)造函數(shù)生成的, 則使用JSON.parse(JSON.stringify(obj))深拷貝后,會(huì)丟棄對(duì)象的constructor;
6. 如果對(duì)象中存在循環(huán)引用的情況也無(wú)法正確實(shí)現(xiàn)深拷貝;
js事件循環(huán)機(jī)制
js是一門(mén)單線程語(yǔ)言
1.所有任務(wù)都在主線程上執(zhí)行,形成一個(gè)執(zhí)行棧。
2.主線程之外,還存在一個(gè)"任務(wù)隊(duì)列"(task queue)。只要異步任務(wù)有了運(yùn)行結(jié)果,就在"任務(wù)隊(duì)列"之中放置一個(gè)事件。
3.一旦"執(zhí)行棧"中的所有同步任務(wù)執(zhí)行完畢,系統(tǒng)就會(huì)讀取"任務(wù)隊(duì)列"。那些對(duì)應(yīng)的異步任務(wù),結(jié)束等待狀態(tài),進(jìn)入執(zhí)行棧并開(kāi)始執(zhí)行。
4.主線程不斷重復(fù)上面的第三步。
宏任務(wù)與微任務(wù):
異步任務(wù)分為 宏任務(wù)(macrotask) 與 微任務(wù) (microtask),不同的API注冊(cè)的任務(wù)會(huì)依次進(jìn)入自身對(duì)應(yīng)的隊(duì)列中,然后等待 Event Loop 將它們依次壓入執(zhí)行棧中執(zhí)行。
宏任務(wù)(macrotask)::
script(整體代碼)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 環(huán)境)
微任務(wù)(microtask):
Promise、 MutaionObserver、process.nextTick(Node.js環(huán)境)
Event Loop(事件循環(huán)):
Event Loop(事件循環(huán))中,每一次循環(huán)稱(chēng)為 tick, 每一次tick的任務(wù)如下:
選擇最先進(jìn)入隊(duì)列的宏任務(wù)(通常是script整體代碼),如果有則執(zhí)行
檢查是否存在 Microtask,如果存在則不停的執(zhí)行,直至清空 microtask 隊(duì)列
更新render(每一次事件循環(huán),瀏覽器都可能會(huì)去更新渲染)
重復(fù)以上步驟
原型鏈
JavaScript?中所有的對(duì)象都是由它的原型對(duì)象繼承而來(lái)。而原型對(duì)象自身也是一個(gè)對(duì)象,它也有自己的原型對(duì)象,這樣層層上溯,就形成了一個(gè)類(lèi)似鏈表的結(jié)構(gòu),這就是原型鏈
所有原型鏈的終點(diǎn)都是?Object?函數(shù)的?prototype?屬性
Objec.prototype?指向的原型對(duì)象同樣擁有原型,不過(guò)它的原型是?null?,而?null?則沒(méi)有原型
每一個(gè)構(gòu)造函數(shù)都擁有一個(gè)?prototype?屬性,這個(gè)屬性指向一個(gè)對(duì)象,也就是原型對(duì)象
原型對(duì)象默認(rèn)擁有一個(gè)?constructor?屬性,指向指向它的那個(gè)構(gòu)造函數(shù)
每個(gè)對(duì)象都擁有一個(gè)隱藏的屬性?__proto__,指向它的原型對(duì)象
實(shí)例可以共享原型上面的屬性和方法
實(shí)例自身的屬性會(huì)屏蔽原型上面的同名屬性,實(shí)例上面沒(méi)有的屬性會(huì)去原型上面找
instanceof
最常用的確定原型指向關(guān)系的關(guān)鍵字,檢測(cè)的是原型,但是只能用來(lái)判斷兩個(gè)對(duì)象是否屬于實(shí)例關(guān)系, 而不能判斷一個(gè)對(duì)象實(shí)例具體屬于哪種類(lèi)型
通過(guò)使用?hasOwnProperty?可以確定訪問(wèn)的屬性是來(lái)自于實(shí)例還是原型對(duì)象
對(duì)象繼承方法
new做了什么操作
首先創(chuàng)建一個(gè)新的空對(duì)象
然后將空對(duì)象的__proto__指向構(gòu)造函數(shù)的原型
它將新生成的對(duì)象的__proto__屬性賦值為構(gòu)造函數(shù)的prototype屬性,使得通過(guò)構(gòu)造函數(shù)創(chuàng)建的所有對(duì)象可以共享相同的原型。
這意味著同一個(gè)構(gòu)造函數(shù)創(chuàng)建的所有對(duì)象都繼承自一個(gè)相同的對(duì)象,因此它們都是同一個(gè)類(lèi)的對(duì)象。
改變this的指向,指向空對(duì)象
對(duì)構(gòu)造函數(shù)的返回值做判斷,然后返回對(duì)應(yīng)的值
一般是返回第一步創(chuàng)建的空對(duì)象;
但是當(dāng)構(gòu)造函數(shù)有返回值時(shí)則需要做判斷再返回對(duì)應(yīng)的值,是對(duì)象類(lèi)型則返回該對(duì)象,是原始類(lèi)型則返回第一步創(chuàng)建的空對(duì)象。
apply()、call()和 bind() 是做什么的,區(qū)別,手撕源碼
相同點(diǎn):三者都可以改變 this 的指向
不同點(diǎn):
apply 方法傳入兩個(gè)參數(shù):一個(gè)是作為上下文的對(duì)象,另一個(gè)是作為函數(shù)所組成的數(shù)組
call 方法第一個(gè)參數(shù)也是作為作為函數(shù)上下文的對(duì)象,但是后面?zhèn)魅氲氖?b>一個(gè)參數(shù)列表,而不是單個(gè)數(shù)組
bind接受的參數(shù)有兩個(gè)部分,第一個(gè)參數(shù)是作為函數(shù)上下文的對(duì)象,第二部分參數(shù)是一個(gè)列表,可以接受多個(gè)參數(shù)
apply、call 方法都會(huì)使函數(shù)立即執(zhí)行,因此它們也可以用來(lái)調(diào)用函數(shù)
ajax原理,手撕源碼
ES6 中箭頭函數(shù) VS 普通函數(shù)的 this 指向
普通函數(shù)中 this
1. 查看函數(shù)在哪被調(diào)用。
2. 點(diǎn)左側(cè)有沒(méi)有對(duì)象?如果有,它就是 “this” 的引用。如果沒(méi)有,繼續(xù)第 3 步。
3. 該函數(shù)是不是用 “call”、“apply” 或者 “bind” 調(diào)用的?如果是,它會(huì)顯式地指明 “this” 的引用。如果不是,繼續(xù)第 4 步。
4. 該函數(shù)是不是用 “new” 調(diào)用的?如果是,“this” 指向的就是 JavaScript 解釋器新創(chuàng)建的對(duì)象。如果不是,繼續(xù)第 5 步。
5. 是否在“嚴(yán)格模式”下?如果是,“this” 就是 undefined,如果不是,繼續(xù)第 6 步。
6. JavaScript 很奇怪,“this” 會(huì)指向 “window” 對(duì)象。
ES6 箭頭函數(shù)中 this
1. 默認(rèn)指向定義它時(shí),所處上下文的對(duì)象的 this 指向;
? 偶爾沒(méi)有上下文對(duì)象,this 就指向 window
設(shè)計(jì)模式,應(yīng)用場(chǎng)景,手撕源碼
promise
Promise 對(duì)象代表一個(gè)異步操作,有三種狀態(tài):pending(進(jìn)行中)、fulfilled(已成功)和 rejected(已失?。V挥挟惒讲僮鞯慕Y(jié)果,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無(wú)法改變這個(gè)狀態(tài)
特點(diǎn):
對(duì)象的狀態(tài)不受外界影響
一旦狀態(tài)改變,就不會(huì)再變,任何時(shí)候都可以得到這個(gè)結(jié)果
Promise 新建后就會(huì)立即執(zhí)行
Promise 實(shí)例生成以后,可以用 then 方法分別指定 resolved 狀態(tài)和 rejected 狀態(tài)的回調(diào)函數(shù)。
Promise.prototype.catch 用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù),具有冒泡性質(zhì),會(huì)一直向后傳遞,直到被捕獲為止。也就是說(shuō),錯(cuò)誤總是會(huì)被下一個(gè)catch語(yǔ)句捕獲
then 方法返回的是一個(gè)新的Promise實(shí)例
promise.all,promise.race源碼手撕(讓你寫(xiě)一個(gè)請(qǐng)求,5秒內(nèi)執(zhí)行完就返回執(zhí)行結(jié)果,否則返回超時(shí))
垃圾回收
基本數(shù)據(jù)類(lèi)型
==和===的區(qū)別
隱式轉(zhuǎn)換(坑1)var a=?;console.log(a==1&&a==2&&a==3);//返回true?坑2)為什么[]==![])
如何判斷數(shù)組
js動(dòng)畫(huà)和css動(dòng)畫(huà)區(qū)別
dom0級(jí)事件dom2級(jí)事件
模塊化
防抖節(jié)流
深淺拷貝
事件流
瀏覽器渲染過(guò)程
generator
數(shù)組去重
數(shù)組展平
typeof 和instance?of
為什么0.1+0.2!=0.3
用symbol.iterator實(shí)現(xiàn)對(duì)象遍歷(清晰的記得是b站的問(wèn)題,難搞哦)
javascript的三種引入方式_喜你已久丶不棄的博客-CSDN博客_js引入方式
DOM 和 BOM 區(qū)別 - HoneyCY - 博客園 (cnblogs.com)
es5實(shí)現(xiàn)繼承 - 簡(jiǎn)書(shū) (jianshu.com)
吊打面試官前端系列(一): require和import的區(qū)別 - 云+社區(qū) - 騰訊云 (tencent.com)
Javascript異步編程的4種方法 - 阮一峰的網(wǎng)絡(luò)日志 (ruanyifeng.com)
web前端面試題@十二(數(shù)組去重,數(shù)組扁平化) - 簡(jiǎn)書(shū) (jianshu.com)
5種方式實(shí)現(xiàn)數(shù)組扁平化 - _wind - 博客園 (cnblogs.com)
判斷數(shù)據(jù)類(lèi)型的5種方法 - 簡(jiǎn)書(shū) (jianshu.com)
1.typeof
2.instanceof
3.object下的toString.call()
4.object的constructor
利用JS十分鐘判斷數(shù)組中存在元素的多種方式_javascript技巧_腳本之家 (jb51.net)
1.find()
2.indexOf()
3.filter()
4.some()
5.includes()
js去除字符串空格(空白符) - 一只看夕陽(yáng)的貓 - 博客園 (cnblogs.com)
前端常見(jiàn)跨域解決方案(全) - SegmentFault 思否
徹底弄懂jsonp原理及實(shí)現(xiàn)方法 - xiaobe - 博客園 (cnblogs.com)
防抖(debounce)和節(jié)流(throttle)的原理以及實(shí)現(xiàn)_Seeker-Joseph的博客-CSDN博客
js事件委托,事件冒泡及捕獲_么灬名字的博客-CSDN博客_js事件委托和事件冒泡
HTTP狀態(tài)碼
1xx:指示信息——表示請(qǐng)求已接收,繼續(xù)處理
2xx:成功——表示請(qǐng)求已經(jīng)被成功接收
3xx:重定向——要完成請(qǐng)求必須進(jìn)行更進(jìn)一步的操作
4xx:客戶(hù)端錯(cuò)誤——請(qǐng)求有語(yǔ)法錯(cuò)誤或請(qǐng)求無(wú)法實(shí)現(xiàn)
5xx:服務(wù)器錯(cuò)誤——服務(wù)器未能實(shí)現(xiàn)合法的請(qǐng)求
- 1XX 信息性狀態(tài)碼
? - 100 繼續(xù)
? - 101 切換協(xié)議
- 2XX 成功狀態(tài)碼
? - 200 OK 成功處理了請(qǐng)求
? - 204 No Content 請(qǐng)求處理成功,但沒(méi)有資源可返回
? - 206 Partial Content 請(qǐng)求資源的某一部分
- 3XX 重定向狀態(tài)碼
? - 301 永久性重定向,表示請(qǐng)求的資源已被分配了新的 URI
? - 302 臨時(shí)性重定向,資源的 URL 已臨時(shí)定位到其他位置
? - 303 告訴客戶(hù)端應(yīng)該用另一個(gè) URL 獲取資源
? - 304 表示客戶(hù)端發(fā)送附帶條件的請(qǐng)求時(shí),服務(wù)器端允許請(qǐng)求訪問(wèn)資源,但未滿(mǎn)足條件的情況
- 4XX 客戶(hù)端錯(cuò)誤狀態(tài)碼
? - 400 表示請(qǐng)求報(bào)文中存在語(yǔ)法錯(cuò)誤
? - 401 未授權(quán)
? - 403 服務(wù)器拒絕了請(qǐng)求
? - 404 服務(wù)器無(wú)法找到所請(qǐng)求的 URL
- 5XX 服務(wù)器錯(cuò)誤狀態(tài)碼
? - 500 內(nèi)部服務(wù)器錯(cuò)誤
? - 502 錯(cuò)誤網(wǎng)關(guān)
? - 503 服務(wù)器暫時(shí)處于超負(fù)載或正在進(jìn)行停機(jī)維護(hù),現(xiàn)在無(wú)法處理請(qǐng)求。
? - 504 響應(yīng)超時(shí)
瀏覽器緩存
瀏覽器將用戶(hù)請(qǐng)求過(guò)的靜態(tài)資源(html、css、js),存儲(chǔ)到電腦本地磁盤(pán)中,當(dāng)瀏覽器再次訪問(wèn)時(shí),就可以直接從本地加載了,不需要再去服務(wù)端請(qǐng)求了
緩存的優(yōu)點(diǎn):
減少了冗余的數(shù)據(jù)傳輸,節(jié)省網(wǎng)費(fèi)
減少服務(wù)器的負(fù)擔(dān),提升網(wǎng)站性能
加快了客戶(hù)端加載網(wǎng)頁(yè)的速度
arguments怎么轉(zhuǎn)化成真數(shù)組
遞歸setTimeout()和setInterval()有何不同