文章

未命名文章

// ===== for...in vs for...of 基础区别 =====

// 1. 数组遍历

const arr = ['a', 'b', 'c']

console.log('=== 数组遍历 ===')

console.log('for...in (遍历索引):')

for (const index in arr) {

console.log(`index: ${index}, value: ${arr[index]}`)

}

// 输出: index: 0, value: a

// index: 1, value: b

// index: 2, value: c

console.log('for...of (遍历值):')

for (const value of arr) {

console.log(`value: ${value}`)

}

// 输出: value: a

// value: b

// value: c

// 2. 对象遍历

const obj = { name: 'John', age: 30, city: 'NYC' }

console.log('\n=== 对象遍历 ===')

console.log('for...in (遍历属性名):')

for (const key in obj) {

console.log(`key: ${key}, value: ${obj[key]}`)

}

// 输出: key: name, value: John

// key: age, value: 30

// key: city, value: NYC

console.log('for...of 不能直接遍历普通对象:')

try {

for (const value of obj) {

console.log(value)

}

} catch (error) {

console.log('Error:', error.message) // obj is not iterable

}

// ===== 详细使用场景 =====

// 1. 字符串遍历

const str = 'hello'

console.log('\n=== 字符串遍历 ===')

console.log('for...in (遍历索引):')

for (const index in str) {

console.log(`${index}: ${str[index]}`)

}

console.log('for...of (遍历字符):')

for (const char of str) {

console.log(char)

}

// 2. Map 和 Set

const map = new Map([['a', 1], ['b', 2], ['c', 3]])

const set = new Set([1, 2, 3])

console.log('\n=== Map 遍历 ===')

console.log('for...of 遍历 Map:')

for (const [key, value] of map) {

console.log(`${key}: ${value}`)

}

console.log('\n=== Set 遍历 ===')

console.log('for...of 遍历 Set:')

for (const value of set) {

console.log(value)

}

// 3. 数组的陷阱示例

const sparseArray = [1, , , 4] // 稀疏数组

sparseArray.customProp = 'custom'

console.log('\n=== 稀疏数组遍历 ===')

console.log('for...in (会遍历所有可枚举属性):')

for (const key in sparseArray) {

console.log(`${key}: ${sparseArray[key]}`)

}

// 输出: 0: 1, 3: 4, customProp: custom

console.log('for...of (只遍历数组元素):')

for (const value of sparseArray) {

console.log(value)

}

// 输出: 1, undefined, undefined, 4

// 4. 原型链属性

function Person(name) {

this.name = name

}

Person.prototype.species = 'human'

const person = new Person('Alice')

person.age = 25

console.log('\n=== 原型链属性 ===')

console.log('for...in (会遍历继承属性):')

for (const key in person) {

console.log(`${key}: ${person[key]}`)

}

// 输出: name: Alice, age: 25, species: human

console.log('for...in + hasOwnProperty (只遍历自有属性):')

for (const key in person) {

if (person.hasOwnProperty(key)) {

console.log(`${key}: ${person[key]}`)

}

}

// 输出: name: Alice, age: 25

// ===== 性能对比 =====

console.log('\n=== 性能测试 ===')

const largeArray = Array.from({ length: 100000 }, (_, i) => i)

console.time('for...of')

for (const value of largeArray) {

// 简单操作

}

console.timeEnd('for...of')

console.time('for...in')

for (const index in largeArray) {

const value = largeArray[index]

// 简单操作

}

console.timeEnd('for...in')

console.time('traditional for')

for (let i = 0; i < largeArray.length; i++) {

const value = largeArray[i]

// 简单操作

}

console.timeEnd('traditional for')

// ===== 实际应用场景示例 =====

// 场景1: 处理配置对象

const config = {

apiUrl: 'https://api.example.com',

timeout: 5000,

retries: 3,

debug: true

}

console.log('\n=== 配置对象处理 ===')

console.log('验证配置:')

for (const key in config) {

if (config.hasOwnProperty(key)) {

console.log(`${key}: ${config[key]} (${typeof config[key]})`)

}

}

// 场景2: 数组数据处理

const userList = [

{ id: 1, name: 'Alice', active: true },

{ id: 2, name: 'Bob', active: false },

{ id: 3, name: 'Charlie', active: true }

]

console.log('\n=== 用户列表处理 ===')

console.log('活跃用户:')

for (const user of userList) {

if (user.active) {

console.log(`${user.name} (ID: ${user.id})`)

}

}

// 场景3: 表单数据验证

const formData = {

username: 'john_doe',

email: 'john@example.com',

age: '',

terms: false

}

const requiredFields = ['username', 'email', 'age']

console.log('\n=== 表单验证 ===')

console.log('必填字段检查:')

for (const field of requiredFields) {

if (!formData[field]) {

console.log(`❌ ${field} 是必填项`)

} else {

console.log(`✅ ${field}: ${formData[field]}`)

}

}

// 场景4: DOM 元素遍历

console.log('\n=== DOM 遍历模拟 ===')

const nodeList = ['div', 'span', 'p', 'a'] // 模拟 NodeList

console.log('遍历 DOM 元素:')

for (const element of nodeList) {

console.log(`处理元素: <${element}>`)

}

// ===== 最佳实践总结 =====

console.log('\n=== 最佳实践总结 ===')

// ✅ 推荐用法

const recommendations = {

'for...of': [

'遍历数组元素',

'遍历字符串字符',

'遍历 Set、Map 的值',

'遍历 NodeList',

'需要值而不是索引时'

],

'for...in': [

'遍历对象属性',

'需要属性名/键名时',

'动态访问对象属性',

'检查对象结构'

]

}

for (const loopType in recommendations) {

console.log(`\n${loopType} 适用场景:`)

for (const useCase of recommendations[loopType]) {

console.log(` • ${useCase}`)

}

}

// ❌ 常见错误示例

console.log('\n=== 常见错误 ===')

// 错误1: 用 for...in 遍历数组

const numbers = [10, 20, 30]

numbers.custom = 'property'

console.log('❌ 错误示例 - 用 for...in 遍历数组:')

for (const index in numbers) {

console.log(`${index}: ${numbers[index]}`) // 会包含 custom 属性

}

console.log('✅ 正确示例 - 用 for...of 遍历数组:')

for (const number of numbers) {

console.log(number) // 只遍历数组元素

}

// 错误2: 用 for...of 遍历普通对象

console.log('\n❌ 错误示例 - 用 for...of 遍历对象:')

const settings = { theme: 'dark', lang: 'en' }

try {

for (const value of settings) { // 会报错

console.log(value)

}

} catch (error) {

console.log('Error:', error.message)

}

console.log('✅ 正确示例 - 用 for...in 遍历对象:')

for (const key in settings) {

console.log(`${key}: ${settings[key]}`)

}

// 或者使用 Object 方法

console.log('✅ 或使用 Object.entries():')

for (const [key, value] of Object.entries(settings)) {

console.log(`${key}: ${value}`)

}

License:  CC BY 4.0