从初始化到遍历解析JavaScript中数组避坑指南

 更新时间:2026年02月24日 09:32:31   作者:用户575730334624  
数组是 JS 开发中最常用的数据结构,但你真的会用吗,本文带你彻底搞懂数组初始化、遍历方法,以及那个让无数人踩坑的 let vs var 闭包问题

一、数据结构基础:为什么要学数组?

在开始之前,先快速了解一下数据结构的世界:

数据结构分类
├── 线性结构
│   ├── 数组 ✅ 连续内存,随机访问
│   ├── 栈   先进后出 FILO
│   ├── 队列  先进先出 FIFO
│   └── 链表  离散内存,指针连接
└── 非线性结构
    └── 树(二叉树)层级关系

数组的优势

  • 开箱即用,无需额外实现
  • 连续内存,访问速度快 O(1)
  • API 丰富,JS 内置大量方法

二、数组的初始化方法(面试高频考点)

2.1 字面量创建(最常用)

// ✅ 推荐:元素确定时使用
const arr = [1, 3, 4];
console.log(arr); // [1, 3, 4]

// 内存结构:
// 栈内存:arr → 引用地址
// 堆内存:[1, 3, 4] 连续空间

2.2 Array 构造函数

// 创建空数组
const arr1 = new Array(); // []

// ⚠️ 坑点:单个数字参数创建的是"空位数组"
const arr3 = new Array(6); 
console.log(arr3); // [empty × 6]
console.log(arr3.length); // 6

// ✅ 正确用法:创建并填充
const arr2 = (new Array(6)).fill(1);
console.log(arr2); // [1, 1, 1, 1, 1, 1]

const arr = (new Array(6)).fill(3);
console.log(arr); // [3, 3, 3, 3, 3, 3]

2.3 初始化方法对比

方法代码结果推荐度
字面量[1,2,3][1,2,3]⭐⭐⭐⭐⭐
Array 空参new Array()[]⭐⭐⭐
Array 数字new Array(6)[empty×6]⭐⭐(有坑)
Array+fillnew Array(6).fill(1)[1,1,1,1,1,1]⭐⭐⭐⭐

三、数组遍历方法大比拼(核心考点)

3.1 for 循环(性能王者)

const arr = [1, 3, 4, 5, 6, 7];

// ✅ 优化版:缓存 length,避免重复查询
const len = arr.length; // RHS 查询只做一次
for (let i = 0; i < len; i++) {
    console.log(arr[i]);
}

// ❌ 不推荐:每次循环都查询 length
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}

性能分析

  • CPU 友好:计数循环与 CPU 指令契合
  • 性能最好:无函数调用开销
  • 可读性差i=0; i<len; i++ 过于死板

3.2 forEach(简洁但有限制)

const arr = [1, 3, 4, 5, 6, 7];

arr.forEach((item, index) => {
    console.log(item, index);
});

三大限制

  • 不能 break:无法提前退出循环
  • 不能 return:return 只退出当前回调
  • 性能较差:函数入栈出栈有开销
// ❌ 错误示范:break 无效
arr.forEach((item) => {
    if (item === 4) break; // SyntaxError!
});

// ✅ 正确做法:用 for 或 for...of
for (let item of arr) {
    if (item === 4) break; // 可以正常退出
}

3.3 map(遍历 + 转换)

const arr = [1, 2, 3, 4, 5, 6];

// forEach 只遍历,不返回
// map 遍历 + 加工,返回新数组
const newArr = arr.map(item => item + 1);
console.log(newArr); // [2, 3, 4, 5, 6, 7]
console.log(arr);    // [1, 2, 3, 4, 5, 6] 原数组不变

使用场景

  • 数据转换(如 API 响应处理)
  • 保持函数式编程的不可变性
  • 不需要新数组时别用(浪费内存)

3.4 for...of(ES6 新宠)

const arr = [1, 2, 3, 4, 5, 6];

for (let item of arr) {
    console.log(item);
}

优势

  • 可读性好:语义清晰
  • 支持 break/continue
  • 支持 async/await
  • 性能略低于 for 循环

3.5 for...in(数组慎用!)

const arr = [1, 3, 4, 5, 6, 7, 8, 8];

for (let key in arr) {
    // ⚠️ key 是字符串类型的下标!
    console.log(key, typeof key); // "0" "string"
    console.log(arr[key]);
}

为什么不建议用于数组

  • key 是字符串,不是数字
  • 会遍历原型链上的属性
  • 顺序不保证(虽然现代引擎通常有序)

正确用途:遍历对象

const obj = {
    name: 'llili',
    age: 16,
    hobbies: ["篮球", "足球", "跑步"]
};

for (let k in obj) {
    console.log(k, obj[k]);
}
// 输出:name llili / age 16 / hobbies [...]

3.6 遍历方法对比表

方法可 break可 continue返回值性能推荐场景
for⭐⭐⭐⭐⭐性能敏感
for...of⭐⭐⭐⭐日常遍历
forEach⭐⭐⭐简单遍历
map新数组⭐⭐⭐数据转换
for...in⭐⭐遍历对象

四、let vs var 在 for 循环中的区别(闭包经典题)

这是 JavaScript 面试最高频考点,没有之一!

4.1 var 版本(错误示范)

for (var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}

// 输出:10 个 10 ❌

4.2 let 版本(正确示范)

for (let i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}

// 输出:0 1 2 3 4 5 6 7 8 9 ✅

为什么结果不同?

