最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript稀疏數(shù)組的成因、坑點(diǎn)與解決方案

 更新時(shí)間:2026年03月24日 08:41:10   作者:隨意_  
在?JavaScript?開(kāi)發(fā)中,稀疏數(shù)組是一個(gè)極易被忽視但高頻踩坑的知識(shí)點(diǎn),本文將從本質(zhì)、成因、實(shí)戰(zhàn)坑點(diǎn)、解決方案四個(gè)維度,來(lái)理解這個(gè)稀疏數(shù)組,需要的朋友可以參考下

在 JavaScript 開(kāi)發(fā)中,稀疏數(shù)組(Sparse Array)是一個(gè)極易被忽視但高頻踩坑的知識(shí)點(diǎn) —— 它看似是普通數(shù)組,卻因 “索引空洞” 特性引發(fā)各種詭異的 TypeError 和邏輯錯(cuò)誤。本文將從本質(zhì)、成因、實(shí)戰(zhàn)坑點(diǎn)、解決方案四個(gè)維度,來(lái)理解這個(gè)稀疏數(shù)組。

一、稀疏數(shù)組的本質(zhì):不是 “數(shù)組” 的數(shù)組

1. 核心定義

稀疏數(shù)組是指索引不連續(xù)、存在未賦值 “空洞(empty slot)” 的 JavaScript 數(shù)組,其核心特征:

  • 數(shù)組 length 由最大索引 + 1 決定,但遠(yuǎn)大于實(shí)際存在的元素?cái)?shù)量;
  • 空洞位置訪問(wèn)返回 undefined,但與顯式賦值 undefined 完全不同;
  • 空洞不占用內(nèi)存,也不會(huì)被多數(shù)枚舉方法識(shí)別

2. 稀疏數(shù)組 vs 密集數(shù)組

// 1. 稀疏數(shù)組(天然空洞)
const sparseArr = [];
sparseArr[10] = 'JavaScript'; // 僅給索引10賦值,0-9均為空洞
console.log('稀疏數(shù)組長(zhǎng)度:', sparseArr.length); // 11(最大索引+1)
console.log('索引5的值:', sparseArr[5]); // undefined(空洞)
console.log('實(shí)際存在的鍵:', Object.keys(sparseArr)); // ['10'](僅賦值索引)
console.log('是否包含索引5:', sparseArr.hasOwnProperty(5)); // false(空洞不占內(nèi)存)

// 2. 密集數(shù)組(顯式賦值undefined)
const denseArr = new Array(11).fill(undefined);
denseArr[10] = 'JavaScript';
console.log('密集數(shù)組長(zhǎng)度:', denseArr.length); // 11
console.log('索引5的值:', denseArr[5]); // undefined(顯式賦值)
console.log('實(shí)際存在的鍵:', Object.keys(denseArr)); // ['0','1',...,'10'](全索引存在)
console.log('是否包含索引5:', denseArr.hasOwnProperty(5)); // true(占用內(nèi)存)

關(guān)鍵差異:稀疏數(shù)組的空洞是 “不存在的索引”,而密集數(shù)組的 undefined 是 “存在但值為空的索引”—— 這是理解所有坑點(diǎn)的核心。

二、稀疏數(shù)組的常見(jiàn)生成場(chǎng)景(原創(chuàng)示例)

稀疏數(shù)組幾乎都是 “無(wú)意產(chǎn)生” 的,以下是開(kāi)發(fā)中最易踩坑的場(chǎng)景:

場(chǎng)景 1:跨索引直接賦值(最常見(jiàn))

// 業(yè)務(wù)場(chǎng)景:根據(jù)ID索引存儲(chǔ)用戶數(shù)據(jù),ID從100開(kāi)始
const userList = [];
userList[100] = { id: 100, name: '張三' };
userList[105] = { id: 105, name: '李四' };

console.log(userList.length); // 106(而非2)
console.log(userList[99]); // undefined(空洞)
// 此時(shí) userList 是典型的稀疏數(shù)組:0-99、101-104均為空洞

場(chǎng)景 2:Array構(gòu)造函數(shù)指定長(zhǎng)度

// 錯(cuò)誤認(rèn)知:new Array(5) 會(huì)創(chuàng)建 [undefined, undefined, ...]
const emptyArr = new Array(5); 
console.log(emptyArr); // [empty × 5](純空洞數(shù)組)
console.log(emptyArr.map(item => item || '默認(rèn)值')); // [empty × 5](map跳過(guò)空洞)

// 對(duì)比:真正的密集空數(shù)組
const realEmptyArr = Array.from({ length: 5 });
console.log(realEmptyArr); // [undefined, undefined, undefined, undefined, undefined]
console.log(realEmptyArr.map(item => item || '默認(rèn)值')); // ['默認(rèn)值','默認(rèn)值',...,'默認(rèn)值']

