【问题标题】:Array-like object in javascriptjavascript中的类数组对象
【发布时间】:2009-12-28 20:27:06
【问题描述】:

查看 Closure 库中的 dom.js 源代码,我发现了这个(在 goog.dom.getElementsByTagNameAndClass_ 中):

if (opt_class) {
var arrayLike = {};
var len = 0;
for (var i = 0, el; el = els[i]; i++) {
  var className = el.className;
  // Check if className has a split function since SVG className does not.
  if (typeof className.split == 'function' &&
      goog.array.contains(className.split(' '), opt_class)) {
    arrayLike[len++] = el;
  }
}
arrayLike.length = len;
return arrayLike;
}

与常规数组相比,这样做有什么好处?

【问题讨论】:

    标签: javascript google-closure-library


    【解决方案1】:

    代码作者使用空 JavaScript 对象作为数组对象的基础,即可以通过索引访问并具有长度属性的对象。

    我能想到的原因可能有两个:

    1. 内存使用 - 如果数组由分配 n 个元素的实现支持,当它达到其限制时,它会增长一些因素以增加其容量,从而浪费 capacity - length 的内存
    2. cpu 时间 - 实现者选择插入速度而不是随机访问速度 - 与随机访问相比,此方法的返回更有可能按顺序迭代,并且在插入时调整数组大小会导致分配复制释放,这会造成 cpu 损失

    我敢打赌,在其他 JavaScript 库中也会找到类似的代码,这是基准测试和找到适合不同浏览器的最佳解决方案的结果。

    贾斯汀评论后编辑

    在进一步谷歌搜索后,类似数组的对象似乎在 JavaScript 开发人员中很常见:结帐 JavaScript:David Flanagan 的权威指南,它有一个完整的 sub-chapter on Array-like objects。还有theseguys 提到他们。

    没有提到为什么要更喜欢类数组与数组对象。这可能是一个很好的 SO 问题。

    所以第三个选项可能是关键:遵守 JavaScript API 的规范。

    【讨论】:

    • 容量不一定随着每次插入而增长。每当达到容器的容量时,将容量增加大于 1 的值是一种常见的优化。我还没有查看实际引擎的代码,但我愿意为当前的优化投入资金。至于 CPU 时间,我的基准测试结果正好相反,因为它只在 FF 3.5.6 中。
    • 您当然是对的,但我并没有(有意)暗示每次插入容量都会增加...
    【解决方案2】:

    在这种情况下,我的猜测是 arrayLike[len++] = el 是对 actualArray.push(el) 的优化。然而,在做了一个简单的基准测试(结果下方提供的代码)之后,似乎这种方法实际上比使用push 方法以及相同构造技术的标准数组要慢。

    结果(来自 OS X 10.5.8、FF 3.5.6)*:

    push construction:        199ms (fastest)
    indexed construction:     209ms
    associative construction: 258ms (slowest)
    

    总之,为什么 Closure 在这种情况下使用关联数组是我无法理解的。可能是有原因的(例如,这种技术可能在 Chrome 中表现得更好,或者不那么可疑,这种技术在未来的 JavaScript 引擎版本中可能会表现得更好),但在这种情况下我认为没有充分的理由。

    * 未提供平均值,因为时间因测试运行而异,但始终导致相同的顺序。有兴趣的可以自己动手。

    基准代码:

    var MAX = 100000, i = 0, 
        a1 = {}, a2 = [], a3 = [],
        value = "";
    
    for ( i=0; i<1024; ++i ) {
        value += "a";
    }
    
    console.time("associative construction");
    for ( i=0; i<MAX; ++i ) {
        a1[i] = value;
    }
    a1.length = i;
    console.timeEnd("associative construction");
    
    console.time("push construction");
    for ( i=0; i<MAX; ++i ) {
        a2.push(value);
    }
    console.timeEnd("push construction");
    
    console.time("indexed construction");
    for ( i=0; i<MAX; ++i ) {
        a3[i] = value;
    }
    console.timeEnd("indexed construction");
    

    value 的大小和类型对于测试来说无关紧要,因为 JavaScript 使用 copy-on-write。一个大的 (1kb) value 用于说服那些不熟悉 JavaScript 特性的读者。

    【讨论】:

    • 每当进行基准测试时,请记住对 IE 6 进行基准测试。您看到的许多奇怪的性能黑客可能会解决 IE 6 的一些可怕的性能特征,而对其他设备几乎没有任何好处浏览器。
    • 确实,我知道这一点。但是,就像我说的那样,我目前使用的是 Mac,无法在所有浏览器上进行测试。
    • 没错,布赖恩。由于浏览器的市场份额,在性能上偏爱 IE 是有道理的(那里有很多反对者,但事实就是事实)。此外,请务必使用 Google 的 V8 引擎运行此测试。
    【解决方案3】:

    我认为这个例子创建了一个类数组对象而不是一个真正的数组,因为其他 DOM 方法也返回类数组对象(NodeList)。

    在 API 中始终使用“array-likes”会迫使开发人员避免使用特定于数组的方法(改为使用 goog.array),因此当有人后来决定将 getElementsByTagNameAndClass 调用更改为,例如, getElementByTagName。

    【讨论】:

      【解决方案4】:
      goog.dom.getElementsByTagNameAndClass_
      

      您正在处理 html 集合。 arrayLike 对象是节点列表的快照,而不是“实时”集合对象。它使它像索引数组一样容易使用,并且如果您在循环其成员时创建或删除节点,则不太可能导致复杂化。

      【讨论】:

        【解决方案5】:

        我看不出有什么不同,因为alert(typeof []); 返回“对象”。每当我看到这样的东西(尤其是来自 Goog)时,我不得不假设它是为了提高性能。

        它本质上是返回一个没有所有继承函数的数组,例如pushpop

        【讨论】:

        • 我也是这么想的。原来我们都错了。查看我的基准
        • 实际上,上次我检查时,Google Closure 根本没有针对性能进行优化,看起来更像是遗留代码。
        【解决方案6】:

        据我所知,没有任何好处,因为除了创建语法之外,这与“常规数组”之间实际上没有任何区别 - 所有 Javascript 对象都是关联数组。

        【讨论】:

        • 不完全是。对象/关联数组没有 length 属性,这就是 Closure 库在这种情况下添加它的原因(例如:console.log({}.length, [].length) => "undefined 0")。
        • 此外,数组 ([]) 具有更广泛的方法集(参见 Array.prototype)。
        • 数组扩展对象(关联数组);它添加了所有数组方法,并跟踪长度。请注意,length 不会跟踪非整数键对象。是PHP他们完全一样
        猜你喜欢
        • 2011-09-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-16
        • 2019-07-28
        • 2021-11-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多