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面試題的難度不會很高,但是被面試官提問的概率還是很高的(親身經歷),希望各位練習生們 取其精華,去其糟粕,提取自己想要的內容,同時也歡迎你們提出寶貴的意見。