場(chǎng)景 3:delete操作刪除數(shù)組元素

const scoreList = [90, 85, 78, 92];
delete scoreList[1]; // 刪除索引1的元素,留下空洞

console.log(scoreList); // [90, empty, 78, 92]
console.log(scoreList.length); // 4(長(zhǎng)度不變)
// 遍歷陷阱:forEach跳過(guò)空洞
scoreList.forEach((score, index) => {
  console.log(`索引${index}:${score}`); // 僅輸出索引0、2、3
});

場(chǎng)景 4:數(shù)組拼接 / 截取的邊界情況

const arr1 = [1, 2];
const arr2 = arr1.slice(0, 0); // 截取空范圍,返回稀疏數(shù)組
arr2[5] = 6;

console.log(arr2); // [empty × 5, 6]
console.log(arr2.concat([7])); // [empty × 5, 6, 7](拼接后仍保留空洞)

三、稀疏數(shù)組的實(shí)戰(zhàn)坑點(diǎn)

稀疏數(shù)組的危害集中在 “遍歷 / 方法調(diào)用” 環(huán)節(jié),坑點(diǎn)示例:

坑點(diǎn) 1:some()/every()訪問(wèn)空洞屬性報(bào)錯(cuò)

// 業(yè)務(wù)場(chǎng)景:檢查購(gòu)物車(chē)是否有選中商品(選中商品存于數(shù)組指定索引)
const cartSelected = [];
cartSelected[3] = ['goods1', 'goods2']; // 稀疏數(shù)組:0-2為空洞

// 期望:檢查是否有選中商品,實(shí)際報(bào)錯(cuò)
try {
  const hasSelected = cartSelected.some(ids => ids.length > 0);
} catch (e) {
  console.error(e); // TypeError: Cannot read properties of undefined (reading 'length')
}
// 原因:some()遍歷索引0時(shí),ids = undefined,訪問(wèn)length報(bào)錯(cuò)

坑點(diǎn) 2:map()跳過(guò)空洞導(dǎo)致數(shù)據(jù)長(zhǎng)度不一致

// 業(yè)務(wù)場(chǎng)景:將商品ID數(shù)組轉(zhuǎn)為商品名稱數(shù)組
const goodsIds = [];
goodsIds[2] = 'g001';
goodsIds[5] = 'g002'; // 稀疏數(shù)組:長(zhǎng)度6,僅2、5有值

// 期望:返回長(zhǎng)度6的名稱數(shù)組,實(shí)際返回稀疏數(shù)組
const goodsNames = goodsIds.map(id => {
  const nameMap = { g001: '手機(jī)', g002: '電腦' };
  return nameMap[id] || '未知商品';
});

console.log(goodsNames); // [empty × 2, '手機(jī)', empty × 2, '電腦']
console.log(goodsNames.length); // 6,但索引0-1、3-4仍為空洞
// 后續(xù)邏輯陷阱:如果用goodsNames.length做循環(huán),會(huì)拿到undefined

坑點(diǎn) 3:for...in遍歷漏值,for循環(huán)多值

// 業(yè)務(wù)場(chǎng)景:統(tǒng)計(jì)數(shù)組中有效數(shù)據(jù)的數(shù)量
const dataList = [];
dataList[1] = '有效數(shù)據(jù)1';
dataList[4] = '有效數(shù)據(jù)2';

// 錯(cuò)誤1:for...in僅遍歷有值索引,統(tǒng)計(jì)結(jié)果偏小
let count1 = 0;
for (const index in dataList) {
  count1++;
}
console.log('for...in統(tǒng)計(jì):', count1); // 2(正確,但易被誤認(rèn)為“遍歷全索引”)

// 錯(cuò)誤2:for循環(huán)遍歷全索引,統(tǒng)計(jì)結(jié)果偏大
let count2 = 0;
for (let i = 0; i < dataList.length; i++) {
  if (dataList[i]) count2++;
}
console.log('for循環(huán)統(tǒng)計(jì):', count2); // 2(看似正確,但如果有值為0/null會(huì)誤判)

// 正確統(tǒng)計(jì):結(jié)合hasOwnProperty
let count3 = 0;
for (let i = 0; i < dataList.length; i++) {
  if (dataList.hasOwnProperty(i)) count3++;
}
console.log('正確統(tǒng)計(jì):', count3); // 2

四、通用解決方案:將稀疏數(shù)組轉(zhuǎn)為密集數(shù)組

