Javascript数组稀疏吗?

Ber*_*rry 90 javascript sparse-matrix

也就是说,如果我使用当前时间作为数组的索引:

array[Date.getTime()] = value;
Run Code Online (Sandbox Code Playgroud)

解释器会实例化从0到现在的所有元素吗?不同的浏览器有不同的做法吗?

我记得曾经是AIX内核中的一个错误,它会根据请求创建伪ttys,但如果你这样做,比如"echo>/dev/pty10000000000"它会创建/ dev/pty0,/ dev/pty1, ......然后摔倒了.在贸易展览会上很有趣,但我不希望这发生在我的客户身上.

Joh*_*ica 62

对,他们是.它们实际上是内部的哈希表,因此您不仅可以使用大整数,还可以使用字符串,浮点数或其他对象.toString()在添加到散列之前,所有键都会转换为字符串.您可以使用一些测试代码确认:

<script>
  var array = [];
  array[0] = "zero";
  array[new Date().getTime()] = "now";
  array[3.14] = "pi";

  for (var i in array) {
      alert("array["+i+"] = " + array[i] + ", typeof("+i+") == " + typeof(i));
  }
</script>
Run Code Online (Sandbox Code Playgroud)

显示:

array[0] = zero, typeof(0) == string
array[1254503972355] = now, typeof(1254503972355) == string
array[3.14] = pi, typeof(3.14) == string
Run Code Online (Sandbox Code Playgroud)

请注意我是如何使用for...in语法的,它只为您提供实际定义的索引.如果您使用更常见for (var i = 0; i < array.length; ++i)的迭代样式,那么您显然会遇到非标准数组索引的问题.

  • 那么你只能使用Strings作为对象键,不多也不少.字符串应为您使用的类型,键的类型应为String.你不使用整数,也不使用非整数,除非你继续使用String.任意对象都是正确的. (67认同)
  • JavaScript中的所有对象键始终为`String`; 你放在下标中的任何东西都会得到`toString()`-ed.将此与大数字的整数不精确相结合,这意味着如果设置'a [9999999999999999] = 1`,`a [10000000000000000]`将为1(以及更多令人惊讶的行为).使用非整数作为键是非常不明智的,任意对象都是正确的. (13认同)
  • 数组索引必须是整数.array [3.14] = pi可以工作,因为Array不是来自Object.示例:var x = []; x [.1] = 5; 然后x的长度为0. (8认同)
  • 如果可能的话,大多数JS实现在实际数组中存储数字索引属性; 然而,这就是幕后魔术:从语言的角度来看,数组是具有魔术"长度"属性的常规对象 (7认同)
  • @John:`length`只在`for..in`循环中不可见,因为它设置了'DontEnum`标志; 在ES5中,属性属性称为"enumerable",可以通过`Object.defineProperty()显式设置. (6认同)
  • 哇。var y = {}; var x = []; x[y] = 5; 那么 x["[object Object]"] 是 5。 (2认同)
  • 这真是过时了。实际的实现。在ECMAScript规范中可以做很多优化工作(并允许这样做)-显示的情况只是证明了这种实现需要“后退”以支持这种用例。(然后,我想有一个额外的问题,即如果数组包含未设置的索引,数组是否稀疏。) (2认同)

Chr*_*oph 36

实现JavaScript数组的精确程度因浏览器而异,但它们通常会回退到稀疏实现 - 很可能与常规对象的属性访问相同 - 如果使用实际数组效率低下.

你必须要求对具体实现有更多了解的人来回答激动地触发从密集到稀疏的转变,但你的例子应该是完全安全的.如果你想获得一个密集的数组,你应该使用一个显式长度参数调用构造函数,并希望你实际上得到一个.

有关olliej的更详细说明,请参阅此答案.


Joh*_*her 10

您可以通过使用为此类事物设计的javascript语法来避免此问题.您可以将其视为字典,但"for ... in ..."语法将让您全部抓取它们.

var sparse = {}; // not []
sparse["whatever"] = "something";
Run Code Online (Sandbox Code Playgroud)


Jus*_*ove 7

Javascript对象是稀疏的,并且数组只是具有自动维护长度属性的专用对象(实际上是比最大索引大一个,而不是已定义元素的数量)和一些其他方法.你是安全的; 如果您需要额外的功能,则使用数组,否则使用对象.

  • 这是从语言的角度来看; 实现实际上使用真实数组来存储密集的数字属性 (4认同)

Cha*_*iam 6

与 JavaScript 一样,答案通常是“它有点奇怪......”

内存使用没有定义,任何实现都是愚蠢的。理论上,const a = []; a[1000000]=0;可以像const a = [];. 在实践中,即使是微软也避免了这些实现。

Justin Love指出,length 属性是最高索引集。但只有在索引是整数时才更新。

所以,数组是稀疏的。但是像reduce()、Math.max()和“for ... of”这样的内置函数将遍历从0到长度的整个可能整数索引范围,访问许多返回'undefined'的函数。但是 'for ... in' 循环可能会如您所愿,只访问定义的键。

下面是一个使用 Node.js 的例子:

"use strict";
const print = console.log;

let a = [0, 10];
// a[2] and a[3] skipped
a[4] = 40;
a[5] = undefined;  // which counts towards setting the length
a[31.4] = 'ten pi';  // doesn't count towards setting the length
a['pi'] = 3.14;
print(`a.length= :${a.length}:, a = :${a}:`);
print(`Math.max(...a) = :${Math.max(a)}: because of 'undefined values'`);
for (let v of a) print(`v of a; v=:${v}:`);
for (let i in a) print(`i in a; i=:${i}: a[i]=${a[i]}`);
Run Code Online (Sandbox Code Playgroud)

给予:

a.length= :6:, a = :0,10,,,40,:
Math.max(...a) = :NaN: because of 'undefined values'
v of a; v=:0:
v of a; v=:10:
v of a; v=:undefined:
v of a; v=:undefined:
v of a; v=:40:
v of a; v=:undefined:
i in a; i=:0: a[i]=0
i in a; i=:1: a[i]=10
i in a; i=:4: a[i]=40
i in a; i=:5: a[i]=undefined
i in a; i=:31.4: a[i]=ten pi
i in a; i=:pi: a[i]=3.14
Run Code Online (Sandbox Code Playgroud)

但。还有更多关于 Arrays 的极端情况尚未提及。


pzr*_*zrq 5

可以使用非标准的process.memoryUsage()凭经验确认 NodeJS 的稀疏性(或密集性

有时节点足够聪明以保持数组稀疏:

Welcome to Node.js v12.15.0.
Type ".help" for more information.
> console.log(`The script is using approximately ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024 * 100) / 100} MB`)
The script is using approximately 3.07 MB
undefined
> array = []
[]
> array[2**24] = 2**24
16777216
> array
[ <16777216 empty items>, 16777216 ]
> console.log(`The script is using approximately ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024 * 100) / 100} MB`)
The script is using approximately 2.8 MB
undefined
Run Code Online (Sandbox Code Playgroud)

有时节点会选择使其密集(这种行为将来可能会得到优化):

> otherArray = Array(2**24)
[ <16777216 empty items> ]
> console.log(`The script is using approximately ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024 * 100) / 100} MB`)
The script is using approximately 130.57 MB
undefined
Run Code Online (Sandbox Code Playgroud)

然后再次稀疏:

> yetAnotherArray = Array(2**32-1)
[ <4294967295 empty items> ]
> console.log(`The script is using approximately ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024 * 100) / 100} MB`)
The script is using approximately 130.68 MB
undefined
Run Code Online (Sandbox Code Playgroud)

因此,也许使用密集数组来感受原始 AIX 内核错误可能需要强制使用range-alike

> denseArray = [...Array(2**24).keys()]
[
   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,
  12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
  24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
  36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
  48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
  60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
  72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
  84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
  96, 97, 98, 99,
  ... 16777116 more items
]
> console.log(`The script is using approximately ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024 * 100) / 100} MB`);
The script is using approximately 819.94 MB
undefined
Run Code Online (Sandbox Code Playgroud)

因为为什么不让它倒下?

> tooDenseArray = [...Array(2**32-1).keys()]

<--- Last few GCs --->

[60109:0x1028ca000]   171407 ms: Scavenge 1072.7 (1090.0) -> 1056.7 (1090.0) MB, 0.2 / 0.0 ms  (average mu = 0.968, current mu = 0.832) allocation failure 
[60109:0x1028ca000]   171420 ms: Scavenge 1072.7 (1090.0) -> 1056.7 (1090.0) MB, 0.2 / 0.0 ms  (average mu = 0.968, current mu = 0.832) allocation failure 
[60109:0x1028ca000]   171434 ms: Scavenge 1072.7 (1090.0) -> 1056.7 (1090.0) MB, 0.2 / 0.0 ms  (average mu = 0.968, current mu = 0.832) allocation failure 


<--- JS stacktrace --->

==== JS stack trace =========================================

    0: ExitFrame [pc: 0x100931399]
    1: StubFrame [pc: 0x1008ee227]
    2: StubFrame [pc: 0x100996051]
Security context: 0x1043830808a1 <JSObject>
    3: /* anonymous */ [0x1043830b6919] [repl:1] [bytecode=0x1043830b6841 offset=28](this=0x104306fc2261 <JSGlobal Object>)
    4: InternalFrame [pc: 0x1008aefdd]
    5: EntryFrame [pc: 0x1008aedb8]
    6: builtin exit frame: runInThisContext(this=0x104387b8cac1 <ContextifyScript map = 0x1043...

FATAL ERROR: invalid array length Allocation failed - JavaScript heap out of memory

Writing Node.js report to file: report.20200220.220620.60109.0.001.json
Node.js report completed
 1: 0x10007f4b9 node::Abort() [/Users/pzrq/.nvm/versions/node/v12.15.0/bin/node]
 2: 0x10007f63d node::OnFatalError(char const*, char const*) [/Users/pzrq/.nvm/versions/node/v12.15.0/bin/node]
 3: 0x100176a27 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/Users/pzrq/.nvm/versions/node/v12.15.0/bin/node]
 4: 0x1001769c3 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/Users/pzrq/.nvm/versions/node/v12.15.0/bin/node]
 5: 0x1002fab75 v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/Users/pzrq/.nvm/versions/node/v12.15.0/bin/node]
 6: 0x1005f3e9b v8::internal::Runtime_FatalProcessOutOfMemoryInvalidArrayLength(int, unsigned long*, v8::internal::Isolate*) [/Users/pzrq/.nvm/versions/node/v12.15.0/bin/node]
 7: 0x100931399 Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit [/Users/pzrq/.nvm/versions/node/v12.15.0/bin/node]
 8: 0x1008ee227 Builtins_IterableToList [/Users/pzrq/.nvm/versions/node/v12.15.0/bin/node]
Abort trap: 6
Run Code Online (Sandbox Code Playgroud)

  • 很好,我有点惊讶我十年前的问题仍然有意义! (2认同)