【问题标题】:Empty slots in JavaScript objects?JavaScript 对象中的空槽?
【发布时间】:2017-06-13 06:27:36
【问题描述】:

最近我开始在 Firefox 的控制台中看到这个

Object [ <6 empty slots>, false, <3 empty slots>, 1 more… ]

当我有像

这样的对象时
{
  6: false,
  10: true
}

我只是想要一个可以访问的带有数字键的对象,但我对此感到担心,因为如果它跟踪空插槽,那么这一定意味着浪费了一些内存?

我的担忧是否有效,如果是,那么定义此类对象的正确方法是什么?

【问题讨论】:

  • 如果你这样做[{6:false, 10:true}],压缩视图输出Array [ Object[11] ]
  • @Jaromanda X Firefox 严格遵循标准,因此它反映在其控制台输出中。另一方面,Chrome 正在为用户/开发人员的易用性而简化,并且也忽略了许多 W3C 标准。因此,请解释行为背后的原因,而不是简单地抨击它。 Firefox 必须有充分的理由显示空元素。在真实情况下,它有助于调试问题,同时帮助发现 Array 的问题。

标签: javascript


【解决方案1】:

问题可能是由 Firefox 的 console.log 解释输入对象的方式引起的。不知何故,它被评估为一个数组而不是一个简单的对象。 Chrome 做得对。如果您深入了解 Javascript 中如何管理数组,您会发现以下内容:

数组不能使用字符串作为元素索引(如在关联数组中),但必须使用整数。使用方括号表示法(或点表示法)通过非整数设置或访问不会从数组列表本身设置或检索元素,但会设置或访问与该数组的对象属性集合关联的变量。数组的对象属性和数组元素列表是分开的,数组的遍历和变异操作不能应用于这些命名属性。 src

对此更好的理解是修改 Array 的 length 属性。尤其是当您使用[] 构造了数组时。要将元素添加到数组中,我们必须使用.push(...)。此函数使用length 属性(检查15.4.4.7 Array.prototype.push)。所以简而言之(互动示例在底部)

const arr = []; // length = 0
arr.push('1stEl', '2ndEl', '3thEl'); // length = 3
// this isn't allowed, but you can do this
arr[7] = '7thEl'; // length = 8

您会看到长度现在是 8 而不是 4。索引3..6 是保留的,但未定义。下面是控制台输出。

[
  "1stEl",
  "2ndEl",
  "3thEl",
  undefined,
  undefined,
  undefined,
  undefined,
  "7thEl"
]

如果你再次使用.push 方法,它会将新元素放在'7thEl' 元素之后(以此类推,索引8)。

要检查此对象使用的键,我们可以在数组上使用Object.keys()。你会得到

[
  "0",
  "1",
  "2",
  "7"
]

您会看到数值被用作键。就像你的对象,这是

{
  6: false,
  10: true
}

在这个对象上使用Object.keys 会得到["6", "10"]。它具有与上述类似的输出。因此,来自 firefox 的 console.log 将您的对象解释为数组,从而将其显示为数组。为了正确显示数组,它从键0 开始(逻辑上看,还需要检查源代码)并在键array.length - 1 结束。但是索引0,1..57..9 没有“定义”。因此它导致了这个输出

Object [ <6 empty slots>, false, <3 empty slots>, 1 more… ]

我不确定是否必须将此定义为 Firefox 控制台 API 的错误或故障...或者控制台输入(在初始化变量时)错误地读取了对象。

--- 实例--

const a = new Array(3);
console.log('using "new Array(...)" reserves memory space: ' + a.length);
console.log('---');

// using brackets
const b = [];
console.log('but what with [] ? At initial, we have ' + b.length);
b.push('1stEl', '2ndEl', '3thEl');
console.log('After push(\'1stEl\', \'2ndEl\', \'3thEl\'), we have ' + b.length);
// add to random index
b[7] = '7thEl';
console.log('After b[7] = \'7thEl\', we have ' + b.length);

console.log('displaying gives ', b);
console.log('using Object.keys: ', Object.keys(b));

// adding again
b.push('newEl');
console.log('After b.push(\'newEl\'), we have ' + b.length);

// object 
const obj = {
  6: false,
  10: true
};
console.log('obj defined as {6: false, 10: true }');
console.log('using Object.keys: ', Object.keys(obj));
console.log('obj: ', obj);

【讨论】:

  • 但是如果控制台将它解释为一个数组,那么为什么它说Object而不是Array,就像记录数组时的情况一样?
  • 如前所述,我必须检查源代码。但我认为它在显示其内容之前显示“数组”或“对象”时使用Object.prototype.toString.call(此处为“optc”)。如果您在 b = [] 上使用 optc,您将获得“Array”。如果你在你的对象上使用它,你会得到“对象”。
【解决方案2】:

Javascript 使用稀疏数组。 “由于数组的长度可以随时更改,并且数据可以存储在数组中的非连续位置,因此 JavaScript 数组不能保证是密集的;这取决于程序员选择使用它们的方式。” (source)

如果对象的类型是Array,那么使用的内存就是引擎的实现细节。在您的情况下,对象是objects,因此它只占用对象本身的内存,并存储属性名称和对属性值的引用。

【讨论】:

  • 其实javascript中没有数组。 “数组”只是一个具有特殊“长度”属性的对象。
  • 你知道,我可以发誓你可以从 typeof 中得到“Array”,但我只是在 js 控制台中运行 typeof [] 并得到了“object”。现在我记得为什么我更喜欢非 JavaScript 语言了!
猜你喜欢
  • 2016-01-09
  • 1970-01-01
  • 1970-01-01
  • 2013-11-12
  • 2014-06-07
  • 1970-01-01
  • 2019-08-09
  • 2021-12-18
  • 1970-01-01
相关资源
最近更新 更多