核心思路:用有效值填充所有空洞,確保數(shù)組每個(gè)索引都有明確值(無(wú)空洞)。以下是 4 種原創(chuàng)解決方案,覆蓋不同場(chǎng)景:

方案 1:Array.from()(推薦,簡(jiǎn)潔通用)

/**
 * 將稀疏數(shù)組轉(zhuǎn)為密集數(shù)組
 * @param {Array} sparseArr - 稀疏數(shù)組
 * @param {any} defaultValue - 空洞填充值
 * @returns {Array} 密集數(shù)組
 */
const toDenseArray = (sparseArr, defaultValue = undefined) => {
  return Array.from({ length: sparseArr.length }, (_, index) => {
    // 有值則保留,無(wú)值則用默認(rèn)值填充
    return sparseArr[index] ?? defaultValue;
  });
};

// 實(shí)戰(zhàn)示例:修復(fù)購(gòu)物車(chē)選中檢查問(wèn)題
const cartSelected = [];
cartSelected[3] = ['goods1', 'goods2'];
// 轉(zhuǎn)為密集數(shù)組,空洞填充為空數(shù)組
const denseCart = toDenseArray(cartSelected, []);

console.log(denseCart); // [[], [], [], ['goods1', 'goods2']](長(zhǎng)度4,無(wú)空洞)
const hasSelected = denseCart.some(ids => ids.length > 0);
console.log(hasSelected); // true(正常執(zhí)行,無(wú)報(bào)錯(cuò))

方案 2:fill()+ 擴(kuò)展運(yùn)算符(適合固定默認(rèn)值)

// 場(chǎng)景:快速創(chuàng)建指定長(zhǎng)度的密集空數(shù)組
const createDenseEmptyArray = (length) => {
  // 先創(chuàng)建長(zhǎng)度為length的數(shù)組,填充空數(shù)組(注意:fill的引用類(lèi)型會(huì)共享,需額外處理)
  return Array(length).fill().map(() => []); 
};

// 示例:創(chuàng)建長(zhǎng)度5的密集數(shù)組,每個(gè)元素都是獨(dú)立空數(shù)組
const denseArr = createDenseEmptyArray(5);
denseArr[2].push('test');
console.log(denseArr); // [[], [], ['test'], [], []](無(wú)共享問(wèn)題)

方案 3:Object.assign()(適合小數(shù)據(jù)量)

// 原理:Object.assign會(huì)遍歷所有可枚舉屬性,自動(dòng)填充空洞為undefined
const sparseArr = [];
sparseArr[4] = 'test';
const denseArr = Object.assign([], sparseArr);

console.log(denseArr); // [undefined, undefined, undefined, undefined, 'test']
// 再替換undefined為自定義默認(rèn)值
const finalArr = denseArr.map(item => item ?? '默認(rèn)值');
console.log(finalArr); // ['默認(rèn)值', '默認(rèn)值', '默認(rèn)值', '默認(rèn)值', 'test']

五、實(shí)踐:避免稀疏數(shù)組的開(kāi)發(fā)規(guī)范

  1. 禁止跨索引直接賦值:如需按索引存儲(chǔ)數(shù)據(jù),先初始化指定長(zhǎng)度的密集數(shù)組,再賦值;
  2. 慎用 new Array(length) :優(yōu)先用 Array.from({ length }) 創(chuàng)建密集數(shù)組;
  3. 刪除數(shù)組元素用 splice() 而非 deletesplice() 會(huì)重置索引,避免空洞;
  4. 遍歷前先校驗(yàn):對(duì)不確定是否為稀疏的數(shù)組,先轉(zhuǎn)為密集數(shù)組再遍歷;
  5. 使用空值占位:如無(wú)特殊需求,用空數(shù)組 []、空字符串 '' 等替代空洞。

總結(jié)

  1. 稀疏數(shù)組核心特征:索引不連續(xù)、存在空洞,空洞不占內(nèi)存且訪問(wèn)返回 undefined,與顯式賦值 undefined 有本質(zhì)區(qū)別;
  2. 主要坑點(diǎn):遍歷方法(some()/map() 等)處理空洞時(shí)易報(bào)錯(cuò)或邏輯異常,核心原因是空洞傳入回調(diào)的參數(shù)為 undefined
  3. 通用解決方案:通過(guò) Array.from()、手動(dòng)遍歷等方式,將稀疏數(shù)組轉(zhuǎn)為密集數(shù)組,用自定義默認(rèn)值(如 []/0)填充所有空洞,從根源避免問(wèn)題。

以上就是JavaScript稀疏數(shù)組的成因、坑點(diǎn)與解決方案的詳細(xì)內(nèi)容,更多關(guān)于JavaScript稀疏數(shù)組的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JS字符串與二進(jìn)制的相互轉(zhuǎn)化實(shí)例代碼詳解

    JS字符串與二進(jìn)制的相互轉(zhuǎn)化實(shí)例代碼詳解

    這篇文章主要介紹了JS字符串與二進(jìn)制的相互轉(zhuǎn)化 ,在文中給大家提到了Js之字符串和字節(jié)碼之間的相互轉(zhuǎn)換,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-06-06
  • JavaScript 替換所有匹配內(nèi)容及正則替換方法

    JavaScript 替換所有匹配內(nèi)容及正則替換方法

    這篇文章主要介紹了JavaScript 替換所有匹配內(nèi)容,文中給大家提到了使用正則表達(dá)式替換方法,通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2020-02-02
  • javascript 模擬JQuery的Ready方法實(shí)現(xiàn)并出現(xiàn)的問(wèn)題

    javascript 模擬JQuery的Ready方法實(shí)現(xiàn)并出現(xiàn)的問(wèn)題

    今天在閱讀網(wǎng)上一些模擬Jq的ready方法時(shí),發(fā)現(xiàn)一些小細(xì)節(jié),就是網(wǎng)上的ready事件大部分都是在onload事件執(zhí)行后加載,而jquery確能在onload加載前。
    2009-12-12
  • js實(shí)現(xiàn)表格單列按字母排序

    js實(shí)現(xiàn)表格單列按字母排序

    這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)表格單列按字母排序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-08-08
  • 基于BootStrap的文本編輯器組件Summernote

    基于BootStrap的文本編輯器組件Summernote

    Summernote是一個(gè)基于jquery的bootstrap超級(jí)簡(jiǎn)單WYSIWYG在線編輯器。這篇文章主要介紹了基于BootStrap的文本編輯器組件Summernote的相關(guān)資料,需要的朋友可以參考下
    2017-10-10
  • 基于Bootstrap實(shí)現(xiàn)tab標(biāo)簽切換效果

    基于Bootstrap實(shí)現(xiàn)tab標(biāo)簽切換效果

    這篇文章主要為大家詳細(xì)介紹了基于Bootstrap實(shí)現(xiàn)tab標(biāo)簽切換效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-05-05
  • JS設(shè)置下拉列表框當(dāng)前所選值的方法

    JS設(shè)置下拉列表框當(dāng)前所選值的方法

    這篇文章主要介紹了JS設(shè)置下拉列表框當(dāng)前所選值的方法,涉及JavaScript結(jié)合jsp動(dòng)態(tài)設(shè)置select下拉列表框的相關(guān)技巧,需要的朋友可以參考下
    2015-12-12
  • webapi根據(jù)id獲取元素的實(shí)現(xiàn)思路

    webapi根據(jù)id獲取元素的實(shí)現(xiàn)思路

    掌握document.getElementById() 根據(jù)id獲取元素,在頁(yè)面畫(huà)出一個(gè)寬200 高200 粉色的盒子, 在控制臺(tái)打印這個(gè)盒子,接到這樣的需求如何處理呢,下面小編給大家分享webapi根據(jù)id獲取元素的實(shí)現(xiàn)思路,感興趣的朋友一起看看吧
    2024-02-02
  • javascript 兼容所有瀏覽器的DOM擴(kuò)展功能

    javascript 兼容所有瀏覽器的DOM擴(kuò)展功能

    技術(shù)文章寫(xiě)得少,所以有時(shí)候想寫(xiě)點(diǎn)什么卻下不了手,不知道該寫(xiě)什么;往往到了準(zhǔn)備要寫(xiě)的時(shí)候才發(fā)現(xiàn)自己想寫(xiě)的東西其實(shí)很無(wú)聊,甚至覺(jué)得很幼稚,于是又關(guān)掉了編緝器
    2012-08-08
  • 關(guān)于js中window.location.href,location.href,parent.location.href,top.location.href的用法與區(qū)別

    關(guān)于js中window.location.href,location.href,parent.location.href

    關(guān)于js中window.location.href,location.href,parent.location.href,top.location.href的用法
    2010-10-10

最新評(píng)論

航空| 宜都市| 太康县| 普安县| 阿克陶县| 长治县| 本溪市| 鲜城| 三河市| 永登县| 道孚县| 项城市| 柳河县| 布尔津县| 巫山县| 雷州市| 莆田市| 宝清县| 临江市| 波密县| 嘉峪关市| 新晃| 十堰市| 兴宁市| 保定市| 库尔勒市| 德江县| 林口县| 炎陵县| 东城区| 满洲里市| 本溪| 平利县| 顺义区| 名山县| 抚顺市| 高邑县| 洛浦县| 白山市| 台北县| 阿鲁科尔沁旗|