1.什么是類數組對象
????????一個擁有 length 屬性和若干索引屬性的對象就可以被稱為類數組對象,類數組對象和數組類似,但是不能調用數組的方法。常見的類數組對象有 arguments 和 DOM 方法的返回結果,還有一個函數也可以被看作是類數組對象,因為它含有 length 屬性值,代表可接收的參數個數。
常見的類數組轉換為數組的方法有這樣幾種:
(1)通過 call 調用數組的 slice 方法來實現轉換
Array.prototype.slice.call(arrayLike);
(2)通過 call 調用數組的 splice 方法來實現轉換
Array.prototype.splice.call(arrayLike, 0);
(3)通過 apply 調用數組的 concat 方法來實現轉換
Array.prototype.concat.apply([], arrayLike);
(4)通過 Array.from 方法來實現轉換
Array.from(arrayLike);
2.Js有哪些基本數據類型
簡單類型
Number
String
Boolean
Symbol
Null
Undefined
復雜數據類型
Object
Array
Function
Map
Set
3.Js判斷類型的幾種方式,有什么區別
方法 | 支持類型 | 跨框架可靠性 | 原始類型 | 引用類型細分 | 特殊說明 |
typeof | 原始類型、函數 | 可靠 | ?? | ? | null 返回 "object" |
instanceof | 引用類型 | 不可靠 | ? | ?? | 依賴原型鏈 |
Object.prototype.toString | 所有類型 | 可靠 | ?? | ?? | 最全面 |
1.typeof
作用:返回變量的原始類型(字符串形式)。 語法:
typeof variable
返回值:
"undefined"
(未定義)
"boolean"
(布爾值)
"string"
(字符串)
"number"
(數字)
"bigint"
(BigInt類型)
"symbol"
(Symbol類型)
"function"
(函數)
"object"
(對象、數組、null
等)
"object"
(未識別的ES6+類型,如Map
、Set
)特點:
對原始類型(除
null
)有效,但對引用類型無法細分(如數組、對象、null
均返回"object"
)。
typeof null === "object"
(歷史遺留問題)。無法區分對象的具體類型(如
Date
、RegExp
)。適用場景:快速判斷原始類型或函數。
2.instanceof
作用:檢測變量是否為某個構造函數的實例(基于原型鏈)。 語法:
variable instanceof Constructor
返回值:true
或false
。特點:
適合判斷引用類型(如數組、自定義類)。
無法判斷原始類型(如
1 instanceof Number
為false
,除非用new Number(1)
創建)。跨框架/窗口(iframe)時會失效,因為不同全局環境下的構造函數不共享原型。
若原型鏈被修改,結果可能不準確。
3.Object.prototype.toString.call()
作用:返回變量的內部
[[Class]]
類型標識。 語法:Object.prototype.toString.call(variable)
返回值:形如"[object Type]"
的字符串,如"[object Array]"
、"[object Null]"
。特點:
最全面的類型判斷方法,支持所有內置類型(包括
null
和undefined
)。可通過
Symbol.toStringTag
自定義類型標簽(ES6+),但大部分內置對象不受影響。對原始類型和引用類型均有效。
適用場景:需要精確判斷所有類型(包括ES6+新增類型)。
4. Js的事件循環(Event Loop)是什么
????????Js是一種單線程語言(HTML5提供了Web Worker可以開啟子線程),同步代碼直接交由Js引擎執行,異步代碼則交由瀏覽器或Node.js執行。
? ? ? ? 關鍵概念:
- 調用棧(Call Stack)??:用于跟蹤當前正在執行的函數(執行上下文)。當函數被調用時,它被壓入棧;當函數返回時,它被彈出棧。
- ??任務隊列(Task Queue)??:也稱為宏任務隊列(Macro Task Queue),包括 setTimeout、setInterval、I/O、事件回調等。
- ??微任務隊列(Micro Task Queue)??:包括 Promise 的回調(then/catch/finally)、MutationObserver、queueMicrotask 等。微任務在當前宏任務結束后立即執行,且會清空整個微任務隊列,直到隊列為空。
- ??渲染(Render)步驟??:在事件循環中,可能會在宏任務和微任務之間進行頁面渲染(UI 更新),但這不是事件循環規范的一部分,而是瀏覽器行為。
? ? ? ? 事件循環執行過程:
- 首先依次執行調用棧中的全部同步代碼
- 處理微任務:
- 調用棧清空后會執行全部微任務
- 微任務執行期間加入的微任務也會順次執行
- 渲染更新(瀏覽器行為與Js事件循環無關)
- 執行一個宏任務
- 重復循環
? ? ? ? 常見的宏任務與微任務
- 宏任務
- setTimeout/setInterval
- I/O操作
- 瀏覽器渲染
- 事件回調(click等事件)
- 腳本執行(不同的script標簽)
- 微任務
- Promise相關方法(Promise.then等)
- MutationObserver(Dom變化監視)
5.下面代碼的輸出順序是什么(事件循環)
async function async1() {console.log('async1 start');await async2(); // 后續為微任務console.log('async1 end');
}async function async2() {console.log('async2');
}console.log('script start');setTimeout(function() {console.log('setTimeout');
}, 0) // 宏任務async1();new Promise(function(resolve) {console.log('promise1');resolve();
}).then(function() { // 微任務console.log('promise2');
});console.log('script end');
// 順序如下
script start // 第一個同步任務
async1 start // async1方法中的第一個同步任務
async2 // await修飾的方法立即執行,輸出async2,async1方法進入微任務隊列
promise1 // 執行new Promise,輸出promise1,并進入微任務隊列
script end // 執行同步代碼
async1 end // 執行微任務隊列中的第一個任務
promise2 // 執行微任務隊列的第二個任務
setTimeout // 執行宏任務
6.Js原生如何獲取cookie
document.cookie // 結果是一個Json字符串,可使用JSON.parse()轉換
7.Promise.all/allSettled/any/race的區別
Promise.all
核心:等待所有 Promise 全部完成(fulfilled),或遇到第一個失敗(rejected)。
結果:
全部成功時:返回一個數組,元素為每個 Promise 的成功值(順序與輸入一致)。
遇到失敗時:立即拒絕(reject),返回第一個失敗的 Promise 的原因。
適用場景:多個異步操作強依賴,需要所有結果都成功(如:并發請求多個接口)。
示例:
Promise.all([promise1, promise2]) .then(values => console.log(values)) // 所有成功:[value1, value2].catch(error => console.log(error)); // 任一失敗(返回第一個錯誤)
Promise.allSettled
核心:等待所有 Promise 全部完成(無論成功或失敗)。
結果:永遠成功(resolve),返回一個數組,每個元素為對象:
{ status: "fulfilled", value: <result> }
(成功)
{ status: "rejected", reason: <error> }
(失敗)適用場景:需知道每個操作的最終狀態(無論成功失敗)(如:批量操作后匯總結果)。
示例:
Promise.allSettled([promise1, promise2]).then(results => { results.forEach(result => { if (result.status === "fulfilled") console.log(result.value); else console.error(result.reason); }); });
Promise.any
核心:等待第一個 成功(fulfilled)的 Promise,或所有都失敗。
結果:
任一成功時:返回第一個成功的值。
全部失敗時:拒絕(reject)并拋出
AggregateError
,包含所有錯誤信息。適用場景:獲取最快成功的操作(如:多服務器請求,取最快響應)。
示例:
Promise.any([promise1, promise2]).then(value => console.log(value)) // 第一個成功的值.catch(errors => console.log(errors)); // 所有失敗(AggregateError 包含錯誤數組)
Promise.race
核心:等待第一個 完成(無論成功或失敗)的 Promise。
結果:返回第一個完成的 Promise 的結果(可能是成功值或失敗原因)。
適用場景:競速場景(如:請求超時控制)。
示例:
// 實現超時控制 const timeout = new Promise((_, reject) =>setTimeout(() => reject("Timeout"), 1000) ); Promise.race([fetchData(), timeout]).then(data => console.log(data)) // 在超時前完成.catch(error => console.log(error)); // 超時或請求失敗
對比表格
方法 行為描述 成功條件 失敗條件 返回值類型 ??Promise.all?? 所有成功或任一失敗(短路) 全部成功 第一個失敗 數組(成功值) ??Promise.allSettled?? 等待所有完成(無論成敗) 永不失敗 - 對象數組(包含狀態和值/原因) ??Promise.any?? 等待第一個成功或所有失敗 第一個成功 全部失敗 第一個成功的值 ??Promise.race?? 等待第一個完成(無論成敗) 第一個完成的結果是成功 第一個完成的結果是失敗 第一個完成的結果 關鍵區別
all
vsallSettled
:all
會在遇到失敗時立即終止,而allSettled
始終等待所有結果。
any
vsrace
:any
只關心第一個成功(忽略失敗),race
關心第一個完成(無論成敗)。錯誤處理:
any
在所有失敗時返回AggregateError
;其他方法直接返回單條錯誤原因。根據實際需求選擇合適的方法:需要全部成功用
all
,容忍失敗用allSettled
,取最快成功用any
,競速場景用race
。
?8.原型對象、構造函數、實例之間的關系
對象 | 指向關系 | 屬性/方法 |
??構造函數?? | prototype → 原型對象 | Person.prototype |
??原型對象?? | constructor → 構造函數 | Person.prototype.constructor |
??實例?? | __proto__ → 原型對象 | person.__proto__ |
9.Proxy和Object.defineProperty的區別?
Object.defineProperty
: 只能對已存在的屬性進行劫持,無法攔截新增的屬性和刪除的屬性(需要通過Vue.set
和Vue.delete
實現響應式),由于劫持基于屬性級別,對于大規模對象或者數組來說會導致性能下降;(Vue2響應式的實現方法)
Proxy
: 劫持整個對象,并返回一個代理對象,提高了初始化性能,同時可以攔截新增屬性和刪除屬性。(Vue3響應式的實現方法)
10.使用Proxy代理一個對象,是對這個對象所有層級進行了劫持嗎
????????不會,proxy只代理外層對象,內層對象需要單獨proxy代理
?11.簡單數組去重的方法
- 使用
Set
只運行存儲唯一值的特點
const arr = [1, 2, 2, 3, 3, 4, 5, 5, 6]
const uniqueArr = [...new Set(arr)]
// 或 const uniqueArr = Array.from(new Set(arr))
-
使用
includes
方法檢查新數值中是否存在該元素
const arr = [1, 2, 2, 3, 3, 4, 5, 5, 6]
const uniqueArr = []
arr.forEach(val => { if(!uniqueArr.includes(val)) { uniqueArr.push(val) }
}
-
使用
indexOf
方法,檢查新數組中是否存在該元素
const arr = [1, 2, 2, 3, 3, 4, 5, 5, 6]
const uniqueArr = []
arr.forEach(val => { if(uniqueArr.indexOf(val) === -1) { uniqueArr.push(val) }
}
-
使用
filter
方法過濾第一次出現的元素
const arr = [1, 2, 2, 3, 3, 4, 5, 5, 6]
const uniqueArr = arr.filter((val, index, self) => self.indexOf(val) === index
}
-
使用
reduce
方法遍歷數組元素
const arr = [1, 2, 2, 3, 3, 4, 5, 5, 6]
const uniqueArr = arr.reduce((res, current) => {if(!res.includes(current) {res.push(current) } return res
}, [])
12.對象數組如何去重
-
id(或其他鍵相同算為重復)
-
使用
Map
-
function uniqueById(arr) { return [ ...new Map( arr.map(item => [item.id, item]) ).values() ];
} // 使用示例
const users = [ { id: 1, name: 'John' },{ id: 2, name: 'Jane' }, { id: 1, name: 'Johnny' }, // 相同id { id: 3, name: 'Alice' }
];
console.log(uniqueById(users));
// [
// { id: 1, name: 'Johnny' },
// { id: 2, name: 'Jane' },
// { id: 3, name: 'Alice' }
// ]
- 使用
filter
+緩存?
function uniqueById(arr) { const seen = new Set(); return arr.filter(item => { const duplicate = seen.has(item.id); seen.add(item.id); return !duplicate; });
}
- ?使用
reduce?
function uniqueById(arr) { return Object.values(arr.reduce((acc, item) => { acc[item.id] = item; return acc; }, {}) );
}
- 完全相同算重復
-
使用JSON.stringify(簡單對象)
-
function deepUnique(arr) { const seen = new Set(); return arr.filter(item => { const serialized = JSON.stringify(item); return seen.has(serialized) ? false : seen.add(serialized); });
}
// 使用示例
const items = [ { id: 1, name: 'Apple' }, { id: 2, name: 'Banana' }, { id: 1, name: 'Apple' }, // 相同對象 { id: 1, name: 'apple' } // 不同對象
];
console.log(deepUnique(items));
// [
// { id: 1, name: 'Apple' },
// { id: 2, name: 'Banana' },
// { id: 1, name: 'apple' }
// ]
- ?Lodash的isEqual方法(復雜對象推薦)? ? ? ? ? ? ? ? ?
// 需要安裝lodash:npm install lodash
import _ from 'lodash';
function deepUnique(arr) { return arr.reduce((acc, item) => { const isDuplicate = acc.some(accItem => _.isEqual(accItem, item)); return isDuplicate ? acc : [...acc, item]; }, []);
}
創建于2025.6.2,后續繼續更新