深度解析for...in和for...of究竟有啥区别(麻麻再也不担心,我学不明白啦)

前言

该篇文章会给你普及并且深入 for…in 和 for…of 的遍历的相关知识,还会给你介绍对象属性描述符和迭代器这两个知识点(简单概念,会在其他文章详讲),当你理解了这两个知识点后,你就会真正的明白 for…in 和 for…of 的使用场景,篇幅较长,希望大家能够坚持看下去,有所收获🎉🎉🎉

这里先提前告诉你主要内容:for…in 是用来枚举对象属性,for…of 用来遍历迭代器(有序结构数据)


一、对象属性描述符 - for…in

1. 获取对象属性描述符的 API

首先我们定义一个对象,并声明一个属性

const obj = { ikun: 666 }

获取属性描述符状态 API:Object.getOwnPropertyDescriptor()

Object.getOwnPropertyDescriptor(obj, 'ikun')
image-20230111183133328

这里我们可以看到,这里有几个属性:configurable、enumerable、value、writable

这里面只有 enumerable(可枚举) 会对 for...in 枚举有影响

其他属性该篇文章不做过深讲解,会写篇文章进行详讲


2. 设置对象属性描述符的 API

首先我们定义一个对象,并声明一个属性

const obj = { ikun: 666 }

获取属性描述符状态 API:Object.defineProperty()

Object.defineProperty(obj, 'rap', {
	value: "music",
  ...,
})

image-20230111183917939

3. enumerable

接下来通过 Object.defineProperty() 来给对象添加属性并配置

const obj = { ikun: 666 }

Object.defineProperty(obj, 'music', {
  value: 200,
  enumerable: false
})
Object.defineProperty(obj, 'basketball', {
  value: "基尼太美",
  enumerable: true
})

for (let key in obj) {
  console.log(key)  // ikun basketball
}

此时只能遍历出来,ikun, basketball 两个属性,但可以通过 in 来判断某个属性是否存在

'ikun' in obj  // true
'music' in obj  // true
'basketball' in obj  // true

设置了 enumerable 属性后,无法被 for...in 遍历,但可以通过 in 来判断是否存在该属性

4. 原型链属性和方法的 enumerable

学过 JavaScript 高阶语法的同学都应该知道,当我们在获取对象属性的时候,他会现在自身上寻找有没有这个属性,自身没有这个属性,则会在原型链上面进行寻找

当我们在 for...in 枚举属性时,他也会把原型链上面的属性和方法一起枚举出来

但你们知道为啥在使用 for...in 的时候并没有遍历出来原型链上的属性和方法吗?无奖竞猜🎈

3

2

1

答案:原型链上面的属性和方法都被设置了 enumerable 为 false

5. 实验:for…in 会不会遍历原型链上面的属性

是吗?我不信?好小子,那我们可以来做一个实验,我们来获取所有原型链最终的归宿 Object.prototype 身上的 toString 方法身上的对象属性描述符

Object.getOwnPropertyDescriptor(Object.prototype, 'toString')

image-20230111190012279

这里可以看到 toString 方法的 enumerable 被设置成了 false

接下来我们介绍一个新的 API Object.create() 用于将指定数据的原型链指向为其他数据

const obj_prototype = {
  ikun: 666,
  music: 'music',
  basketball: '篮球'
}

const obj = Object.create(obj_prototype)

console.log(obj.__proto__) // {ikun: 666, music: 'music', basketball: '篮球'}
console.log(obj) // {}

obj 为空对象,而它的原型链上有数据

可以看到已经指向成功了,这时我们通过 for...in 进行枚举 obj,看看结果如何

image-20230111193027217

经过证实,这次信了吧?你要是还不信,就不让你吃我家鸽鸽下的蛋了🙄



二、迭代器 - for…of

1. 什么是迭代器

所有的有序结构(如数组、NodeList)内部都有内置的迭代器

该篇文章不会详讲迭代器,会在另一篇文章进行详讲,这里先说说大致内容


2. 有序结构

字符串

数组

NodeList 等 DOM 集合

Map

Set

arguments

注意:object 对象不属于迭代器,因为它内部的顺序不能够保障有序


3. 每个迭代器都会有一个属性 Symbol.iterator

实验证明有序数据存在 Symbol.iteraotr 属性:

let arr = [100,200,300]

console.log(arr[Symbol.iterator])    // ƒ values() { [native code] }

