(我们假设有充分的理由希望如此。如果你想阅读正当理由,请参阅问题的结尾。)
我想获得与for in
循环相同的结果,但没有使用该语言结构。结果我的意思是只有一个属性名称的数组(我不需要重现在迭代时修改对象时会发生的行为)。
要将问题放入代码中,我希望在没有for in
的情况下实现此功能:
function getPropertiesOf(obj) {
var props = [];
for (var prop in obj)
props.push(prop);
return props;
}
根据我对关于the for in statement和the 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
的具体实施顺序?
说明:
编辑:为了满足@ lexicore的好奇心(但不是问题的真正部分),正确的理由如下。我开发了一个JavaScript编译器(来自Scala),for in
语言结构不是我想在编译器的中间表示中直接支持的东西的一部分。相反,我有一个&#34;内置&#34;函数getPropertiesOf
这基本上就是我作为第一个例子展示的内容。我试图通过用用户空间&#34;替换掉尽可能多的内置组件。实现(用Scala编写)。为了性能,我仍然有一个优化器,有时候&#34;内在化&#34;一些方法,在这种情况下,它会通过有效的第一次实现来内化getPropertiesOf
。但是为了使中间表示听起来有效,并且在优化器被禁用时工作,我需要真正实现该功能,无论性能成本如何,只要它是正确的。在这种情况下,我无法使用for in
,因为我的IR不能代表该构造(但我可以在任何对象上调用任意JavaScript函数,例如Object.keys
)。
答案 0 :(得分:3)
从规范的角度来看,只有在假设特定实现为for-in语句定义枚举的特定顺序时,您的分析才会正确:
如果实现定义了枚举的特定顺序 for-in语句,必须在步骤5中使用相同的枚举顺序 这个算法。
请参阅最后一句here。
因此,如果实现不提供此类特定订单,那么for-in和Object.keys
可能会返回不同的内容。那么,在这种情况下,即使是两个不同的for-ins也可以返回不同的东西。
非常有趣的是,如果对象没有改变,整个故事会减少两个插入是否会给出相同结果的问题。因为,如果不是这样,那你怎么能测试“相同”呢?
在实践中,这很可能是正确的,但我也可以轻易地想象一个对象可以在for-in调用之间动态地重建其内部结构。例如,如果经常访问某些属性,则实现可以重构哈希表,以便更有效地访问该属性。据我所知,规范并未禁止这样做。而且这也不是那么不合理。
所以你的问题的答案是:不,根据规范没有保证,但仍可能在实践中发挥作用。
<强>更新强>
我认为还有另一个问题。在哪里定义,原型链成员之间的属性顺序是什么?您可以按正确的顺序获得“自己的”属性,但它们是否与您的方式完全合并?例如,为什么儿童属性首先和父母的下一个?