阿里云主机折上折
  • 微信号
Current Site:Index > Standardization of the for-in mechanism

Standardization of the for-in mechanism

Author:Chuan Chen 阅读数:33710人阅读 分类: JavaScript

Background of Standardization of the for-in Mechanism in ECMAScript 11

ECMAScript 11 (ES2020) standardized the traversal order of for-in loops, resolving long-standing inconsistencies in implementations across different JavaScript engines. Previously, the traversal order of for-in loops varied between engines like V8 and SpiderMonkey, forcing developers to handle property order issues manually. ES11 explicitly mandates that for-in should traverse enumerable properties in the order they were created, while also standardizing the handling of prototype chain properties.

Basic Behavior of for-in Loops

The for-in loop is used to iterate over an object's enumerable properties (excluding Symbol properties) and those on its prototype chain. After ES11's standardization, its predictability has significantly improved:

const obj = {
  a: 1,
  b: 2,
  [Symbol('secret')]: 3
};

Object.defineProperty(obj, 'c', {
  value: 3,
  enumerable: false
});

// Only enumerable non-Symbol properties are traversed  
for (const key in obj) {
  console.log(key); // Outputs 'a', 'b' in order  
}

Details of Standardized Property Traversal Order

ES11 explicitly defines the following rules for for-in traversal order:

  1. Numeric keys are sorted in ascending order (including string keys that can be converted to numbers).
  2. Non-numeric keys follow their creation order.
  3. Prototype chain properties are traversed last.
const obj = {
  '2': 'two',
  '1': 'one',
  'b': 'bee',
  'a': 'ay'
};

// Numeric keys are sorted first, non-numeric keys retain creation order  
for (const key in obj) {
  console.log(key);  
  // Output order: '1', '2', 'b', 'a'  
}

Handling Rules for Prototype Chain Properties

When an object has a prototype chain, for-in traverses its own properties first before moving up the chain:

const parent = { a: 1, b: 2 };
const child = { c: 3, d: 4 };
Object.setPrototypeOf(child, parent);

for (const key in child) {
  console.log(key);
  // Pre-ES11 output might vary: c, d, a, b or a, b, c, d  
  // Post-ES11 output is fixed: c, d, a, b (own properties first)  
}

Differences Compared to Object.keys()

Although Object.keys() also returns an object's property list, key differences exist:

  • Object.keys() excludes prototype chain properties.
  • Its sorting rules align with for-in.
  • It returns an array instead of an iterator.
const proto = { a: 1 };
const obj = Object.create(proto);
obj.b = 2;
obj.c = 3;

console.log(Object.keys(obj)); // ['b', 'c']  
for (const key in obj) {
  console.log(key); // 'b', 'c', 'a'  
}

Performance Optimization Recommendations

Since for-in traverses the entire prototype chain, consider these optimizations for performance-critical scenarios:

  1. Use hasOwnProperty to filter out prototype properties.
  2. Prefer Object.keys() for objects with known structures.
  3. For loop interruption, use for-of + Object.keys().
// Optimization example  
const obj = { /* large number of properties */ };  
for (const key in obj) {
  if (!obj.hasOwnProperty(key)) continue;  
  // Only processes own properties  
}

// Better approach  
for (const key of Object.keys(obj)) {
  // Directly retrieves own enumerable properties  
}

Handling Edge Cases

ES11 also standardizes some edge cases:

  • Deleting properties during traversal does not affect the already iterated list.
  • Whether newly added properties are traversed depends on the implementation.
  • Non-configurable properties may still be skipped.
const obj = { a: 1, b: 2, c: 3 };  
for (const key in obj) {
  console.log(key);  
  if (key === 'b') delete obj.c;  
  // Still outputs 'c' because the traversal list is already determined  
}

Practical Use Cases

Standardized for-in is particularly useful in:

  1. Configuration processing where property order matters.
  2. Deep object cloning while preserving property order.
  3. Ensuring consistent field order during object serialization.
// Configuration merging example  
function mergeConfig(target, ...sources) {
  for (const source of sources) {
    for (const key in source) {
      if (source.hasOwnProperty(key)) {
        target[key] = source[key];  
      }
    }
  }
  return target;  
}

// Ensures consistent property override order  
const config = mergeConfig({}, { a: 1 }, { a: 2 });  

Current Browser Compatibility

As of now, major JavaScript engine implementations:

  • V8 (Chrome 80+) fully complies with ES11.
  • SpiderMonkey (Firefox 72+) has adjusted its implementation.
  • JavaScriptCore (Safari 13.1+) follows the new standard.
  • Babel 7.8+ transpiles for-in to simulate standard behavior.
// Check if the engine complies  
function checkForInOrder() {
  const obj = { '2': 2, '1': 1, 'b': 'b', 'a': 'a' };  
  const keys = [];  
  for (const key in obj) keys.push(key);  
  return keys.join(',') === '1,2,b,a';  
}

console.log(checkForInOrder()); // Modern browsers should output true  

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.