174道JavaScript 面試知識點總結(jié)(下)
文章有許多金典閱讀推薦建議點贊加收藏
來源 | https://github.com/CavsZhouyou/
全篇篇幅較長鸟悴,保障閱讀體驗,故拆分為上中下3部分發(fā)布
174道JavaScript 面試知識點總結(jié)(上)
174道JavaScript 面試知識點總結(jié)(中)
以下為第三部分
121. URL 和 URI 的區(qū)別别智?
URI: Uniform Resource Identifier 指的是統(tǒng)一資源標(biāo)識符
URL: Uniform Resource Location 指的是統(tǒng)一資源定位符
URN: Universal Resource Name 指的是統(tǒng)一資源名稱
URI 指的是統(tǒng)一資源標(biāo)識符闪朱,用唯一的標(biāo)識來確定一個資源挟冠,
它是一種抽象的定義和橙,也就是說汪茧,不管使用什么方法來定義,只要能唯一的標(biāo)識一個資源装黑,就可以稱為 URI。
URL 指的是統(tǒng)一資源定位符弓熏,URN 指的是統(tǒng)一資源名稱恋谭。
URL 和 URN 是 URI 的子集,URL 可以理解為使用地址來標(biāo)識資源挽鞠,URN 可以理解為使用名稱來標(biāo)識資源疚颊。
詳細(xì)資料可以參考:《HTTP 協(xié)議中 URI 和 URL 有什么區(qū)別?》《你知道 URL信认、URI 和 URN 三者之間的區(qū)別嗎材义?》《URI、URL 和 URN 的區(qū)別》
122. get 和 post 請求在緩存方面的區(qū)別
相關(guān)知識點:
get 請求類似于查找的過程狮杨,用戶獲取數(shù)據(jù),可以不用每次都與數(shù)據(jù)庫連接到忽,所以可以使用緩存橄教。
post 不同,post 做的一般是修改和刪除的工作喘漏,所以必須與數(shù)據(jù)庫交互护蝶,所以不能使用緩存。
因此 get 請求適合于請求緩存翩迈。
回答:
緩存一般只適用于那些不會更新服務(wù)端數(shù)據(jù)的請求持灰。
一般 get 請求都是查找請求,不會對服務(wù)器資源數(shù)據(jù)造成修改负饲,而 post 請求一般都會對服務(wù)器數(shù)據(jù)造成修改堤魁,
所以,一般會對 get 請求進(jìn)行緩存返十,很少會對 post 請求進(jìn)行緩存妥泉。
詳細(xì)資料可以參考:《HTML 關(guān)于 post 和 get 的區(qū)別以及緩存問題的理解》
123. 圖片的懶加載和預(yù)加載
相關(guān)知識點:
預(yù)加載:提前加載圖片,當(dāng)用戶需要查看時可直接從本地緩存中渲染洞坑。
懶加載:懶加載的主要目的是作為服務(wù)器前端的優(yōu)化盲链,減少請求數(shù)或延遲請求數(shù)。
兩種技術(shù)的本質(zhì):兩者的行為是相反的迟杂,一個是提前加載刽沾,一個是遲緩甚至不加載。
懶加載對服務(wù)器前端有一定的緩解壓力作用排拷,預(yù)加載則會增加服務(wù)器前端壓力侧漓。
回答:
懶加載也叫延遲加載,指的是在長網(wǎng)頁中延遲加載圖片的時機(jī)监氢,當(dāng)用戶需要訪問時火架,再去加載鉴象,
這樣可以提高網(wǎng)站的首屏加載速度,提升用戶的體驗何鸡,并且可以減少服務(wù)器的壓力纺弊。
它適用于圖片很多,頁面很長的電商網(wǎng)站的場景骡男。懶加載的實現(xiàn)原理是淆游,將頁面上的圖片的 src 屬性設(shè)置為空字符串,
將圖片的真實路徑保存在一個自定義屬性中隔盛,當(dāng)頁面滾動的時候犹菱,進(jìn)行判斷,
如果圖片進(jìn)入頁面可視區(qū)域內(nèi)吮炕,則從自定義屬性中取出真實路徑賦值給圖片的 src 屬性腊脱,以此來實現(xiàn)圖片的延遲加載。
預(yù)加載指的是將所需的資源提前請求加載到本地龙亲,這樣后面在需要用到時就直接從緩存取資源陕凹。
通過預(yù)加載能夠減少用戶的等待時間驾孔,提高用戶的體驗盗痒。
我了解的預(yù)加載的最常用的方式是使用 js 中的 image 對象匿刮,
通過為 image 對象來設(shè)置 scr 屬性帝嗡,來實現(xiàn)圖片的預(yù)加載怀读。
這兩種方式都是提高網(wǎng)頁性能的方式蹦玫,兩者主要區(qū)別是一個是提前加載奥秆,一個是遲緩甚至不加載阻肿。
懶加載對服務(wù)器前端有一定的緩解壓力作用谈竿,預(yù)加載則會增加服務(wù)器前端壓力团驱。
詳細(xì)資料可以參考:《懶加載和預(yù)加載》《網(wǎng)頁圖片加載優(yōu)化方案》《基于用戶行為的圖片等資源預(yù)加載》
124. mouseover 和 mouseenter 的區(qū)別?
當(dāng)鼠標(biāo)移動到元素上時就會觸發(fā) mouseenter 事件空凸,類似 mouseover店茶,
它們兩者之間的差別是 mouseenter 不會冒泡。
由于 mouseenter 不支持事件冒泡劫恒,導(dǎo)致在一個元素的子元素上進(jìn)入或離開的時候會觸發(fā)其 mouseover 和
mouseout 事件贩幻,但是卻不會觸發(fā) mouseenter 和 mouseleave 事件。
詳細(xì)資料可以參考:《mouseenter 與 mouseover 為何這般糾纏不清两嘴?》
125. js 拖拽功能的實現(xiàn)
相關(guān)知識點:
首先是三個事件丛楚,分別是 mousedown,mousemove憔辫,mouseup當(dāng)鼠標(biāo)點擊按下的時候趣些,需要一個 tag 標(biāo)識此時已經(jīng)
按下,可以執(zhí)行 mousemove 里面的具體方法贰您。clientX坏平,clientY 標(biāo)識的是鼠標(biāo)的坐標(biāo)拢操,分別標(biāo)識橫坐標(biāo)和縱坐標(biāo),
并且我們用 offsetX 和 offsetY 來表示元素的元素的初始坐標(biāo)舶替,
移動的舉例應(yīng)該是:鼠標(biāo)移動時候的坐標(biāo)-鼠標(biāo)按下去時候的坐標(biāo)令境。
也就是說定位信息為:鼠標(biāo)移動時候的坐標(biāo)-鼠標(biāo)按下去時候的坐標(biāo)+元素初始情況下的 offetLeft.
回答:
一個元素的拖拽過程,我們可以分為三個步驟顾瞪,第一步是鼠標(biāo)按下目標(biāo)元素舔庶,第二步是鼠標(biāo)保持按下的狀態(tài)移動鼠標(biāo),
第三步是鼠標(biāo)抬起陈醒,拖拽過程結(jié)束惕橙。這三步分別對應(yīng)了三個事件,
mousedown 事件钉跷,mousemove 事件和 mouseup 事件弥鹦。
只有在鼠標(biāo)按下的狀態(tài)移動鼠標(biāo)我們才會執(zhí)行拖拽事件,因此我們需要在 mousedown 事件中設(shè)置一個狀態(tài)來標(biāo)識鼠
已經(jīng)按下爷辙,然后在 mouseup 事件中再取消這個狀態(tài)彬坏。在 mousedown 事件中我們首先應(yīng)該判斷,目標(biāo)元素是否為
拖拽元素犬钢,如果是拖拽元素苍鲜,我們就設(shè)置狀態(tài)并且保存這個時候鼠標(biāo)的位置思灰。然后在 mousemove 事件中玷犹,
我們通過判斷鼠標(biāo)現(xiàn)在的位置和以前位置的相對移動,來確定拖拽元素在移動中的坐標(biāo)洒疚。
最后 mouseup 事件觸發(fā)后歹颓,清除狀態(tài),結(jié)束拖拽事件油湖。
詳細(xì)資料可以參考:《原生 js 實現(xiàn)拖拽功能基本思路》
126. 為什么使用 setTimeout 實現(xiàn) setInterval巍扛?怎么模擬?
相關(guān)知識點:
// 思路是使用遞歸函數(shù)乏德,不斷地去執(zhí)行 setTimeout 從而達(dá)到 setInterval 的效果
function mySetInterval(fn, timeout) {
// 控制器撤奸,控制定時器是否繼續(xù)執(zhí)行
var timer = { flag: true };
// 設(shè)置遞歸函數(shù),模擬定時器執(zhí)行喊括。
function interval() {
if (timer.flag) {
fn();
setTimeout(interval, timeout);
}
}
// 啟動定時器
setTimeout(interval, timeout);
// 返回控制器
return timer;
}
回答:
setInterval 的作用是每隔一段指定時間執(zhí)行一個函數(shù)胧瓜,
但是這個執(zhí)行不是真的到了時間立即執(zhí)行,它真正的作用是每隔一段時間將事件加入事件隊列中去郑什,
只有當(dāng)當(dāng)前的執(zhí)行棧為空的時候府喳,才能去從事件隊列中取出事件執(zhí)行。所以可能會出現(xiàn)這樣的情況蘑拯,
就是當(dāng)前執(zhí)行棧執(zhí)行的時間很長钝满,導(dǎo)致事件隊列里邊積累多個定時器加入的事件兜粘,當(dāng)執(zhí)行棧結(jié)束的時候,
這些事件會依次執(zhí)行弯蚜,因此就不能到間隔一段時間執(zhí)行的效果孔轴。
針對 setInterval 的這個缺點,我們可以使用 setTimeout 遞歸調(diào)用來模擬 setInterval熟吏,
這樣我們就確保了只有一個事件結(jié)束了距糖,我們才會觸發(fā)下一個定時器事件,這樣解決了 setInterval 的問題牵寺。
詳細(xì)資料可以參考:《用 setTimeout 實現(xiàn) setInterval》《setInterval 有什么缺點悍引?》
127. let 和 const 的注意點?
- 1.聲明的變量只在聲明時的代碼塊內(nèi)有效
- 2.不存在聲明提升
- 3.存在暫時性死區(qū)帽氓,如果在變量聲明前使用趣斤,會報錯
- 4.不允許重復(fù)聲明,重復(fù)聲明會報錯
128. 什么是 rest 參數(shù)黎休?
rest 參數(shù)(形式為...變量名)浓领,用于獲取函數(shù)的多余參數(shù)。
129. 什么是尾調(diào)用势腮,使用尾調(diào)用有什么好處联贩?
尾調(diào)用指的是函數(shù)的最后一步調(diào)用另一個函數(shù)。我們代碼執(zhí)行是基于執(zhí)行棧的捎拯,所以當(dāng)我們在一個函數(shù)里調(diào)用另一個
函數(shù)時泪幌,我們會保留當(dāng)前的執(zhí)行上下文,然后再新建另外一個執(zhí)行上下文加入棧中署照。
使用尾調(diào)用的話祸泪,因為已經(jīng)是函數(shù)的最后一步,所以這個時候我們可以不必再保留當(dāng)前的執(zhí)行上下文建芙,從而節(jié)省了內(nèi)存
没隘,這就是尾調(diào)用優(yōu)化。但是 ES6 的尾調(diào)用優(yōu)化只在嚴(yán)格模式下開啟禁荸,正常模式是無效的右蒲。
130. Symbol 類型的注意點?
- 1.Symbol 函數(shù)前不能使用 new 命令赶熟,否則會報錯瑰妄。
- 2.Symbol 函數(shù)可以接受一個字符串作為參數(shù),表示對 Symbol 實例的描述钧大,主要是為了在控制臺顯示翰撑,或者轉(zhuǎn)為字符串時,比較容易區(qū)分。
- 3.Symbol 作為屬性名眶诈,該屬性不會出現(xiàn)在 for...in涨醋、for...of 循環(huán)中,也不會被 Object.keys()逝撬、Object.getOwnPropertyNames()浴骂、JSON.stringify() 返回。
- 4.Object.getOwnPropertySymbols 方法返回一個數(shù)組宪潮,成員是當(dāng)前對象的所有用作屬性名的 Symbol 值溯警。
- 5.Symbol.for 接受一個字符串作為參數(shù),然后搜索有沒有以該參數(shù)作為名稱的 Symbol 值狡相。如果有梯轻,就返回這個 Symbol 值,否則就新建并返回一個以該字符串為名稱的 Symbol 值尽棕。
- 6.Symbol.keyFor 方法返回一個已登記的 Symbol 類型值的 key喳挑。
131. Set 和 WeakSet 結(jié)構(gòu)?
- 1.ES6 提供了新的數(shù)據(jù)結(jié)構(gòu) Set滔悉。它類似于數(shù)組伊诵,但是成員的值都是唯一的,沒有重復(fù)的值回官。
- 2.WeakSet 結(jié)構(gòu)與 Set 類似曹宴,也是不重復(fù)的值的集合。但是 WeakSet 的成員只能是對象歉提,而不能是其他類型的值笛坦。WeakSet 中的對象都是弱引用,即垃圾回收機(jī)制不考慮 WeakSet 對該對象的引用唯袄,
132. Map 和 WeakMap 結(jié)構(gòu)弯屈?
- 1.Map 數(shù)據(jù)結(jié)構(gòu)蜗帜。它類似于對象恋拷,也是鍵值對的集合,但是“鍵”的范圍不限于字符串厅缺,各種類型的值(包括對象)都可以當(dāng)作鍵蔬顾。
- 2.WeakMap 結(jié)構(gòu)與 Map 結(jié)構(gòu)類似,也是用于生成鍵值對的集合湘捎。但是 WeakMap 只接受對象作為鍵名( null 除外)诀豁,不接受其他類型的值作為鍵名。而且 WeakMap 的鍵名所指向的對象窥妇,不計入垃圾回收機(jī)制舷胜。
133. 什么是 Proxy ?
Proxy 用于修改某些操作的默認(rèn)行為活翩,等同于在語言層面做出修改烹骨,所以屬于一種“元編程”翻伺,
即對編程語言進(jìn)行編程。Proxy 可以理解成沮焕,在目標(biāo)對象之前架設(shè)一層“攔截”吨岭,外界對該對象的訪問,
都必須先通過這層攔截峦树,因此提供了一種機(jī)制辣辫,可以對外界的訪問進(jìn)行過濾和改寫。
Proxy 這個詞的原意是代理魁巩,用在這里表示由它來“代理”某些操作急灭,可以譯為“代理器”。
134. Reflect 對象創(chuàng)建目的谷遂?
- 1.將 Object 對象的一些明顯屬于語言內(nèi)部的方法(比如 Object.defineProperty化戳,放到 Reflect 對象上。
- 2.修改某些 Object 方法的返回結(jié)果埋凯,讓其變得更合理点楼。
- 3.讓 Object 操作都變成函數(shù)行為。
- 4.Reflect 對象的方法與 Proxy 對象的方法一一對應(yīng)白对,只要是 Proxy 對象的方法掠廓,
就能在 Reflect 對象上找到對應(yīng)的方法。這就讓 Proxy 對象可以方便地調(diào)用對應(yīng)的 Reflect 方法甩恼,
完成默認(rèn)行為蟀瞧,作為修改行為的基礎(chǔ)。也就是說条摸,不管 Proxy 怎么修改默認(rèn)行為悦污,你總可以在 Reflect 上獲取默認(rèn)行為。
135. require 模塊引入的查找方式钉蒲?
當(dāng) Node 遇到 require(X) 時切端,按下面的順序處理。
(1)如果 X 是內(nèi)置模塊(比如 require('http'))
a. 返回該模塊顷啼。 b. 不再繼續(xù)執(zhí)行踏枣。
(2)如果 X 以 "./" 或者 "/" 或者 "../" 開頭 a. 根據(jù) X 所在的父模塊,確定 X 的絕對路徑钙蒙。
b. 將 X 當(dāng)成文件茵瀑,依次查找下面文件,只要其中有一個存在躬厌,就返回該文件马昨,不再繼續(xù)執(zhí)行。
X X.js X.json X.node
c. 將 X 當(dāng)成目錄,依次查找下面文件鸿捧,只要其中有一個存在抢呆,就返回該文件,不再繼續(xù)執(zhí)行笛谦。
X/package.json(main字段) X/index.js X/index.json X/index.node
(3)如果 X 不帶路徑 a. 根據(jù) X 所在的父模塊抱虐,確定 X 可能的安裝目錄。
b. 依次在每個目錄中饥脑,將 X 當(dāng)成文件名或目錄名加載恳邀。
(4)拋出 "not found"
詳細(xì)資料可以參考:《require() 源碼解讀》
136. 什么是 Promise 對象,什么是 Promises/A+ 規(guī)范灶轰?
Promise 對象是異步編程的一種解決方案谣沸,最早由社區(qū)提出。Promises/A+ 規(guī)范是 JavaScript Promise 的標(biāo)準(zhǔn)笋颤,
規(guī)定了一個 Promise 所必須具有的特性乳附。Promise 是一個構(gòu)造函數(shù),接收一個函數(shù)作為參數(shù)伴澄,返回一個 Promise
實例赋除。一個 Promise 實例有三種狀態(tài),分別是 pending非凌、resolved 和 rejected举农,分別代表了進(jìn)行中、
已成功和已失敗敞嗡。實例的狀態(tài)只能由 pending 轉(zhuǎn)變 resolved 或者 rejected 狀態(tài)颁糟,并且狀態(tài)一經(jīng)改變,
就凝固了喉悴,無法再被改變了棱貌。狀態(tài)的改變是通過 resolve() 和 reject() 函數(shù)來實現(xiàn)的,我們可以在異步操作結(jié)束后
調(diào)用這兩個函數(shù)改變 Promise 實例的狀態(tài)箕肃,它的原型上定義了一個 then 方法婚脱,使用這個 then 方法可以為兩個
狀態(tài)的改變注冊回調(diào)函數(shù)。這個回調(diào)函數(shù)屬于微任務(wù)突雪,在本輪事件循環(huán)的末尾執(zhí)行起惕。
詳細(xì)資料可以參考:《Promises/A+ 規(guī)范》《Promise》
137. 手寫一個 Promise
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
function MyPromise(fn) {
// 保存初始化狀態(tài)
var self = this;
// 初始化狀態(tài)
this.state = PENDING;
// 用于保存 resolve 或者 rejected 傳入的值
this.value = null;
// 用于保存 resolve 的回調(diào)函數(shù)
this.resolvedCallbacks = [];
// 用于保存 reject 的回調(diào)函數(shù)
this.rejectedCallbacks = [];
// 狀態(tài)轉(zhuǎn)變?yōu)?resolved 方法
function resolve(value) {
// 判斷傳入元素是否為 Promise 值涡贱,如果是咏删,則狀態(tài)改變必須等待前一個狀態(tài)改變后再進(jìn)行改變
if (value instanceof MyPromise) {
return value.then(resolve, reject);
}
// 保證代碼的執(zhí)行順序為本輪事件循環(huán)的末尾
setTimeout(() => { // 只有狀態(tài)為 pending 時才能轉(zhuǎn)變,
if (self.state === PENDING) {
// 修改狀態(tài)
self.state = RESOLVED; // 設(shè)置傳入的值
self.value = value; // 執(zhí)行回調(diào)函數(shù)
self.resolvedCallbacks.forEach(callback => {
callback(value);
});
}
}, 0);
}
// 狀態(tài)轉(zhuǎn)變?yōu)?rejected 方法
function reject(value) {
// 保證代碼的執(zhí)行順序為本輪事件循環(huán)的末尾
setTimeout(() => { // 只有狀態(tài)為 pending 時才能轉(zhuǎn)變
if (self.state === PENDING) { // 修改狀態(tài)
self.state = REJECTED; // 設(shè)置傳入的值
self.value = value; // 執(zhí)行回調(diào)函數(shù)
self.rejectedCallbacks.forEach(callback => {
callback(value);
});
}
}, 0);
}
// 將兩個方法傳入函數(shù)執(zhí)行
try {
fn(resolve, reject);
} catch (e) {
// 遇到錯誤時问词,捕獲錯誤督函,執(zhí)行 reject 函數(shù)
reject(e);
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
// 首先判斷兩個參數(shù)是否為函數(shù)類型,因為這兩個參數(shù)是可選參數(shù)
onResolved = typeof onResolved === "function" ? onResolved : function (value) {
return value;
};
onRejected = typeof onRejected === "function" ? onRejected : function (error) {
throw error;
};
// 如果是等待狀態(tài),則將函數(shù)加入對應(yīng)列表中
if (this.state === PENDING) {
this.resolvedCallbacks.push(onResolved);
this.rejectedCallbacks.push(onRejected);
}
// 如果狀態(tài)已經(jīng)凝固辰狡,則直接執(zhí)行對應(yīng)狀態(tài)的函數(shù)
if (this.state === RESOLVED) {
onResolved(this.value);
}
if (this.state === REJECTED) {
onRejected(this.value);
}
};
138. 如何檢測瀏覽器所支持的最小字體大蟹孢丁?
用 JS 設(shè)置 DOM 的字體為某一個值宛篇,然后再取出來娃磺,如果值設(shè)置成功,就說明支持叫倍。
139. 怎么做 JS 代碼 Error 統(tǒng)計偷卧?
error 統(tǒng)計使用瀏覽器的 window.error 事件。
140. 單例模式模式是什么吆倦?
單例模式保證了全局只有一個實例來被訪問听诸。比如說常用的如彈框組件的實現(xiàn)和全局狀態(tài)的實現(xiàn)。
141. 策略模式是什么蚕泽?
策略模式主要是用來將方法的實現(xiàn)和方法的調(diào)用分離開晌梨,外部通過不同的參數(shù)可以調(diào)用不同的策略。
我主要在 MVP 模式解耦的時候用來將視圖層的方法定義和方法調(diào)用分離须妻。
142. 代理模式是什么仔蝌?
代理模式是為一個對象提供一個代用品或占位符,以便控制對它的訪問荒吏。比如說常見的事件代理掌逛。
143. 中介者模式是什么?
中介者模式指的是司倚,多個對象通過一個中介者進(jìn)行交流豆混,而不是直接進(jìn)行交流,這樣能夠?qū)⑼ㄐ诺母鱾€對象解耦动知。
144. 適配器模式是什么皿伺?
適配器用來解決兩個接口不兼容的情況,不需要改變已有的接口盒粮,通過包裝一層的方式實現(xiàn)兩個接口的正常協(xié)作鸵鸥。
假如我們需要一種新的接口返回方式,但是老的接口由于在太多地方已經(jīng)使用了丹皱,不能隨意更改妒穴,
這個時候就可以使用適配器模式。比如我們需要一種自定義的時間返回格式摊崭,
但是我們又不能對 js 時間格式化的接口進(jìn)行修改讼油,這個時候就可以使用適配器模式。
更多關(guān)于設(shè)計模式的資料可以參考:《前端面試之道》《JavaScript 設(shè)計模式》《JavaScript 中常見設(shè)計模式整理》
145. 觀察者模式和發(fā)布訂閱模式有什么不同呢簸?
發(fā)布訂閱模式其實屬于廣義上的觀察者模式在觀察者模式中矮台,觀察者需要直接訂閱目標(biāo)事件乏屯。在目標(biāo)發(fā)出內(nèi)容改變的事件
后,直接接收事件并作出響應(yīng)瘦赫。而在發(fā)布訂閱模式中辰晕,發(fā)布者和訂閱者
之間多了一個調(diào)度中心。調(diào)度中心一方面從發(fā)布者接收事件确虱,
另一方面向訂閱者發(fā)布事件含友,訂閱者需要在調(diào)度中心中訂閱事件。通過調(diào)度中心實現(xiàn)了發(fā)布者和訂閱者關(guān)系的解耦校辩。
使用發(fā)布訂閱者模式更利于我們代碼的可維護(hù)性唱较。
詳細(xì)資料可以參考:《觀察者模式和發(fā)布訂閱模式有什么不同?》
146. Vue 的生命周期是什么召川?
Vue 的生命周期指的是組件從創(chuàng)建到銷毀的一系列的過程南缓,被稱為 Vue 的生命周期。
通過提供的 Vue 在生命周期各個階段的鉤子函數(shù)荧呐,我們可以很好的在 Vue 的各個生命階段實現(xiàn)一些操作汉形。
147. Vue 的各個生命階段是什么?
Vue 一共有8個生命階段倍阐,分別是創(chuàng)建前概疆、創(chuàng)建后、加載前峰搪、加載后岔冀、更新前、更新后概耻、銷毀前和銷毀后使套,
每個階段對應(yīng)了一個生命周期的鉤子函數(shù)。(1)beforeCreate 鉤子函數(shù)鞠柄,在實例初始化之后侦高,在數(shù)據(jù)監(jiān)聽和事件配置
之前觸發(fā)。因此在這個事件中我們是獲取不到 data 數(shù)據(jù)的厌杜。
(2)created 鉤子函數(shù)奉呛,在實例創(chuàng)建完成后觸發(fā),此時可以訪問 data夯尽、methods 等屬性瞧壮。
但這個時候組件還沒有被掛載到頁面中去,所以這個時候訪問不到 $el 屬性匙握。
一般我們可以在這個函數(shù)中進(jìn)行一些頁面初始化的工作咆槽,比如通過 ajax 請求數(shù)據(jù)來對頁面進(jìn)行初始化。
(3)beforeMount 鉤子函數(shù)肺孤,在組件被掛載到頁面之前觸發(fā)罗晕。在 beforeMount 之前济欢,會找到對應(yīng)的 template赠堵,并
編譯成 render 函數(shù)小渊。
(4)mounted 鉤子函數(shù),在組件掛載到頁面之后觸發(fā)茫叭。此時可以通過 DOM API 獲取到頁面中的 DOM 元素酬屉。
(5)beforeUpdate 鉤子函數(shù),在響應(yīng)式數(shù)據(jù)更新時觸發(fā)揍愁,發(fā)生在虛擬 DOM 重新渲染和打補(bǔ)丁之前呐萨,
這個時候我們可以對可能會被移除的元素做一些操作,比如移除事件監(jiān)聽器莽囤。
(6)updated 鉤子函數(shù)谬擦,虛擬 DOM 重新渲染和打補(bǔ)丁之后調(diào)用。
(7)beforeDestroy 鉤子函數(shù)朽缎,在實例銷毀之前調(diào)用惨远。一般在這一步我們可以銷毀定時器、解綁全局事件等话肖。
(8)destroyed 鉤子函數(shù)北秽,在實例銷毀之后調(diào)用,調(diào)用后最筒,Vue 實例中的所有東西都會解除綁定贺氓,
所有的事件監(jiān)聽器會被移除,所有的子實例也會被銷毀床蜘。當(dāng)我們使用 keep-alive 的時候辙培,還有兩個鉤子函數(shù),
分別是 activated 和 deactivated 邢锯。用 keep-alive 包裹的組件在切換時不會進(jìn)行銷毀虏冻,
而是緩存到內(nèi)存中并執(zhí)行 deactivated 鉤子函數(shù),命中緩存渲染后會執(zhí)行 actived 鉤子函數(shù)弹囚。
詳細(xì)資料可以參考:《vue 生命周期深入》《Vue 實例》
148. Vue 組件間的參數(shù)傳遞方式厨相?
(1)父子組件間通信第一種方法是子組件通過 props 屬性來接受父組件的數(shù)據(jù),然后父組件在子組件上注冊監(jiān)聽事件鸥鹉,
子組件通過 emit 觸發(fā)事件來向父組件發(fā)送數(shù)據(jù)蛮穿。
第二種是通過 ref 屬性給子組件設(shè)置一個名字。
父組件通過 $refs 組件名來獲得子組件毁渗,子組件通過 $parent 獲得父組件践磅,這樣也可以實現(xiàn)通信。
第三種是使用 provider/inject灸异,在父組件中通過 provider 提供變量府适,在子組件
中通過 inject 來將變量注入到組件中羔飞。
不論子組件有多深,只要調(diào)用了 inject 那么就可以注入 provider 中的數(shù)據(jù)檐春。
(2)兄弟組件間通信第一種是使用 eventBus 的方法逻淌,它的本質(zhì)是通過創(chuàng)建一個空的 Vue 實例來作為消息傳遞的對象
,通信的組件引入這個實例疟暖,通信的組件通過在這個實例上監(jiān)聽和觸發(fā)事件卡儒,來實現(xiàn)消息的傳遞。
第二種是通過 $parent.$refs 來獲取到兄弟組件俐巴,也可以進(jìn)行通信骨望。
(3)任意組件之間使用 eventBus ,其實就是創(chuàng)建一個事件中心欣舵,相當(dāng)于中轉(zhuǎn)站擎鸠,可以用它來傳遞事件和接收事件。
如果業(yè)務(wù)邏輯復(fù)雜缘圈,很多組件之間需要同時處理一些公共的數(shù)據(jù)劣光,這個時候采用上面這一些方法可能不利于項目的維護(hù)。
這個時候可以使用 vuex 准验,vuex 的思想就是將這一些公共的數(shù)據(jù)抽離出來赎线,將它作為一個全局的變量來管理,
然后其他組件就可以對這個公共數(shù)據(jù)進(jìn)行讀寫操作糊饱,這樣達(dá)到了解耦的目的垂寥。
詳細(xì)資料可以參考:《VUE 組件之間數(shù)據(jù)傳遞全集》
149. computed 和 watch 的差異?
(1)computed 是計算一個新的屬性另锋,并將該屬性掛載到 Vue 實例上滞项,
而 watch 是監(jiān)聽已經(jīng)存在且已掛載到 Vue 實例上的數(shù)據(jù),所以用 watch 同樣可以監(jiān)聽 computed 計算屬性的變化夭坪。
(2)computed 本質(zhì)是一個惰性求值的觀察者文判,具有緩存性,只有當(dāng)依賴變化后室梅,第一次訪問 computed 屬性戏仓,
才會計算新的值。而 watch 則是當(dāng)數(shù)據(jù)發(fā)生變化便會調(diào)用執(zhí)行函數(shù)亡鼠。
(3)從使用場景上說赏殃,computed 適用一個數(shù)據(jù)被多個數(shù)據(jù)影響,而 watch 適用一個數(shù)據(jù)影響多個數(shù)據(jù)间涵。
詳細(xì)資料可以參考:《做面試的不倒翁:淺談 Vue 中 computed 實現(xiàn)原理》《深入理解 Vue 的 watch 實現(xiàn)原理及其實現(xiàn)方式》
150. vue-router 中的導(dǎo)航鉤子函數(shù)
(1)全局的鉤子函數(shù) beforeEach 和 afterEachbeforeEach 有三個參數(shù)仁热,to 代表要進(jìn)入的路由對象,
from 代表離開的路由對象勾哩。next 是一個必須要執(zhí)行的函數(shù)抗蠢,如果不傳參數(shù)举哟,那就執(zhí)行下一個鉤子函數(shù),
如果傳入 false迅矛,則終止跳轉(zhuǎn)妨猩,如果傳入一個路徑,則導(dǎo)航到對應(yīng)的路由诬乞,如果傳入 error 册赛,則導(dǎo)航終止钠导,
error 傳入錯誤的監(jiān)聽函數(shù)震嫉。(2)單個路由獨享的鉤子函數(shù) beforeEnter,它是在路由配置上直接進(jìn)行定義的牡属。
(3)組件內(nèi)的導(dǎo)航鉤子主要有這三種:beforeRouteEnter票堵、beforeRouteUpdate、beforeRouteLeave逮栅。
它們是直接在路由組件內(nèi)部直接進(jìn)行定義的悴势。
詳細(xì)資料可以參考:《導(dǎo)航守衛(wèi)》
151. <svg xmlns="http://www.w3.org/2000/svg" role="img" focusable="false" viewBox="0 -750 3547.3 950" aria-hidden="true" style="vertical-align: -0.452ex;width: 8.026ex;height: 2.149ex;"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="matrix(1 0 0 -1 0 0)"><g data-mml-node="math"><g data-mml-node="mo" transform="translate(2612.8, 0)"><text data-variant="normal" transform="matrix(1 0 0 -1 0 0)" font-size="934.6px" font-family="serif">和</text></g></g></g></svg>router 的區(qū)別?
$route 是“路由信息對象”措伐,包括 path特纤,params,hash侥加,query捧存,fullPath,matched担败,name 等路由信息參數(shù)昔穴。
而 $router 是“路由實例”對象包括了路由的跳轉(zhuǎn)方法,鉤子函數(shù)等提前。
152. vue 常用的修飾符吗货?
.prevent: 提交事件不再重載頁面;.stop: 阻止單擊事件冒泡狈网;.self: 當(dāng)事件發(fā)生在該元素本身而不是子元素的
時候會觸發(fā)宙搬;
153. vue 中 key 值的作用?
vue 中 key 值的作用可以分為兩種情況來考慮拓哺。第一種情況是 v-if 中使用 key勇垛。由于 Vue 會盡可能高效地渲染元
素,通常會復(fù)用已有元素而不是從頭開始渲染拓售。因此當(dāng)我們使用 v-if 來實現(xiàn)元素切換的時候窥摄,如果切換前后含有相同
類型的元素,那么這個元素就會被復(fù)用础淤。如果是相同的 input 元素崭放,
那么切換前后用戶的輸入不會被清除掉哨苛,這樣是不符合需求的。
因此我們可以通過使用 key 來唯一的標(biāo)識一個元素币砂,這個情況下建峭,使用 key 的元素不會被復(fù)用。
這個時候 key 的作用是用來標(biāo)識一個獨立的元素决摧。第二種情況是 v-for 中使用 key亿蒸。
用 v-for 更新已渲染過的元素列表時,
它默認(rèn)使用“就地復(fù)用”的策略掌桩。如果數(shù)據(jù)項的順序發(fā)生了改變边锁,Vue 不會移動 DOM 元素來匹配數(shù)據(jù)項的順序,
而是簡單復(fù)用此處的每個元素波岛。因此通過為每個列表項提供一個 key 值茅坛,來以便 Vue 跟蹤元素的身份,
從而高效的實現(xiàn)復(fù)用则拷。這個時候 key 的作用是為了高效的更新渲染虛擬 DOM贡蓖。
詳細(xì)資料可以參考:《Vue 面試中,經(jīng)常會被問到的面試題 Vue 知識點整理》《Vue2.0 v-for 中 :key 到底有什么用煌茬?》《vue 中 key 的作用》
154. computed 和 watch 區(qū)別斥铺?
computed 是計算屬性,依賴其他屬性計算值坛善,并且 computed 的值有緩存晾蜘,只有當(dāng)計算值變化才會返回內(nèi)容。
watch 監(jiān)聽到值的變化就會執(zhí)行回調(diào)浑吟,在回調(diào)中可以進(jìn)行一些邏輯操作笙纤。
155. keep-alive 組件有什么作用?
如果你需要在組件切換的時候组力,保存一些組件的狀態(tài)防止多次渲染敷燎,就可以使用 keep-alive 組件包裹需要保存的組件琳状。
156. vue 中 mixin 和 mixins 區(qū)別凑懂?
mixin 用于全局混入勾笆,會影響到每個組件實例。mixins 應(yīng)該是我們最常使用的擴(kuò)展組件的方式了候衍。
如果多個組件中有相同的業(yè)務(wù)邏輯笼蛛,就可以將這些邏輯剝離出來,
通過 mixins 混入代碼蛉鹿,比如上拉下拉加載數(shù)據(jù)這種邏輯等等滨砍。
另外需要注意的是 mixins 混入的鉤子函數(shù)會先于組件內(nèi)的鉤子函數(shù)執(zhí)行,
并且在遇到同名選項的時候也會有選擇性的進(jìn)行合并
詳細(xì)資料可以參考:《前端面試之道》《混入》
157. 開發(fā)中常用的幾種 Content-Type ?
(1)application/x-www-form-urlencoded瀏覽器的原生 form 表單惋戏,如果不設(shè)置 enctype 屬性领追,
那么最終就會以 application/x-www-form-urlencoded 方式提交數(shù)據(jù)。
該種方式提交的數(shù)據(jù)放在 body 里面响逢,數(shù)據(jù)按照 key1=val1&key2=val2 的方式進(jìn)行編碼绒窑,
key 和 val 都進(jìn)行了 URL轉(zhuǎn)碼。
(2)multipart/form-data該種方式也是一個常見的 POST 提交方式舔亭,通常表單上傳文件時使用該種方式些膨。
(3)application/json告訴服務(wù)器消息主體是序列化后的 JSON 字符串。
(4)text/xml該種方式主要用來提交 XML 格式的數(shù)據(jù)钦铺。
詳細(xì)資料可以參考:《常用的幾種 Content-Type》
158. 如何封裝一個 javascript 的類型判斷函數(shù)?
function getType(value) { // 判斷數(shù)據(jù)是 null 的情況
if (value === null) { return value + ""; }
// 判斷數(shù)據(jù)是引用類型的情況
if (typeof value === "object") {
let valueClass = Object.prototype.toString.call(value),
type = valueClass.split(" ")[1].split("");
type.pop();
return type.join("").toLowerCase();
} else {
// 判斷數(shù)據(jù)是基本數(shù)據(jù)類型的情況和函數(shù)的情況
return typeof value;
}
}
詳細(xì)資料可以參考:《JavaScript 專題之類型判斷(上)》
159. 如何判斷一個對象是否為空對象职抡?
function checkNullObj(obj) {
return Object.keys(obj).length === 0;
}
詳細(xì)資料可以參考:《js 判斷一個 object 對象是否為空》
160. 使用閉包實現(xiàn)每隔一秒打印 1,2,3,4
// 使用閉包實現(xiàn)
for (var i = 0; i < 5; i++) {
(function (i) {
setTimeout(function () {
console.log(i); }, i * 1000);
})(i);
}
// 使用 let 塊級作用域
for (let i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i); }, i * 1000);
}
161. 手寫一個 jsonp
function jsonp(url, params, callback) {
// 判斷是否含有參數(shù)
let queryString = url.indexOf("?") === "-1" ? "?" : "&";
// 添加參數(shù)
for (var k in params) {
if (params.hasOwnProperty(k)) {
queryString += k + "=" + params[k] + "&";
}
}
// 處理回調(diào)函數(shù)名
let random = Math.random().toString().replace(".", ""),
callbackName = "myJsonp" + random;
// 添加回調(diào)函數(shù)
queryString += "callback=" + callbackName;
// 構(gòu)建請求
let scriptNode = document.createElement("script");
scriptNode.src = url + queryString;
window[callbackName] = function () {
// 調(diào)用回調(diào)函數(shù)
callback(...arguments);
// 刪除這個引入的腳本
document.getElementsByTagName("head")[0].removeChild(scriptNode);
}; // 發(fā)起請求
document.getElementsByTagName("head")[0].appendChild(scriptNode);
}
詳細(xì)資料可以參考:《原生 jsonp 具體實現(xiàn)》《jsonp 的原理與實現(xiàn)》
162. 手寫一個觀察者模式缚甩?
var events = (function () {
var topics = {}; return { // 注冊監(jiān)聽函數(shù)
subscribe: function (topic, handler) {
if (!topics.hasOwnProperty(topic)) { topics[topic] = [];
}
topics[topic].push(handler);
},
// 發(fā)布事件窑邦,觸發(fā)觀察者回調(diào)事件
publish: function (topic, info) {
if (topics.hasOwnProperty(topic)) {
topics[topic].forEach(function (handler) {
handler(info);
});
}
},
// 移除主題的一個觀察者的回調(diào)事件
remove: function (topic, handler) {
if (!topics.hasOwnProperty(topic)) return;
var handlerIndex = -1;
topics[topic].forEach(function (item, index) {
if (item === handler) {
handlerIndex = index;
}
});
if (handlerIndex >= 0) {
topics[topic].splice(handlerIndex, 1);
}
}, // 移除主題的所有觀察者的回調(diào)事件
removeAll: function (topic) {
if (topics.hasOwnProperty(topic)) {
topics[topic] = [];
}
}
};
})();
詳細(xì)資料可以參考:《JS 事件模型》
163. EventEmitter 實現(xiàn)
class EventEmitter {
constructor() {
this.events = {};
} on(event, callback) {
let callbacks = this.events[event] || [];
callbacks.push(callback);
this.events[event] = callbacks; return this;
} off(event, callback) {
let callbacks = this.events[event];
this.events[event] = callbacks && callbacks.filter(fn => fn !== callback); return this;
} emit(event, ...args) {
let callbacks = this.events[event];
callbacks.forEach(fn => { fn(...args); }); return this;
} once(event, callback) {
let wrapFun = function (...args) {
callback(...args); this.off(event, wrapFun);
};
this.on(event, wrapFun);
return this;
}
}
164. 一道常被人輕視的前端 JS 面試題
function Foo() {
getName = function () { alert(1); };
return this;
}
Foo.getName = function () { alert(2); };
Foo.prototype.getName = function () { alert(3); };
var getName = function () { alert(4); };
function getName() { alert(5); }
//請寫出以下輸出結(jié)果:Foo.getName();
// 2getName();
// 4Foo().getName();
// 1getName();
// 1new Foo.getName();
// 2new Foo().getName();
// 3new new Foo().getName(); // 3
詳細(xì)資料可以參考:《前端程序員經(jīng)常忽視的一個 JavaScript 面試題》《一道考察運算符優(yōu)先級的 JavaScript 面試題》《一道常被人輕視的前端 JS 面試題》
165. 如何確定頁面的可用性時間,什么是 Performance API冈钦?
Performance API 用于精確度量郊丛、控制、增強(qiáng)瀏覽器的性能表現(xiàn)瞧筛。
這個 API 為測量網(wǎng)站性能厉熟,提供以前沒有辦法做到的精度揍瑟。
使用 getTime 來計算腳本耗時的缺點,
首先绢片,getTime方法(以及 Date 對象的其他方法)都只能精確到毫秒級別(一秒的千分之一)岛琼,
想要得到更小的時間差別就無能為力了。
其次槐瑞,這種寫法只能獲取代碼運行過程中的時間進(jìn)度,無法知道一些后臺事件的時間進(jìn)度,
比如瀏覽器用了多少時間從服務(wù)器加載網(wǎng)頁祠挫。為了解決這兩個不足之處猬错,ECMAScript 5引入“高精度時間戳”這個 API,
部署在 performance 對象上茸歧。它的精度可以達(dá)到1毫秒的千分之一(1秒的百萬分之一)倦炒。
navigationStart:當(dāng)前瀏覽器窗口的前一個網(wǎng)頁關(guān)閉,發(fā)生 unload 事件時的 Unix 毫秒時間戳软瞎。如果沒有前一個網(wǎng)頁逢唤,
則等于 fetchStart 屬性。loadEventEnd:返回當(dāng)前網(wǎng)頁 load 事件的回調(diào)函數(shù)運行結(jié)束時的 Unix 毫秒時間戳涤浇。
如果該事件還沒有發(fā)生鳖藕,返回 0。
根據(jù)上面這些屬性只锭,可以計算出網(wǎng)頁加載各個階段的耗時著恩。比如,網(wǎng)頁加載整個過程的耗時的計算方法如下:
var t = performance.timing;var pageLoadTime = t.loadEventEnd - t.navigationStart;
詳細(xì)資料可以參考:《Performance API》
166. js 中的命名規(guī)則
(1)第一個字符必須是字母蜻展、下劃線(_)或美元符號($)
(2)余下的字符可以是下劃線伍茄、美元符號或任何字母或數(shù)字字符一般我們推薦使用駝峰法來對變量名進(jìn)行命名
敷矫,因為這樣可以與 ECMAScript 內(nèi)置的函數(shù)和對象命名格式保持一致曹仗。
詳細(xì)資料可以參考:《ECMAScript 變量》
167. js 語句末尾分號是否可以省略?
在 ECMAScript 規(guī)范中讥脐,語句結(jié)尾的分號并不是必需的俱萍。但是我們一般最好不要省略分號告丢,
因為加上分號一方面有利于我們代碼的可維護(hù)性岳颇,另一方面也可以避免我們在對代碼進(jìn)行壓縮時出現(xiàn)錯誤栗精。
168. Object.assign()
Object.assign() 方法用于將所有可枚舉屬性的值從一個或多個源對象復(fù)制到目標(biāo)對象悲立。它將返回目標(biāo)對象薪夕。
169. Math.ceil 和 Math.floor
Math.ceil() === 向上取整埂淮,函數(shù)返回一個大于或等于給定數(shù)字的最小整數(shù)误窖。
Math.floor() === 向下取整毒费,函數(shù)返回一個小于或等于給定數(shù)字的最大整數(shù)溪厘。
170. js for 循環(huán)注意點
for (var i = 0, j = 0; i < 5, j < 9; i++, j++) {
console.log(i, j);
}// 當(dāng)判斷語句含有多個語句時,以最后一個判斷語句的值為準(zhǔn),因此上面的代碼會執(zhí)行 10 次惑艇。
// 當(dāng)判斷語句為空時,循環(huán)會一直進(jìn)行秽荤。
171. 一個列表牍氛,假設(shè)有 100000 個數(shù)據(jù),這個該怎么辦玩祟?
我們需要思考的問題:該處理是否必須同步完成?數(shù)據(jù)是否必須按順序完成?解決辦法:
(1)將數(shù)據(jù)分頁宴抚,利用分頁的原理,每次服務(wù)器端只返回一定數(shù)目的數(shù)據(jù),瀏覽器每次只對一部分進(jìn)行加載。
(2)使用懶加載的方法,每次加載一部分?jǐn)?shù)據(jù),其余數(shù)據(jù)當(dāng)需要使用時再去加載。
(3)使用數(shù)組分塊技術(shù)胁镐,基本思路是為要處理的項目創(chuàng)建一個隊列,然后設(shè)置定時器每過一段時間取出一部分?jǐn)?shù)據(jù)阿弃,
然后再使用定時器取出下一個要處理的項目進(jìn)行處理,接著再設(shè)置另一個定時器嗤谚。
172. js 中倒計時的糾偏實現(xiàn)椅野?
在前端實現(xiàn)中我們一般通過 setTimeout 和 setInterval 方法來實現(xiàn)一個倒計時效果炼蛤。
但是使用這些方法會存在時間偏差的問題子寓,這是由于 js 的程序執(zhí)行機(jī)制造成的,
setTimeout 和 setInterval 的作用是隔一段時間將回調(diào)事件加入到事件隊列中,因此事件并不是立即執(zhí)行的也殖,
它會等到當(dāng)前執(zhí)行棧為空的時候再取出事件執(zhí)行捆毫,因此事件等待執(zhí)行的時間就是造成誤差的原因途样。
一般解決倒計時中的誤差的有這樣兩種辦法:(1)第一種是通過前端定時向服務(wù)器發(fā)送請求獲取最新的時間差,
以此來校準(zhǔn)倒計時時間遏插。(2)第二種方法是前端根據(jù)偏差時間來自動調(diào)整間隔時間的方式來實現(xiàn)的。
這一種方式首先是以 setTimeout 遞歸的方式來實現(xiàn)倒計時,然后通過一個變量來記錄已經(jīng)倒計時的秒數(shù)。
每一次函數(shù)調(diào)用的時候粗井,首先將變量加一浇衬,然后根據(jù)這個變量和每次的間隔時間耘擂,
我們就可以計算出此時無偏差時應(yīng)該顯示的時間。然后將當(dāng)前的真實時間與這個時間相減篙悯,
這樣我們就可以得到時間的偏差大小,因此我們在設(shè)置下一個定時器的間隔大小的時候匿垄,
我們就從間隔時間中減去這個偏差大小,以此來實現(xiàn)由于程序執(zhí)行所造成的時間誤差的糾正糠悼。
詳細(xì)資料可以參考:《JavaScript 前端倒計時糾偏實現(xiàn)》
173. 進(jìn)程間通信的方式倔喂?
- 1.管道通信
- 2.消息隊列通信
- 3.信號量通信
- 4.信號通信
- 5.共享內(nèi)存通信
- 6.套接字通信
詳細(xì)資料可以參考:《進(jìn)程間 8 種通信方式詳解》《進(jìn)程與線程的一個簡單解釋》
174. 如何查找一篇英文文章中出現(xiàn)頻率最高的單詞馒索?
function findMostWord(article) { // 合法性判斷
if (!article) return; // 參數(shù)處理
article = article.trim().toLowerCase();
let wordList = article.match(/[a-z]+/g),
visited = [], maxNum = 0, maxWord = "";
article = " " + wordList.join(" ") + " ";
// 遍歷判斷單詞出現(xiàn)次數(shù)
wordList.forEach(function (item) {
if (visited.indexOf(item) < 0) {
// 加入 visited
visited.push(item);
let word = new RegExp(" " + item + " ", "g"),
num = article.match(word).length;
if (num > maxNum) {
maxNum = num;
maxWord = item;
}
}
});
return maxWord + " " + maxNum;
}
推薦閱讀: