數(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ù)組提供專有方法(如 pushmap)用于操作元素集合,對象無此類內(nèi)置方法?

使用場景

數(shù)據(jù)列表存儲?:適用于需保持順序的數(shù)據(jù)集合(如排行榜、表格行數(shù)據(jù))?
批量操作?:通過循環(huán)或高階函數(shù)(如 forEachfilter)高效處理多元素?


注意事項

?性能優(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);

首尾元素?:通過 arrarr[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' }]
  1. 無參調(diào)用slice() 不傳參數(shù)時,默認(rèn)截取整個數(shù)組(0length-1

  2. 獨立數(shù)組:新數(shù)組與原數(shù)組的引用不同,但對象元素共享引用

  3. 非破壞性:原數(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]]
  1. 語法簡潔:相比 slice() 更直觀易讀

  2. 頂層獨立:新數(shù)組與原數(shù)組內(nèi)存地址不同,但對象/數(shù)組元素仍共享引用

  3. 非破壞性:原數(shù)組保持不變

  4. 可結(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]]
  1. 對象合并原理:通過將空數(shù)組作為目標(biāo)對象,原數(shù)組元素作為源對象進(jìn)行屬性復(fù)制

  2. 首層拷貝:新數(shù)組與原數(shù)組引用不同,但對象元素仍共享內(nèi)存地址

  3. 適用場景:主要用于對象合并,數(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]]
  1. 無參調(diào)用concat() 不傳參數(shù)時,默認(rèn)合并空數(shù)組生成副本

  2. 首層獨立:新數(shù)組與原數(shù)組引用不同,但對象/數(shù)組元素共享內(nèi)存地址

  3. 非破壞性:原數(shù)組保持原樣

  4. 歷史兼容性:支持 ES3+ 環(huán)境,比擴展運算符兼容性更好

應(yīng)用場景

  1. ?簡單數(shù)據(jù)復(fù)制?

    • 適用于僅需復(fù)制頂層數(shù)據(jù)的場景(如傳遞配置參數(shù))。

  2. ?性能優(yōu)先?

    • 淺拷貝開銷低,適合對性能敏感且無需隔離嵌套數(shù)據(jù)的場景?。

  3. ?臨時數(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, {}, {}]
  1. 完全獨立副本:新數(shù)組與所有嵌套元素均無引用關(guān)系

  2. 數(shù)據(jù)序列化:通過 JSON 格式轉(zhuǎn)換實現(xiàn)深拷貝

  3. 適用場景:適合處理 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)先處理 DateRegExp 等特殊對象

  • 自動區(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ù)雜類型(如DateMap、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));
}
  1. 現(xiàn)代瀏覽器原生支持:Chrome 98+/Firefox 94+/Safari 15.4+ 等現(xiàn)代瀏覽器支持

  2. 完整類型支持

    • ? 處理循環(huán)引用

    • ? 保留 Date/RegExp/Map/Set 等特殊類型

    • ? 支持 ArrayBuffer/Blob 等二進(jìn)制類型

  3. 深度克隆:所有層級數(shù)據(jù)均獨立,無引用共享