详细解析JavaScript中定义数组的5种常见写法,涵盖字面量、构造函数、Array.of、Array.from及展开运算符,结合代码实例与底层机制对比,提供实用选型指南。
JavaScript数组定义的5种常见写法实例
时间:2026-10-30
浏览:443
文章目录
在 JavaScript 开发中,定义数组最直接、最常见的方式是使用数组字面量 []。但在处理特定长度预分配、类数组结构转换、动态序列生成或解决构造函数参数歧义时,语言还提供了构造函数、Array.of()、Array.from() 以及展开运算符等多种方式。本文将逐一梳理这 5 种常见写法的具体语法、代码实例、底层行为差异及适用场景。
一、数组字面量写法(Array Literal)
数组字面量是 JavaScript 中最直接、最易读且性能最优的数组定义方式。它使用一对中括号 [] 包裹逗号分隔的元素列表。在日常开发中,除非有特殊的动态构建需求,否则应优先使用字面量语法。
代码实例
// 1. 创建空数组
const emptyList = [];
// 2. 初始化具有多种类型的数组
const fruits = ['苹果', '香蕉', '橙子'];
const mixed = [100, '文本', true, { id: 1 }, null];
// 3. 数组字面量支持尾后逗号(Trailing Comma),利于版本控制 diff
const config = [
'dev.server.com',
'test.server.com',
'prod.server.com',
];
console.log(fruits.length); // 示例输出: 3
console.log(fruits[0]); // 示例输出: '苹果'
字面量在 V8 等 JavaScript 引擎中被直接解析为原生数组分配指令,避免了构造函数的作用域链查找和执行上下文开销。同时,字面量的语法语义明确,杜绝了歧义。
二、Array 构造函数写法(Array Constructor)
使用内置的全局构造函数 Array() 或 new Array() 也可以创建数组。在 JavaScript 规范中,带 new 与不带 new 调用的行为是完全一致的。
但构造函数存在一个历史上广为人知的重载设计问题:传参行为取决于参数个数与参数类型。
代码实例与单参数陷阱
// 1. 传入多个参数时,参数成为数组的元素
const arrFromMulti = new Array(1, 2, 3);
console.log(arrFromMulti); // 示例输出: [1, 2, 3]
// 2. 传入单个非数值参数时,该参数成为唯一元素
const arrFromStr = new Array('apple');
console.log(arrFromStr); // 示例输出: ['apple']
// 3. 传入单个整数数值时,创建的是指定长度的“稀疏数组”(包含空槽)
const sparseArr = new Array(3);
console.log(sparseArr.length); // 示例输出: 3
console.log(sparseArr); // 示例输出: [ <3 empty items> ]
console.log(0 in sparseArr); // 示例输出: false (索引 0 并不存在)
// 4. 常见配合写法:预分配并填充默认值
const filledArr = new Array(3).fill(0);
console.log(filledArr); // 示例输出: [0, 0, 0]
当执行 new Array(3) 时,引擎只为数组分配了长度属性 length = 3,但并没有在对应索引上创建属性值。这种状态称为空槽(empty slot / hole)。像 forEach()、map()、filter() 等数组迭代方法会自动跳过这些空槽,从而引发许多非预期的逻辑错误。
三、Array.of() 静态方法
为了解决 Array 构造函数在单数字参数与多参数之间的行为不一致,ECMAScript 2015 (ES6) 引入了 Array.of()。
Array.of() 无论传入几个参数、参数是什么类型,都会将这些参数作为新创建数组的元素,其行为与参数数量完全解耦。
代码实例与对比
// 1. 单个数值参数:始终作为数组元素,而不是长度
const a = Array.of(7);
console.log(a); // 示例输出: [7]
console.log(a.length); // 示例输出: 1
// 对比 new Array(7)
const b = new Array(7);
console.log(b); // 示例输出: [ <7 empty items> ]
console.log(b.length); // 示例输出: 7
// 2. 多个参数
const c = Array.of(1, 2, 3);
console.log(c); // 示例输出: [1, 2, 3]
// 3. 动态函数参数包装场景(防止不定参只有一个且为数字时发生意外)
function createWrapper(...args) {
return Array.of(...args);
}
console.log(createWrapper(10)); // 示例输出: [10],不会变成长度为10的空数组
四、Array.from() 静态方法
Array.from() 同样是在 ES6 引入的强大工具。它的主要作用是从类数组对象(拥有 length 属性和若干索引的对象)或可迭代对象(如 Set、Map、字符串)创建并返回一个全新的浅拷贝数组。
不仅如此,Array.from() 还支持传入第二个参数 mapFn,在生成数组元素的同时执行转换,这比先构造数组再调用 .map() 更加高效且不会产生中间数组。
代码实例
// 1. 将字符串转为字符数组(天然正确处理 Unicode 代理对字符)
const chars = Array.from('hello');
console.log(chars); // 示例输出: ['h', 'e', 'l', 'l', 'o']
// 2. 将 DOM 节点列表(NodeList 类数组)转为真正数组
// const divElements = Array.from(document.querySelectorAll('div'));
// 3. 转换 Set 集合
const uniqueSet = new Set(['a', 'b', 'c', 'a']);
const fromSet = Array.from(uniqueSet);
console.log(fromSet); // 示例输出: ['a', 'b', 'c']
// 4. 经典技巧:生成 1 到 5 的递增数字序列
const sequence = Array.from({ length: 5 }, (_, index) => index + 1);
console.log(sequence); // 示例输出: [1, 2, 3, 4, 5]
// 5. 带有 mapFn 映射函数的转换
const doubled = Array.from([1, 2, 3], x => x * 2);
console.log(doubled); // 示例输出: [2, 4, 6]
五、展开运算符(Spread Operator)
ES6 提供的展开语法 [...iterable] 可以将任何实现了 Symbol.iterator 协议的可迭代对象解构成数组元素,并包裹在新的数组字面量中。
展开运算符语法极其紧凑直观,是现代前端开发中处理数组浅拷贝、多数组合并以及集合转数组的最爱写法。
代码实例
// 1. 数组浅拷贝(生成独立引用的新数组)
const original = [1, 2, 3];
const copy = [...original];
// 2. 多个数组的高效拼接
const frontParts = ['header', 'nav'];
const backParts = ['content', 'footer'];
const layout = [...frontParts, 'main-banner', ...backParts];
console.log(layout); // 示例输出: ['header', 'nav', 'main-banner', 'content', 'footer']
// 3. 结合 Set 进行数组去重并重新定义数组
const duplicates = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = [...new Set(duplicates)];
console.log(uniqueArray); // 示例输出: [1, 2, 3, 4, 5]
// 4. 配合 Array 迭代器快速生成索引序列
const indexSeq = [...Array(4).keys()];
console.log(indexSeq); // 示例输出: [0, 1, 2, 3]
六、5 种写法的关键特性对比与避坑总结
为了在实际工程中做出合理的选择,我们可以从语法形式、单数值表现、空槽特性及典型适用场景四个维度进行总结比对:
实践注意事项
- 避免使用未填充的
new Array(n):如果需要指定长度并初始化,务必紧接着调用.fill(initialValue),或者使用Array.from({ length: n }, (_, i) => initialValue),避免产生空槽导致后续map等遍历失效。 - 注意浅拷贝的引用共享:无论使用
[...arr]还是Array.from(arr),复制出来的都是浅拷贝(Shallow Copy)。如果原数组的元素是对象或嵌套数组,新数组与原数组仍会共享内部对象的引用。 - 优先使用字面量语法:在代码评审(Code Review)中,除非有明确的动态长度生成或格式转换需求,字面量
[]始终是首选方案。
实用工具推荐
绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
选择画质和输出格式,预览压缩前后的大小。