属性名的本质,伪数组存在的原因
欢迎来到我的博客!
使用 Markdown 格式编写。
今天学到了什么
- 属性名的本质
- 伪数组存在的原因
属性名的本质
这是一个非常好的问题!你的观察很仔细。
简单来说:在 JavaScript 中,对象的属性名(键)其实本质上都是字符串,但写的时候可以“偷懒”省略引号。
下面是详细的解释:
-
对象的属性名不仅仅是那串字符 在定义对象
msg时:const msg = {"code": 200, // 写了引号"data": [...] // 写了引号}和下面这种写法,在 JavaScript 引擎看来是完全一样的:
const msg = {code: 200, // 没写引号(只要符合变量命名规则,就可以省略引号)data: [...] // 没写引号}虽然在 JSON 格式(通常是后台传回来的数据格式)中严格要求属性名必须加双引号,但在 JavaScript 代码里操作这些对象时,属性名
data和"data"指向的是同一个东西。 -
解构赋值的匹配规则 当你写:
const { data } = msgJavaScript 的意思是:“请去
msg对象里找一个叫名为data的属性,并把它赋值给一个叫data的变量。”它不需要你去匹配**“定义时的写法”(是否有引号),而是匹配“属性的名字”**。因为在内存里,这个属性的名字就是
data。
总结
- 定义时:
"data":和data:是一样的(只要通过了语法检查)。 - 解构时:只要写属性名
data就能找到它,不需要加引号。
如果非要加引号会怎样?
如果你在解构时强行加引号,语法会变复杂,通常我们只有在属性名包含特殊字符(比如 data-list 这种带横杠的)时,才被迫需要用引号别名的方式来解构,例如 const { "data-list": list } = obj,但标准变量名直接写即可。
伪数组存在的原因
伪数组(Pseudo-array / Array-like object)的存在主要是出于 性能优化、特定功能需求 以及 历史遗留 的原因。
简单来说,伪数组就是一个拥有 length 属性和数值索引(0, 1, 2…)的对象,但它没有数组原型链上的方法(如 push, pop, map, filter 等)。
以下是它存在的几个主要原因:
1. 性能与内存优化 (轻量级)
真正的数组(Array)在 JavaScript 引擎中是非常复杂的对象,它背负了整套原型链方法(数十种方法)和特殊的引擎优化机制。
- DOM 操作场景:当你需要获取成千上万个 DOM 元素(如
document.querySelectorAll或arguments)时,创建一个简单的“带索引的对象”比创建一个完整的“数组对象”要快得多,占用的内存也更少。 - 浏览器设计者认为,绝大多数时候你只用来遍历(for循环),不需要对这些元素列表进行
pop(弹出)或splice(截接)等复杂操作。
2. Live Collection (动态集合) 特性
某些伪数组(如 HTMLCollection,通过 getElementsByClassName 获取)具有 “实时性” (Live) 的特点。
- 数组是静态的:
const arr = [div1, div2],如果你删除了页面上的div1,数组里的div1引用依然存在,不会自动消失。 - 伪数组可以是动态的:
HTMLCollection会随着 DOM 树的改变自动更新。如果页面删除了一个元素,伪数组的length会自动减 1。普通的数组很难高效地实现这种“自动同步”的魔法。
3. 防止误操作 (安全性)
DOM 元素集合通常代表页面上的现状。
- 如果你在一个 DOM 集合上调用
pop(),这意味着什么?是把元素从集合里移除,还是把元素从页面上删除?这会有歧义。 - 通过不提供修改数组的方法(如
push,shift),浏览器API 限制了开发者只能“读取”列表,而不能随意修改列表结构,从而避免了很多潜在的 Bug。
4. 历史遗留原因
在 JavaScript 早期(ES3/ES5 时代),语言设计比较简单。当时 arguments 对象也是用来访问函数参数的唯一方式,为了效率并没有把它设计成真数组。随着时间推移,为了保持 Web 兼容性,这些设计就保留了下来。
总结
| 特性 | 真数组 (Array) | 伪数组 (Array-like) |
|---|---|---|
| 主要用途 | 存储数据,进行各种数据操作 | 存储 DOM 节点引用、函数参数等临时列表 |
| 方法支持 | forEach, map, filter, reduce… | 通常只有 length 和索引访问 (新版 NodeList 支持 forEach) |
| 内存开销 | 较大 | 较小 |
| 动态更新 | 否 (静态快照) | 部分是 (如 collection 会随 DOM 变化) |
转换方法(即你当前文件正在学习的内容):
由于现代开发中我们经常需要用到 map 或 filter,所以 ES6 提供了 Array.from() 或者展开运算符 [...] 来方便地把它们转成真数组:
// 你的代码中的例子const lis = document.querySelectorAll('ul li') // 这是一个 NodeList (伪数组)
// 转换const realArray = Array.from(lis)// 或者const realArray2 = [...lis]
// 现在可以愉快地使用了realArray.map(item => console.log(item))赞助支持
如果这篇文章对你有帮助,欢迎赞助支持!
部分内容可能已过时