┌─────────────────────────────────────────────────────────┐
│                    var vs let 本质区别                  │
├─────────────────────────────────────────────────────────┤
│  var i                                                  │
│  ├── 函数作用域                                          │
│  ├── 整个循环只有 1 个 i 变量                             │
│  └── 所有 setTimeout 共享同一个 i                         │
│                                                         │
│  let i                                                  │
│  ├── 块级作用域                                          │
│  ├── 每次循环创建新的 i 变量                              │
│  └── 每个 setTimeout 捕获独立的 i                         │
└─────────────────────────────────────────────────────────┘

闭包原理解析

// var 版本:所有回调共享同一个 i
var i;  // 全局/函数作用域只有 1 个变量
for (i = 0; i < 10; i++) {
    setTimeout(() => console.log(i), 1000);
}
// 循环结束 i = 10,所有回调都输出 10

// let 版本:每次循环创建新的 i
for (let i = 0; i < 10; i++) {
    // 相当于每次都有新的块级作用域
    setTimeout(() => console.log(i), 1000);
}
// 每个回调都有自己的 i,输出 0~9

三种解决方案

// 方案 1:使用 let(推荐)⭐⭐⭐⭐⭐
for (let i = 0; i < 10; i++) {
    setTimeout(() => console.log(i), 1000);
}

// 方案 2:IIFE 立即执行函数 ⭐⭐⭐
for (var i = 0; i < 10; i++) {
    (function(j) {
        setTimeout(() => console.log(j), 1000);
    })(i);
}

// 方案 3:setTimeout 传参 ⭐⭐⭐⭐
for (var i = 0; i < 10; i++) {
    setTimeout(function(j) {
        console.log(j);
    }, 1000, i);
}

五、最佳实践总结

数组初始化

// 元素已知 → 字面量
const arr = [1, 2, 3];

// 需要指定长度 → Array + fill
const arr = new Array(10).fill(0);

数组遍历

// 性能敏感 → for 循环
for (let i = 0, len = arr.length; i < len; i++) {}

// 日常遍历 → for...of
for (let item of arr) {}

// 数据转换 → map
const newArr = arr.map(item => item * 2);

// 遍历对象 → for...in
for (let key in obj) {}

循环变量声明

// 永远优先使用 let/const
for (let i = 0; i < arr.length; i++) {}

// 避免使用 var(除非有特殊原因)

六、面试考点速记

考点关键知识点
数组初始化new Array(n) 创建空位数组
遍历性能for > for...of > forEach > map
forEach 限制不能 break/continue/return
let vs var块级作用域 vs 函数作用域
闭包陷阱setTimeout + for 循环经典题
for...in适合对象,不适合数组

结语

数组是 JavaScript 最基础也最重要的数据结构,掌握它的初始化、遍历方法,以及 let/var 的区别,是成为合格前端工程师的必备技能

以上就是从初始化到遍历解析JavaScript中数组避坑指南的详细内容,更多关于JavaScript数组的资料请关注脚本之家其它相关文章!

相关文章

  • JS加载解析Markdown文档过程详解

    JS加载解析Markdown文档过程详解

    这篇文章主要介绍了JS加载解析Markdown文档过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05
  • 前端JavaScript页面插入图片的7种方法及避坑指南

    前端JavaScript页面插入图片的7种方法及避坑指南

    文章详细探讨了前端页面插入图片时需要考虑的各个方面,包括加载顺序、响应式适配、懒加载、格式选择、缓存策略、SEO和性能评分等,通过分享作者的实际经验,文章提供了优化图片加载和渲染的实用方法和技巧,需要的朋友可以参考下
    2025-12-12
  • js canvas实现俄罗斯方块

    js canvas实现俄罗斯方块

    这篇文章主要为大家详细介绍了js canvas实现俄罗斯方块,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-10-10
  • echarts学习之legend点击事件解读

    echarts学习之legend点击事件解读

    这篇文章主要介绍了echarts学习之legend点击事件解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • JavaScript显示当然日期和时间即年月日星期和时间

    JavaScript显示当然日期和时间即年月日星期和时间

    使用js显示当然日期和时间在网页中很是常见,方法有很多,不过多说大同小异,下面有个不错的示例,需要的朋友可以感受下
    2013-10-10
  • 一个可以随意添加多个序列的tag函数

    一个可以随意添加多个序列的tag函数

    由于在没有规划好的情况下写的这个代码,写的比较粗糙,也没有添加注释。 JavaScript代码和HTML完全分离;可以随意添加多个子div标签,自动扩展
    2009-07-07
  • JS简单实现点击跳转登陆邮箱功能的方法

    JS简单实现点击跳转登陆邮箱功能的方法

    这篇文章主要介绍了JS简单实现点击跳转登陆邮箱功能的方法,涉及js针对hash表的遍历与页面元素属性动态操作相关实现技巧,需要的朋友可以参考下
    2017-10-10
  • uniapp小程序之配置首页搜索框功能的实现

    uniapp小程序之配置首页搜索框功能的实现

    这篇文章主要介绍了uniapp小程序之配置首页搜索框,我们介绍一下本次开发使用的是uniapp,本次分享内容的搜索框为禁止输入搜索框,点击跳转专属搜索页面,需要的朋友可以参考下
    2022-09-09
  • .net JS模拟Repeater控件的实现代码

    .net JS模拟Repeater控件的实现代码

    一个模板控件规定了它的模板语法和js api,这是一个repeater控件的JS实现:
    2013-06-06
  • JS或jQuery获取ASP.NET服务器控件ID的方法

    JS或jQuery获取ASP.NET服务器控件ID的方法

    这篇文章主要介绍了JS或jQuery获取ASP.NET服务器控件ID的方法,本文介绍一方法,解决如何使用js获取ASP.NET控件在浏览器端生成html标签对应的id,需要的朋友可以参考下
    2015-06-06

最新评论