javascript

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í)

瀏覽器緩存

瀏覽器緩存 - 掘金 (juejin.cn)

瀏覽器將用戶(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()有何不同

函數(shù)防抖和節(jié)流 - 簡(jiǎn)書(shū) (jianshu.com)

前端頁(yè)面性能優(yōu)化 - 簡(jiǎn)書(shū) (jianshu.com)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容