未命名文章
// ===== 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}`)
}