
基礎(chǔ)知識復(fù)習(xí)- 關(guān)于Js中數(shù)組的詳細(xì)學(xué)習(xí)
數(shù)組的基本定義
JavaScript 數(shù)組是一種用于存儲?有序數(shù)據(jù)集合?的特殊對象,可包含任意類型的元素(如數(shù)字、字符串、對象、布爾值等),并通過從 0
開始的數(shù)字索引訪問元素?
核心特性
動態(tài)長度?:數(shù)組長度可動態(tài)增減,無需預(yù)先聲明固定大小?
元素類型靈活?:支持混合存儲不同類型的數(shù)據(jù)
const arr = [1, "text", true, { key: "value" }, [2, 3]];
其中包含數(shù)字、字符串、布爾值、對象和嵌套數(shù)組?
連續(xù)內(nèi)存結(jié)構(gòu)?:數(shù)組在內(nèi)存中表現(xiàn)為一段?連續(xù)的內(nèi)存地址?,數(shù)組名指向首地址?
與普通對象的區(qū)別與聯(lián)系
聯(lián)系?:數(shù)組本質(zhì)是由對象派生而來,可調(diào)用部分對象方法(如 toString
)?
區(qū)別?:索引順序?:數(shù)組元素按嚴(yán)格數(shù)字順序排列,對象屬性無序?。操作方法?:數(shù)組提供專有方法(如 push
、map
)用于操作元素集合,對象無此類內(nèi)置方法?
使用場景
數(shù)據(jù)列表存儲?:適用于需保持順序的數(shù)據(jù)集合(如排行榜、表格行數(shù)據(jù))?
批量操作?:通過循環(huán)或高階函數(shù)(如 forEach
、filter
)高效處理多元素?
注意事項
?性能優(yōu)化?:頻繁增刪元素時,優(yōu)先使用 push
/pop
(操作尾部)而非 unshift
/shift
(操作頭部),避免索引重排開銷?
基本概念
數(shù)據(jù)集合?:數(shù)組是?相同類型元素?的集合,所有元素存儲于?連續(xù)的內(nèi)存空間?中,元素類型可以是整型、字符型等任意一致的數(shù)據(jù)類型?
元素約束?:數(shù)組中的元素個數(shù)不能為0,且必須在定義時明確大?。–99標(biāo)準(zhǔn)前)或通過變長數(shù)組動態(tài)指定(C99標(biāo)準(zhǔn)后)?
數(shù)組的元素組成
元素標(biāo)識?:數(shù)組元素是?變量?,通過?數(shù)組名 + 下標(biāo)?唯一標(biāo)識。例如 arr
表示數(shù)組 arr
的第一個元素?
訪問規(guī)則?:元素需先通過數(shù)組定義分配內(nèi)存空間后,才能逐個通過下標(biāo)訪問,?不支持直接引用整個數(shù)組??
下標(biāo)范圍?:數(shù)組下標(biāo)從 0
開始,依次遞增至?數(shù)組長度減1?。例如長度為 n
的數(shù)組,最大有效下標(biāo)為 n-1
?索引作用?:下標(biāo)用于定位元素在數(shù)組中的?順序位置?,類似于數(shù)學(xué)集合中的索引概念,確保數(shù)據(jù)的有序性和快速訪問?
數(shù)組操作
JavaScript 數(shù)組使用方括號 []
表示,元素以逗號分隔,可包含任意類型的數(shù)據(jù)(如數(shù)值、字符串、對象、其他數(shù)組等)?
// 混合類型數(shù)組
const mixedArr = [1, "text", true, { key: "value" }, [4, 5]];
// 空數(shù)組
const emptyArr = [];
數(shù)組的創(chuàng)建方式
字面量方式 直接使用方括號 []
定義元素,是最簡潔、常用的方式?
const arr1 = [1, 2, 3];
const arr2 = ["a", "b", "c"];
構(gòu)造函數(shù)方式 使用 new Array()
或 Array()
創(chuàng)建,但需注意參數(shù)特性
單一數(shù)值參數(shù)?:表示數(shù)組長度(生成空槽數(shù)組)?
多個參數(shù)或非數(shù)值參數(shù)?:作為數(shù)組元素?
const arr3 = new Array(3); // [empty × 3](稀疏數(shù)組)
const arr4 = new Array(1, "a"); // [1, "a"]
?其他創(chuàng)建方式
?擴展操作符?:復(fù)制或合并現(xiàn)有數(shù)組?
const arr5 = [...arr1, ...arr2]; // [1, 2, 3, "a", "b", "c"]
Array.of()
?:明確將參數(shù)作為元素,避免構(gòu)造函數(shù)歧義?
const arr6 = Array.of(5);
Array.from()
?:將可迭代對象(如字符串、Set
)轉(zhuǎn)換為數(shù)組?
const arr7 = Array.from("abc");
元素訪問與長度獲取
索引訪問?:通過下標(biāo)(從 0 開始)直接獲取元素。
const fruits = ["apple", "banana"];
console.log(fruits);
console.log(fruits.length);
首尾元素?:通過 arr
和 arr[arr.length-1]
獲取首尾元素
數(shù)組長度?:使用 length
屬性獲取元素數(shù)量。
遍歷數(shù)組元素
for
循環(huán)?:通過索引遍歷。
for (let i = 0; i < fruits.length; i++) {
console.log(fruits[i]);
}
forEach
方法?:遍歷元素并執(zhí)行回調(diào)。
fruits.forEach((item, index) => {
console.log(index, item);
});
?高階函數(shù)?:如 map
、filter
、reduce
等。
onst lengths = fruits.map(fruit => fruit.length); // 計算長度 ?
const longFruits = fruits.filter(fruit => fruit.length > 5); // 篩選結(jié)果 ?
?元素查找與篩選
?按值查找?:
indexOf()
:返回第一個匹配的索引(無匹配返回 -1
)。
lastIndexOf()
:返回最后一個匹配的索引 ?
const idx = fruits.indexOf("banana");
條件查找?:
find()
:返回第一個符合條件的元素。
findIndex()
:返回第一個符合條件的索引
const item = fruits.find(fruit => fruit.startsWith("b"));
批量篩選?:filter()
返回符合條件的新數(shù)組
嵌套數(shù)組的檢索
需在數(shù)組中查找子數(shù)組,可通過以下方式:
循環(huán)檢查?:使用 for
循環(huán)結(jié)合 Array.isArray()
判斷元素是否為數(shù)組。
const arr = [1, [2, 3], 4];
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
console.log("子數(shù)組:", arr[i]); // [2, 3] ?:ml-citation{ref="4,6" data="citationList"}
}
}
?批量篩選?:通過 filter()
提取所有子數(shù)組。
const subArrays = arr.filter(item => Array.isArray(item));
數(shù)組的擴展操作
ES6 擴展運算符
復(fù)制數(shù)組 用擴展運算符可快速創(chuàng)建數(shù)組的淺拷貝,避免引用傳遞問題?
const original = [1, 2, 3];
const copy = [...original]; // 新數(shù)組,與原數(shù)組無引用關(guān)系
合并數(shù)組
合并多個數(shù)組時無需調(diào)用 concat
,直接通過擴展運算符實現(xiàn)?
const arr1 = [1, 2], arr2 = [3, 4];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4]
函數(shù)參數(shù)傳遞
將數(shù)組元素展開為函數(shù)參數(shù),替代 apply
方法?
function sum(a, b, c) { return a + b + c; }
const nums = [1, 2, 3];
sum(...nums); // 6
動態(tài)添加元素?
在現(xiàn)有數(shù)組中插入新元素,保持代碼簡潔性?
const base = [2, 3];
const newArr = [1, ...base, 4]; // [1, 2, 3, 4]
實例方法的擴展
數(shù)據(jù)操作基礎(chǔ)方法
增刪元素?:
push()
/pop()
:尾部操作元素?
unshift()
/shift()
:頭部操作元素?
splice()
:任意位置增刪或替換元素?
let arr = [1, 2];
arr.push(3); // [1, 2, 3]
arr.splice(1, 1, 4); // [1, 4, 3]
高階函數(shù)處理數(shù)據(jù)
遍歷與轉(zhuǎn)換?:
map()
:映射新數(shù)組?
filter()
:篩選符合條件的元素?
reduce()
:累計計算為單個值?
const nums = [1, 2, 3];
const doubled = nums.map(x => x * 2); // [2, 4, 6]
const sum = nums.reduce((acc, cur) => acc + cur, 0); // 6
?查找與判斷?:
find()
:返回首個匹配元素?
some()
/every()
:判斷元素是否滿足條件?
const users = [{id: 1}, {id: 2}];
const user = users.find(u => u.id === 2); // {id: 2}
高級技巧
?解構(gòu)賦值結(jié)合擴展運算符?
提取數(shù)組首尾元素時,可配合擴展運算符快速實現(xiàn)?
const [first, ...rest] = [1, 2, 3];
console.log(first); // 1
console.log(rest); // [2, 3]
與 Math
函數(shù)結(jié)合?
直接傳遞數(shù)組參數(shù)進(jìn)行數(shù)學(xué)計算?
const nums = [5, 2, 8];
Math.max(...nums); // 8
數(shù)組的增刪改查
添加元素
末尾添加? push()
:添加一個或多個元素到數(shù)組末尾,返回新數(shù)組長度。
const arr = [1, 2];
arr.push(3); // 返回 3,數(shù)組變?yōu)?[1, 2, 3]
支持鏈?zhǔn)秸{(diào)用:arr.push(4).push(5)
(需注意返回值變化)?
開頭添加? unshift()
:添加一個或多個元素到數(shù)組開頭,返回新數(shù)組長度。
arr.unshift(0); // 返回 4,數(shù)組變?yōu)?[0, 1, 2, 3]
性能較低,需移動所有元素索引?
中間插入? splice(startIndex, 0, newElement)
:從指定位置插入元素,不刪除原元素。
arr.splice(2, 0, "a", "b"); // 數(shù)組變?yōu)?[0, 1, "a", "b", 2, 3]
刪除元素
末尾刪除? pop()
:刪除并返回最后一個元素。
const last = arr.pop(); // last = 3,數(shù)組變?yōu)?[0, 1, "a", "b", 2]
?開頭刪除? shift()
:刪除并返回第一個元素。
const first = arr.shift(); // first = 0,數(shù)組變?yōu)?[1, "a", "b", 2]
指定位置刪除
splice(startIndex, deleteCount)
:刪除指定數(shù)量元素,返回被刪除元素的數(shù)組
const deleted = arr.splice(1, 2); // deleted = ["a", "b"],數(shù)組變?yōu)?[1, 2]
修改元素
?直接索引賦值 通過下標(biāo)直接修改元素
?使用 splice
替換元素 splice(startIndex, deleteCount, newElement)
:刪除并插入新元素。
arr.splice(0, 1, "start"); // 刪除第一個元素并插入 "start",數(shù)組變?yōu)?["start", "newValue"] ?
查詢元素
索引訪問? 通過下標(biāo)直接訪問元素:
const elem = arr[1];
條件查詢? includes()
:判斷是否包含某元素,返回布爾值。
arr.includes("start"); // true
find()
:返回第一個滿足條件的元素。
const result = arr.find(item => item === "start"); // "start" ?
遍歷查詢? forEach()
、map()
等方法遍歷數(shù)組處理數(shù)據(jù)?
數(shù)組的拷貝
淺拷貝與深拷貝的核心差異在于?是否共享嵌套數(shù)據(jù)的引用?。淺拷貝適合輕量級場景,而深拷貝需權(quán)衡性能與數(shù)據(jù)獨立性需求,優(yōu)先選擇穩(wěn)定實現(xiàn)
淺拷貝詳解
淺拷貝(Shallow Copy)指僅復(fù)制數(shù)組的?頂層元素?,若元素為基本類型(如數(shù)字、字符串),則直接復(fù)制值;若為引用類型(如嵌套數(shù)組、對象),則復(fù)制其內(nèi)存地址,導(dǎo)致新舊數(shù)組共享嵌套數(shù)據(jù)?
?核心特點?:
修改頂層基本類型元素時,原數(shù)組不受影響;
修改嵌套的引用類型元素時,原數(shù)組和新數(shù)組會同步變化?
實現(xiàn)方法
?slice()
? 截取數(shù)組片段返回新數(shù)組,嵌套引用類型共享地址。let copy = arr.slice()
const originalArray = [1, 2, 3, { name: 'obj' }];
const copiedArray = originalArray.slice(); // 淺拷貝
copiedArray[0] = 99; // 修改基本類型(不影響原數(shù)組)
copiedArray[3].name = 'new'; // 修改對象屬性(會影響原數(shù)組對象)
console.log(originalArray); // [1, 2, 3, { name: 'new' }]
console.log(copiedArray); // [99, 2, 3, { name: 'new' }]
無參調(diào)用:
slice()
不傳參數(shù)時,默認(rèn)截取整個數(shù)組(0
到length-1
)獨立數(shù)組:新數(shù)組與原數(shù)組的引用不同,但對象元素共享引用
非破壞性:原數(shù)組不會被修改
擴展運算符 ...
? 展開數(shù)組生成新副本,僅頂層獨立。 let copy = [...arr]
const originalArray = [1, 2, { name: 'obj' }, [3, 4]];
const copiedArray = [...originalArray]; // 淺拷貝
copiedArray[0] = 99; // 修改基本類型(不影響原數(shù)組)
copiedArray[2].name = 'new'; // 修改對象屬性(會影響原數(shù)組對象)
copiedArray[3].push(5); // 修改嵌套數(shù)組(會影響原數(shù)組)
console.log(originalArray);
// [1, 2, { name: 'new' }, [3, 4, 5]]
console.log(copiedArray);
// [99, 2, { name: 'new' }, [3, 4, 5]]
語法簡潔:相比
slice()
更直觀易讀頂層獨立:新數(shù)組與原數(shù)組內(nèi)存地址不同,但對象/數(shù)組元素仍共享引用
非破壞性:原數(shù)組保持不變
可結(jié)合其他元素:可在拷貝時插入新元素
應(yīng)用場景
//合并數(shù)組:
const merged = [...arr1, ...arr2];
//添加加元素同時拷貝:
const withNewItem = ['header', ...original, 'footer'];
//函數(shù)參數(shù)傳遞:
Math.max(...[1, 5, 3]); // 等價于 Math.max(1,5,3)
Object.assign()
合并對象生成新數(shù)組,僅首層拷貝。 let copy = Object.assign([], arr)
const originalArray = [1, 2, { name: 'obj' }, [3, 4]];
const copiedArray = Object.assign([], originalArray); // 淺拷貝
copiedArray[0] = 99; // 修改基本類型(不影響原數(shù)組)
copiedArray[2].name = 'new'; // 修改對象屬性(會影響原數(shù)組)
copiedArray[3].push(5); // 修改嵌套數(shù)組(會影響原數(shù)組)
console.log(originalArray);
// [1, 2, { name: 'new' }, [3, 4, 5]]
console.log(copiedArray);
// [99, 2, { name: 'new' }, [3, 4, 5]]
對象合并原理:通過將空數(shù)組作為目標(biāo)對象,原數(shù)組元素作為源對象進(jìn)行屬性復(fù)制
首層拷貝:新數(shù)組與原數(shù)組引用不同,但對象元素仍共享內(nèi)存地址
適用場景:主要用于對象合并,數(shù)組拷貝時不如擴展運算符直觀
應(yīng)用場景
更適合需要合并對象屬性的場景:
// 對象合并示例
const obj1 = { a: 1 };
const obj2 = { b: 2 };
const mergedObj = Object.assign({}, obj1, obj2); // { a:1, b:2 }
concat()
? 合并空數(shù)組生成新副本,效果類似slice()
。 let copy = arr.concat()
const originalArray = [1, 2, { name: 'obj' }, [3, 4]];
const copiedArray = originalArray.concat(); // 淺拷貝
// 或合并空數(shù)組更直觀
const copiedArray2 = [].concat(originalArray);
copiedArray[0] = 99; // 修改基本類型(不影響原數(shù)組)
copiedArray[2].name = 'new'; // 修改對象屬性(會影響原數(shù)組)
copiedArray[3].push(5); // 修改嵌套數(shù)組(會影響原數(shù)組)
console.log(originalArray);
// [1, 2, { name: 'new' }, [3, 4, 5]]
console.log(copiedArray);
// [99, 2, { name: 'new' }, [3, 4, 5]]
無參調(diào)用:
concat()
不傳參數(shù)時,默認(rèn)合并空數(shù)組生成副本首層獨立:新數(shù)組與原數(shù)組引用不同,但對象/數(shù)組元素共享內(nèi)存地址
非破壞性:原數(shù)組保持原樣
歷史兼容性:支持 ES3+ 環(huán)境,比擴展運算符兼容性更好
應(yīng)用場景
?簡單數(shù)據(jù)復(fù)制?
適用于僅需復(fù)制頂層數(shù)據(jù)的場景(如傳遞配置參數(shù))。
?性能優(yōu)先?
淺拷貝開銷低,適合對性能敏感且無需隔離嵌套數(shù)據(jù)的場景?。
?臨時數(shù)據(jù)共享?
需要多個數(shù)組共享同一組嵌套數(shù)據(jù)時(如狀態(tài)快照)?
深拷貝詳解
深拷貝(Deep Copy)指遞歸復(fù)制數(shù)組的?所有層級元素?,包括嵌套的引用類型(如對象、數(shù)組),生成與原數(shù)組完全獨立的新數(shù)組。修改深拷貝后的數(shù)組不會影響原數(shù)組,二者無任何內(nèi)存地址共享?
核心特點?:
所有層級元素獨立,包括基本類型和引用類型;
支持復(fù)雜數(shù)據(jù)結(jié)構(gòu)(如多層嵌套、特殊對象類型)的完全隔離?
實現(xiàn)方法
JSON.parse(JSON.stringify())
? 通過序列化與反序列化生成新數(shù)組,支持大部分?jǐn)?shù)據(jù)類型。
適用場景 簡單數(shù)據(jù)結(jié)構(gòu)的深拷貝(無函數(shù)、undefined
)
局限性 忽略函數(shù)、undefined
、Symbol
,無法處理循環(huán)引用,不支持Date
等特殊對象?
const originalArray = [1, { name: 'obj' }, [3, 4], new Date()];
const deepCopiedArray = JSON.parse(JSON.stringify(originalArray));
deepCopiedArray[1].name = 'new'; // 修改對象屬性
deepCopiedArray[2].push(5); // 修改嵌套數(shù)組
console.log(originalArray);
// [1, { name: 'obj' }, [3, 4], Date對象]
console.log(deepCopiedArray);
// [1, { name: 'new' }, [3, 4, 5], ISO日期字符串]
// 核心限制
const problemArray = [
function(){}, // 函數(shù) → 丟失
undefined, // undefined → 丟失
Symbol('test'), // Symbol → 丟失
new Date(), // Date → 轉(zhuǎn)為字符串
Infinity, // Infinity → null
/regex/g, // 正則表達(dá)式 → 空對象
new Error('test') // 錯誤對象 → 空對象
];
const result = JSON.parse(JSON.stringify(problemArray));
// [null, null, null, "2024-03-07T...", null, {}, {}]
完全獨立副本:新數(shù)組與所有嵌套元素均無引用關(guān)系
數(shù)據(jù)序列化:通過 JSON 格式轉(zhuǎn)換實現(xiàn)深拷貝
適用場景:適合處理 JSON 安全數(shù)據(jù)結(jié)構(gòu)(不包含函數(shù)/Symbol 等特殊類型)
遞歸遍歷 手動遍歷數(shù)組元素,遞歸復(fù)制引用類型。
適用場景 自定義深拷貝邏輯,處理特殊需求
限性 需處理循環(huán)引用和復(fù)雜類型,代碼復(fù)雜度高,性能較低?
function deepClone(arr) {
// 基礎(chǔ)類型直接返回
if (typeof arr !== 'object' || arr === null) return arr
// 處理特殊對象類型
if (arr instanceof Date) return new Date(arr)
if (arr instanceof RegExp) return new RegExp(arr)
// 創(chuàng)建新容器(區(qū)分?jǐn)?shù)組和對象)
const clone = Array.isArray(arr) ? [] : {}
// 遞歸拷貝每個元素
for (let key in arr) {
if (arr.hasOwnProperty(key)) {
clone[key] = deepClone(arr[key])
}
}
return clone
}
// 使用示例
const original = [1, { a: 2 }, [3, new Date()]]
const cloned = deepClone(original)
cloned[1].a = 99 // 修改對象屬性
cloned[2][0] = 300 // 修改嵌套數(shù)組
console.log(original) // [1, {a:2}, [3, Date對象]] 保持原樣
const obj = { a: 1 }
obj.self = obj
// 基礎(chǔ)版本會棧溢出,需增加 WeakMap 跟蹤
類型識別機制:
優(yōu)先處理
Date
和RegExp
等特殊對象自動區(qū)分?jǐn)?shù)組和普通對象
保留原型鏈特性(通過
constructor
可擴展)
增強版方案(含循環(huán)引用處理)
function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return null
if (typeof obj !== 'object') return obj
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
// 檢查循環(huán)引用
if (hash.has(obj)) return hash.get(obj)
const clone = new obj.constructor()
hash.set(obj, clone)
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], hash)
}
}
return clone
}
structuredClone()
原生API,支持復(fù)雜類型(如Date
、Map
、Set
)和循環(huán)引用,性能較好。
適用場景 現(xiàn)代瀏覽器環(huán)境,需處理復(fù)雜類型或循環(huán)引用
限性 部分舊瀏覽器不支持,無法復(fù)制函數(shù)或原型鏈屬性?
const originalArray = [1, { name: 'obj' }, [3, 4], new Date(), new Set([5,6])];
const deepCopiedArray = structuredClone(originalArray);
// 修改拷貝后的數(shù)據(jù)
deepCopiedArray[1].name = 'new';
deepCopiedArray[2].push(5);
deepCopiedArray[3].setFullYear(2025);
console.log(originalArray);
// [1, {name:'obj'}, [3,4], 原日期對象, Set {5,6}]
console.log(deepCopiedArray);
// [1, {name:'new'}, [3,4,5], 2025年日期對象, Set {5,6}]
// 兼容舊環(huán)境的備用方案
function safeStructuredClone(obj) {
if (typeof structuredClone === 'function') {
return structuredClone(obj);
}
// 備用方案(簡易深拷貝)
return JSON.parse(JSON.stringify(obj));
}
現(xiàn)代瀏覽器原生支持:Chrome 98+/Firefox 94+/Safari 15.4+ 等現(xiàn)代瀏覽器支持
完整類型支持:
? 處理循環(huán)引用
? 保留 Date/RegExp/Map/Set 等特殊類型
? 支持 ArrayBuffer/Blob 等二進(jìn)制類型
深度克隆:所有層級數(shù)據(jù)均獨立,無引用共享