获得与for..in循环相同的结果,没有任何for..in循环

时间:2014-10-18 22:35:33

标签: javascript ecmascript-5

(我们假设有充分的理由希望如此。如果你想阅读正当理由,请参阅问题的结尾。)

我想获得与for in循环相同的结果,但没有使用该语言结构。结果我的意思是只有一个属性名称的数组(我不需要重现在迭代时修改对象时会发生的行为)。

要将问题放入代码中,我希望在没有for in的情况下实现此功能:

function getPropertiesOf(obj) {
  var props = [];
  for (var prop in obj)
    props.push(prop);
  return props;
}

根据我对关于the for in statementthe Object.keys method的ECMAScript 5.1规范的理解,以下实现似乎应该是正确的:

function getPropertiesOf(obj) {
  var props = [];
  var alreadySeen = {};

  // Handle primitive types
  if (obj === null || obj === undefined)
    return props;
  obj = Object(obj);

  // For each object in the prototype chain:
  while (obj !== null) {
    // Add own enumerable properties that have not been seen yet
    var enumProps = Object.keys(obj);
    for (var i = 0; i < enumProps.length; i++) {
      var prop = enumProps[i];
      if (!alreadySeen[prop])
        props.push(prop);
    }

    // Add all own properties (including non-enumerable ones)
    // in the alreadySeen set.
    var allProps = Object.getOwnPropertyNames(obj);
    for (var i = 0; i < allProps.length; i++)
      alreadySeen[allProps[i]] = true;

    // Continue with the object's prototype
    obj = Object.getPrototypeOf(obj);
  }

  return props;
}

我们的想法是明确地遍历原型链,并使用Object.keys在链的每个对象中获取自己的属性。我们排除链中先前对象中已经看到的属性名称,包括它们被视为不可枚举的时间。这种方法甚至应该尊重the additional guarantee mentioned on MDN

  

Object.keys()方法返回给定对象自己的数组   可枚举属性,的顺序与a提供的顺序相同   for...in循环 [...]。

(重点是我的)

我玩了一下这个实现,但我还没能打破它。

所以问题:

我的分析是否正确?或者我是否忽略了使该实现不正确的规范细节?

您是否知道另一种方法,在所有情况下都符合for in的具体实施顺序?

说明:

  • 我不关心ECMAScript&lt; 5.1。
  • 我不关心表现(这可能是灾难性的)。

编辑:为了满足@ lexicore的好奇心(但不是问题的真正部分),正确的理由如下。我开发了一个JavaScript编译器(来自Scala),for in语言结构不是我想在编译器的中间表示中直接支持的东西的一部分。相反,我有一个&#34;内置&#34;函数getPropertiesOf这基本上就是我作为第一个例子展示的内容。我试图通过用用户空间&#34;替换掉尽可能多的内置组件。实现(用Scala编写)。为了性能,我仍然有一个优化器,有时候&#34;内在化&#34;一些方法,在这种情况下,它会通过有效的第一次实现来内化getPropertiesOf。但是为了使中间表示听起来有效,并且在优化器被禁用时工作,我需要真正实现该功能,无论性能成本如何,只要它是正确的。在这种情况下,我无法使用for in,因为我的IR不能代表该构造(但我可以在任何对象上调用任意JavaScript函数,例如Object.keys)。

1 个答案:

答案 0 :(得分:3)

从规范的角度来看,只有在假设特定实现为for-in语句定义枚举的特定顺序时,您的分析才会正确:

  

如果实现定义了枚举的特定顺序   for-in语句,必须在步骤5中使用相同的枚举顺序   这个算法。

请参阅最后一句here

因此,如果实现提供此类特定订单,那么for-in和Object.keys可能会返回不同的内容。那么,在这种情况下,即使是两个不同的for-ins也可以返回不同的东西。

非常有趣的是,如果对象没有改变,整个故事会减少两个插入是否会给出相同结果的问题。因为,如果不是这样,那你怎么能测试“相同”呢?

在实践中,这很可能是正确的,但我也可以轻易地想象一个对象可以在for-in调用之间动态地重建其内部结构。例如,如果经常访问某些属性,则实现可以重构哈希表,以便更有效地访问该属性。据我所知,规范并未禁止这样做。而且这也不是那么不合理。

所以你的问题的答案是:不,根据规范没有保证,但仍可能在实践中发挥作用

<强>更新

我认为还有另一个问题。在哪里定义,原型链成员之间的属性顺序是什么?您可以按正确的顺序获得“自己的”属性,但它们是否与您的方式完全合并?例如,为什么儿童属性首先和父母的下一个?