arr[Symbol.iterator]() // 出现如下内容,原型链身上会有一个叫 next 的方法 

image-20230111194120429

4. 为什么会存在 next 方法

next 方法就是用来帮助遍历的一个方法,每次调用这个 next 方法,就会返回一个对象,让我们调用四次

let arr = [100, 200, 300]

arr[Symbol.iterator]().next()		// {value: 100, done: false}
arr[Symbol.iterator]().next()		// {value: 200, done: false}
arr[Symbol.iterator]().next()		// {value: 300, done: false}
arr[Symbol.iterator]().next()		// {value: undefine, done: true}

arr 数组只存在三个元素,当第四次调用时 next() 返回的对象 done 则为了 true,表示终止没有内容了

现在我们使用 for...of 进行遍历 arr 迭代器(数组)

let arr = [100, 200, 300]

for (let n of arr){
	console.log(n)  	// 100 200 300
}

So?你现在能猜到 for...of 的运行方式了吗?

该篇文章不会详 next,会在另一篇迭代器文章中进行详讲,这里先说说大致内容


5. 结论

当我们通过 for...of 进行遍历迭代器(有序结构)时,它会帮我我们调用内部构造器的 next 方法,每次调用通过返回对象的 done 属性判断是否,遍历完了全部数据


三、for…in

1. 介绍

for...in 语句以任意顺序枚举一个对象的除 Symbol 以外的可枚举属性,包括从原型链上继承的可枚举属性,一般用于枚举对象,但可以枚举数组(可以用但没必要)

回忆一下对象属性描述符上的 enumerable 属性


2. 使用示例

const obj = {
  ikun: 'ikun',
  rap: 'rap',
}

for (let key in obj) {
  console.log(key) // ikun, rap
}

3. 枚举数组(可以但不适合)

使用 for in 去遍历数组,得到的 key 就是数组的索引 index

const arr = [
  { ikun: 'ikun' },
  { music: 'music' }
]
for (let key in arr) {
  console.log(key)    	// 0 1
  console.log(arr[key])  // { ikun: 'ikun' }, { music: 'music' }
}

一般都使用 for of 来进行遍历


4. for…in 如何中断循环

for in 中可以使用 break 或者 continue 去中断循环,不可以直接用 return 去中断循环

const arr = {
  ikun: 'ikun',
	music: 'music',
  rap: 'rap'
}


// 中断循环
for (let key in obj) {
  if (key === 'ikun') {
    break
  }
	console.log(key)		// music, rap
}

但如果你是杠精的话说:for in 可以使用 return 中断循环,那就只能将 for…in 放到函数中了,可以但没必要


5. 枚举带有 Symbol 的对象

const obj = {
  ikun: 'ikun',
  rap: 'rap',
  [Symbol('music')]: '我是时长两年半的个人练习生',
}

for (let key in obj) {
  console.log('obj.' + key + ' = 我是' + obj[key])
}

这里可以看到,不能够进行遍历 Symbol 属性

image-20230111200600110


6. 如何枚举对象身上的 Symbol 属性

使用 Reflect.ownKeys() 获取指定对象身上的,所有属性包含(Symbol)

使用 Object.getOwnPropertySymbols() 获取指定对象身上的,所有 Symbol 属性

使用 Object.getOwnPropertyNames() 获取指定对象身上的属性,不包含(Symbol)属性

image-20230111175812610

四、for…of

1. 介绍

for...of 语句以顺序遍历有序结构的数据,详细情况,去看 [二、迭代器 - for…of](#二、迭代器 - for…of)

回忆一下迭代器上的 next 方法


2. 使用示例

var arr = ['ikun', 'singing', 'jump', 'rap', 'music', 'basketball'];
for(let item of arr){	
    console.log(item);   // 'ikun', 'singing', 'jump', 'rap', 'music', 'basketball'
}

遍历数组里的每一项,并得到每一个元素



总结

for...in 专门用于枚举对象数据

  • 内部原理通过 enumerable 对象属性描述符来判断是否可枚举的属性

for...of 专门用于遍历有序结构数据(数组,nodeList DOM集合等)

  • 内部原理通过内置迭代器的 next 方法返回的对象来判断是否可以遍历

该篇文章知识点从慕课网双越老师的视频《快速掌握前端必会的7种设计模式》处习得,如有侵权,联系删除

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值