25道JS面試題總結,讓你實力更上一層樓
theme: v-green highlight: androidstudio
本文正在參加「金石計劃」
前言
雖然說2023年我們預想的金三變成了銅三,但是作為一個練習時長兩年半的資深前端練習生,也不得不為即將到來的銀四(可能是鐵四)準備著。 下面我就來給大家分享 25 道 JS 高頻面試題吧。助力廣大志同道合的練習生們更上一層樓。
1. JS陣列的常用的方法有哪些?
面對這樣的問題,首先我們要自己有一套總結思路,根據陣列的特點來總結,例如:增刪改查、排序、轉換、迭代。 這樣我們才可以保證我們可以完全的給面試官總結清楚。
1.增刪改查
增: push() unshift() splice() concat()
刪: pop() shift() splice() slice(不改變原陣列)
改: splice()
查: indexOf() includes() find()
-
排序
sort() reverse() 3. 轉換
join() 4. 迭代
forEach() map() filter() some() every()
2. JS字串的常用方法有哪些?
既然都問到了陣列,那字串也必不可少,我們同樣採取一樣的套路來總結
1.增刪改查
增: concat()
刪: slice() substr() substring()
改: trim() trimLeft() trimRight() trimStart() trimEnd()
repeat() padStart() padEnd()
toLocaleLowerCase() toLocaleUpperCase()
2.轉換
split()
3.模板匹配
replace() search() match()
3. 來談談JS中的型別轉換
面對這類問題,我們可以根據是什麼、有什麼、怎麼用三個方面去總結,齊總這三個方面中,我們最要解釋清楚的就是是什麼
,其他兩個方面我們可以想到什麼說什麼。
- 是什麼
JS的型別轉換用一句話解釋就是不同的型別之間運算需要先對資料的型別進行轉換,型別轉換是將一種資料型別轉換為另一種資料型別的過程。
-
顯示轉換
-
Number()
- String()
- Boolean()
- parseInt()
- 隱式轉換
- 比較運算子(==, !=, >, <, if, while)
-
算數運算子(+, -, *, /, %)
-
引用型別轉為原始型別的過程
呼叫物件上的toPrimitive()方法
- 如果obj是基本型別,直接返回
- 呼叫valueOf()方法,如果得到一個原始型別,則返回
- 呼叫toString 方法,如果得到一個原始型別,則返回
- 不能轉換,報錯
4. == VS ===
-
是什麼
-
對於 == ,如果雙方的型別不一樣的話,就會進行型別轉換 再進行比較。
- 對於 === 首先要求比較雙方型別相同, 再比較值是否相同。
5. 深拷貝和淺拷貝有什麼區別?實現一個深拷貝
- 是什麼
- 首先我們知道,基本型別存在棧中,引用型別存在堆中但是引用地址存在棧當中
- 淺拷貝如果拷貝的是原始型別,那就是拷貝的值,如果是引用型別,則拷貝的是引用地址
- 深拷貝通常是引用型別的拷貝,深拷貝會開闢一個新的記憶體空間,新的記憶體空間中存放的屬性和值與原物件完全相同,但是引用地址不同。
- 深拷貝的實現
function deepClone(obj) {
if (obj === null) return obj
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
if (typeof obj === 'function') return obj
let newObj = {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]
}
}
return newObj
}
6. 說說你對閉包的理解?
- 是什麼
閉包就是能夠讀取其他函式內部變數的函式。因為在JS中,只有函式內部的子函式才能讀取區域性變數,因此可以把閉包簡單理解成 " 定義在一個函式內部的函式" 。在本質上,閉包就是將函式內部和函式外部連線起來的一座橋樑。
-
使用場景
-
模擬私有方法
- setTimeout迴圈
- 匿名自執行函式
- 結果要快取場景
- 實現類和繼承
7. 聊聊JS的事件流和事件模型
- 是什麼
事件流描述的是從頁面中接受事件的順序。
- 過程
- 事件流順序:捕獲階段 -》 目標階段 -》 冒泡階段
- 模型
- 原始事件模型: DOM0 級別的事件
- 優點: 相容性好,繫結速度快
- 缺點: 只支援冒泡,不支援捕獲
- 標準事件模型
addEventListener
- IE事件模型
attachEvent
- 目標階段 -》冒泡階段
8. 說說你對JS中作用域的理解
- 是什麼
作用域就是變數和函式的可訪問的範圍,變數和函式生效的一個區域就可以稱為作用域,它控制著變數和函式的可見性和週期性。
-
有什麼
-
全域性作用域
- 塊級做使用者
- 函式作用域
- 詞法作用域
9. 聊聊你對原型、原型鏈的理解
- 是什麼
- 所有
引用型別
都有一個__proto__(隱式原型)
屬性,屬性值是一個普通的物件 - 所有
函式
都有一個prototype(原型)
屬性,屬性值是一個普通的物件 - 所有
引用型別的__proto__
屬性指向
它建構函式的prototype
-
原型都是物件,且原型中存在constructor用於記錄誰建立的這個原型
-
原型鏈
當我們試圖去訪問一個物件的屬性時,瀏覽器引擎會在該物件上查詢該屬性,當我們在該物件原型沒有找得到的話,我們就會順著constructor一層一層的往上查詢。我們將順著向上找的這種行為稱為原型鏈。
10. 聊聊JS的繼承
-
是什麼 簡單來說,通過某種手段,讓子類能夠訪問到父類的屬性和方法,這種手段就叫繼承
-
有什麼
-
原型鏈繼承
- 建構函式原型繼承
- 組合繼承
- 原型式繼承
- 寄生式繼承
- 寄生組合式繼承
11. 說說你對js中this的理解
- 是什麼
this關鍵字是函式執行時自動生成的一個內部物件, 只能在函式內使用,總是指向呼叫它的物件,this一旦被確定是不可以修改的。(箭頭函式本身沒有this,它指向的是函式外的this)
-
繫結規則
-
預設繫結
- 函式在那個詞法環境生效,函式中的this就繫結在哪裡
- 隱式繫結
- 當函式引用有上下文物件時,隱式繫結的規則就會把函式呼叫中的this繫結到這個上下文物件
- 隱式丟失
- 當隱式繫結的函式丟失了繫結物件,就會應用預設繫結(this永遠指向之後呼叫它的那個物件)。
- 顯示繫結
- call() apply() bind()
12. js中的執行上下文和執行棧是什麼?
- 是什麼
執行上下文 就是當前 JavaScript 程式碼被解析和執行時所在環境的抽象概念,JavaScript 中執行任何的程式碼都是在執行上下文中執行。
執行棧也叫呼叫棧,是用於儲存程式碼執行期間建立的所有執行上下文。
-
有什麼
-
全域性執行上下文
- 函式執行上下文
- Eval執行上下文
13. 說說typeof 和 instanceof 的區別?
- 是什麼
typeof 是一個操作符,主要的目的是檢測一個變數是不是基本資料型別的變數,同時也可以說是確定一個變數是字串、數值。布林值、undefined、還是函式的一種方法。
instanceof 運算子用於檢測建構函式的 prototype 屬性是否出現在某個例項物件的上。instanceof 可以準確判斷引用資料型別,但是不能正確判斷原始資料型別
14. 聊聊事件委託以及應用場景
- 是什麼
事件代理就是把原本需要繫結在子元素的響應事件(click、keydown......)委託給父元素,讓父元素擔當事件監聽的職務。也可以理解成將多個節點要繫結的事件委託到另一個節點身上,藉助了該節點的事件流機制完成我們需要的操作。
-
應用場景
-
操作列表
15. 說說new都幹了什麼
- 建立一個物件
- 將建構函式的this指向這個物件
- 將物件的原型指向建構函式的原型
- 返回該物件
16. Ajax的原理和實現
- 是什麼
Ajax的原理簡單來說,實際上就是通過XmlHttpRequest物件來向伺服器發非同步請求,從伺服器獲得資料,然後用javascript來操作DOM而更新頁面。
- 具體實現
function ajax(options) {
let {
url,
method= 'GET',
data= null,
dataType= 'JSON',
async = true,
cache = true,
success,
error
} = options
let xhr = new XMLHttpRequest()
// 處理引數
if(method.toUpperCase() === 'GET') {
if(data) {
url += '?'
for(let key in data) {
url += `${key}=${data[key]}&`
}
xhr.open(method, url, async)
xhr.send()
}
}else { //post
xhr.open(method, url, async)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.send(data)
}
xhr.onreadystatechange = () =>{
if(xhr.readyState === 4){
if( !/^(2|3)\d{2}$/.test(xhr.status)) {
error && error(xhr.statusText, xhr)
}
let result = handleDataType(xhr) //格式化處理
success && success(result, xhr)
}
}
function handleDataType(xhr) {
dataType = dataType.toUpperCase()
let result = xhr.responseText
switch(dataType) {
case 'TEXT':
break;
case 'JSON':
result = JSON.parse(result)
break;
case 'XML':
result = xhr.responseXML
break;
// ...
default:
}
return result
}
}
17. call, apply, bind的區別? 實現一個bind
- 是什麼
js中的apply、call、bind是三個函式方法,都可以用來改變函式的this指向。
apply和call的作用是一樣的,都是改變函式的this指向,並且立即執行該函式。區別在於傳入引數的方式不同,apply接收一個數組作為引數,而call則是一個一個引數傳入。
bind則是返回一個新的函式,改變函式的this指向,但不會立即執行該函式,需要手動呼叫。
這三個方法在實際開發中經常用到,特別是在處理函式作為引數傳遞的情況下,可以方便地改變函式的執行環境。
- bind的實現
Function.prototype.my_bind = function(obj) {
if (typeof this !== 'function') {
throw new TypeError('error')
}
obj = obj || window
const args = Array.prototype.slice.call(arguments, 1)
const _this = this
const pro = function() {}
if (this.prototype) {
pro.prototype = this.prototype
}
const bound = function () {
// 判斷該函式是否被new
return _this.apply( //
this instanceof pro ? this : obj,
args.concat(Array.prototype.slice.call(arguments))
)
}
bound.prototype = new pro() // 繼承到了foo的原型
return bound
}
18. 說說你對Event loop的理解
- 是什麼
Event loop 是為了解決JS單執行緒執行阻塞的問題,JS設計的一套迴圈機制。
Event Loop事件迴圈,其實就是JS引擎管理事件執行的一個流程,具體由執行環境確定。
-
有什麼
-
巨集任務
- cript, setTimeout, setInterval, I/O, UI-rendering/UI事件, postMessage
-
微任務
- Promise.then, nextTick, MutaionObserver
-
整體流程
-
巨集任務中的同步 -》
- 微任務 -》
- 頁面渲染 -》
- 下一次巨集任務 -》
19. 說說你對DOM的理解,常見的操作有哪些?
- 是什麼
DOM是一個物件,是HTML和XML文件的程式設計介面,其提供了一個結構化的表述並且定義了一種方式——程式可以對結構樹進行訪問,以改變文件的結構,樣式和內容。 從本質上說,它將web頁面和指令碼或程式語言連線起來了。
-
常見操作
-
建立節點
- createElement createTextNode createDocumentFragment createAttributr
-
查詢節點
- querSelector
-
更新節點
- innerHTML
-
新增節點
- appendChild
-
刪除節點
- removeChild
20. 說說你對BOM的理解, 常見的BOM操作有哪些?
- 是什麼
BOM(Browser Object Model)提供了獨立於內容而與瀏覽器視窗進行互動的物件,主要用於管理視窗與視窗之間的通訊,因此核心物件是window。
-
有什麼
-
history.go(-1)
- screen
- bavigator
- location
- window.open
21. 說說JS記憶體洩露的幾種情況
- 是什麼
由於某些原因,導致程式在執行過程中開銷的記憶體在程式執行完畢後沒有被釋放,造成了記憶體浪費
-
情況
-
閉包
- 回撥地域
- 遞迴
- 瀏覽器的垃圾回收機制
22. JS中本地儲存的方式有哪些,區別和應用場景呢?
- 有什麼
下面我從 name、 size、 when、 where四個方面介紹: - sessionStorage 5~10M 會話期間 本地儲存 - loaclStorage 5~10M 永久 本地儲存 - cookies 4kb 過期時間之內一直有效 自動相互傳遞 - indexedDB 無限大 永久 本地儲存
- 區別
空間大小、生命週期、資料與伺服器之間的互動方式各有不同。
-
應用場景
-
sessionStorage: 敏感賬號的一次性登入
- loaclStorage: 快取不敏感的資料
- cookies: 跟蹤使用者行為
- indexedDB: 存放大檔案
23. 說說函數語言程式設計的優缺點和理解
- 是什麼
函數語言程式設計是一種程式設計正規化,它將電腦運算視為函式運算,並且避免使用程式狀態以及易變物件 其中,λ演算是該語言最重要的基礎。而且λ演算的函式可以接受函式作為輸入的引數和輸出的返回值。
-
優缺點
-
更好管理狀態,減少出錯
- 複用性高
- 程式碼的可維護性高
- 效能降低,上下文切換開銷變大
- 瀏覽器垃圾回收困難
24. 說說JS數字精度丟失問題,怎麼解決?
- 是什麼
由於計算機不能儲存無限長度的小數,會取其近似值。而JS的Number採取的是IEEE754規範的64位雙精度浮點數 (意味著浮點數二進位制X = (-1)^s * M * 2^e)。
-
解決
-
Math.js
- bigNumber.js
- (x * 10^a) / 10^a
25. 說說函式的防抖和節流、怎麼實現?
- 是什麼
函式的防抖和節流都是用來優化體驗,減少相同函式沒有必要的呼叫
防抖是在規定時間內,沒有再次觸發,則執行函式 節流是在規定時間內,不管函式觸發多少次,只執行一次
-
實現
-
防抖
function debounce(fn, wait) {
var timeout;
return function() {
const context = this
const args = arguments
clearTimeout(timeout)
timeout = setTimeout(function() {
fn.apply(context, args)
}, wait);
}
}
- 節流
function throttle(fn, wait) {
var pre = 0
return function() {
var now = +new Date()
const context = this
if(now - pre > wait){
fn.call(context, e)
pre = now
}
}
}
總結
雖然上述的25道JS面試題的難度不會很高,但是被面試官提問的概率還是很高的(親身經歷),希望各位練習生們 取其精華,去其糟粕,提取自己想要的內容,同時也歡迎你們提出寶貴的意見。