使用对象数组时碰到的问题

发现问题

昨天在搭建一个博客时,碰到这样一个问题:

有如下这样一个页面。可以看到,页面中有多个 a 标签。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>
<body>
    <a href="https://imgchr.com/i/ioWnL4"></a>
    <a href="https://imgchr.com/i/ioWmyF"></a>
    <a href="https://imgchr.com/i/ioWeQU"></a>
    <a href="https://imgchr.com/i/ioWVzT"></a>
    <a href="https://imgchr.com/i/ioBqIS"></a>
</body>
</html>

我的需求是:拿到所有 <a> 的 href 属性,并存入数组里。

我的 JS 代码如下:

var a = document.getElementsByTagName('a');
var result = [];
var obj = {};

for (var i = 0; i < a.length; i++) {
    obj.href = a[i].href;
    result.push(obj);
}
console.log(result);

这段代码看上去没有什么问题,那么我们来看一下输入结果呢。

错误结果

可以看到,我们数组里面所有的值,均为最后一个 <a> 的 href 值。

奇怪!我们不是每次都给 obj.href 重新赋值了吗,为什么会是这个结果?

经过我的深思熟虑(没错…想了很久),终于想清楚为什么会是这种错误结果了。所以我决定写下来提醒自己,避免以后再犯这种错。

找到原因

JS 中数据类型分“基本类型”和“引用类型”两种。
基本类型的值就存在变量的地址里。而对于引用类型,其变量名是一个指针,指向的是存储这个变量的值的地址。

Object (对象)为引用类型,所以上面代码中的 obj 指向了一个地址,这个地址里保存了我们为 obj 所赋的值。由于 obj 这个地址一直没有发生改变,所以我们给数组 push 进去的所有元素,都指向了同一个地址,而由于每次赋值操作,使得后来的值覆盖了原先的值,导致所有的值都变成了最后一次所赋的值。

解决问题

问题找到了,那么我们怎么解决呢?答案很简单,只需这样修改代码:

var a = document.getElementsByTagName('a');
var result = [];
var obj = {};

for (var i = 0; i < a.length; i++) {
    obj = {};   // 在代码中加上这句
    obj.href = a[i].href;
    result.push(obj);
}
console.log(result);

在代码中新添加的一句,作用是:每次都为 obj 重新声明一片内存区域,使得每次赋值操作都是独立的,这样就不会出现值覆盖的情况了。

总结

这次出现这样的问题,是因为我对这方面的知识掌握的不够扎实,好在我之前有比较认真学习过这方面的知识,最终还是找到了原因。