转载请标明出处
文章目录
前言
今天在学习Vue中列表渲染时,遇见了一个有趣的描述,
你也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法
然后我就使用of
替代了in
,接着观察到Vue列表的显示没有任何变化,于是得到结论:Vue中of
与in
是没有区别的,只是of
更符合迭代器的语法习惯。
可是我隐约的记得for...of
与for...in
是存在区别的,于是秉承着将问题探究到底的原则,开始了对for...of
与for...in
区别的研究。下面就是学习过程的记录,其中带着自己的思考和实践,希望能够帮助到大家。
for…of 与 for…in
想必大家都清楚,for...of
是对value
的遍历而for...in
是对key
的遍历。在此附上在Chrome Console
中对该结论的实践结果
通过上述结果,更加确定了在使用数组做for...in
遍历时返回的是index
,而使用for...of
时返回的是value
。
紧接着,我又使用了对象来做遍历:
在使用对象做for...of
遍历时报了一个意料之外的错误,并且提示items is not iterable
,由于此处主要是比较for...in
和for...of
的区别,所以对于iterable
的解释将在后续章节继续。
现在我得到一个结论:
for...of
不能做关于对象的遍历,如果需要做对象的遍历则使用for...in
来实现
紧接着我在Object
的原型(Object.Prototype
)上声明了一个新的叫做clone
的方法,然后使用for...in
尝试再次遍历:
通过上述截图,可以清楚的看到刚才声明的clone
方法被遍历出来了,为了探明为什么会出现这中情况我使用Object.getOwnPropertyDescriptor()来查看clone
与其他属性的区别:
区别显而易见 - clone
的enumerable
为false
而toString
的enumerable
为true
, 接下来就介绍Enumerable
究竟是什么,以及为什么能够影响到for...in
循环的遍历结果
Enumerable
enumerable
是对象属性特征。
除了[[enumerable]]
,JavaScript中还存在[[Writable]]
,[[Configurable]]
,[[Value]]
,[[Set]]
,[[Get]]
这些特征。
我们可以使用Object.defineProperties()或者Object.defineProperty()来定义对象属性特征。
在JavaScript中一个对象的属性分为两种,一种是数据属性另外一种是访问器属性,具体细节如下
数据(数据描述符)属性
数据属性有4个描述内部属性的特性
[[Configurable]]
表示能否通过delete
删除此属性,能否修改属性的特性,或能否修改把属性修改为访问器属性,如果直接使用字面量定义对象,默认值为true
[[Enumerable]]
表示该属性是否可枚举,即是否通过for...in
循环或Object.keys()
返回属性,如果直接使用字面量定义对象,默认值true
[[Writable]]
能否修改属性的值,如果直接使用字面量定义对象,默认值为true
[[Value]]
该属性对应的值,默认为undefined
访问器(存取描述符)属性
访问器属性也有4个描述内部属性的特性
[[Configurable]]
和数据属性的[[Configurable]]
一样,表示能否通过delete
删除此属性,能否修改属性的特性,或能否修改把属性修改为访问器属性,如果直接使用字面量定义对象,默认值为true
[[Enumerable]]
和数据属性的[[Enumerable]]
一样,表示该属性是否可枚举,即是否通过for...in
循环或Object.keys()
返回属性,如果直接使用字面量定义对象,默认值为true
[[Get]]
一个给属性提供 getter
的方法(访问对象属性时调用的函数,返回值就是当前属性的值),如果没有 getter
则为 undefined
。该方法返回值被用作属性值。默认为 undefined
[[Set]]
一个给属性提供 setter
的方法(给对象属性设置值时调用的函数),如果没有 setter
则为 undefined
。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为 undefined
此处关于数据属性和访问器属性的描述摘自JavaScript中的Object.defineProperty()和defineProperties(), 如果希望进一步了解对象属性特征的读者可以阅读上述文章。
有了理论的支撑,我接着尝试着使用Object.defineProperty()
来改变clone方法的特征,然后尝试for...in
遍历:
可以看到随着enumerable
变为false,clone方法无法再通过for...in
遍历,并得到结论:
for...in
遍历对象属性的成败,直接受到enumerable
可枚举特征的影响
Iterator
在前面的for...of
遍历对象的试验中,我们得到了items is not iterable
这个错误。那么iterator
是什么?对for...of
遍历有何影响,将在下文中讲解
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。
任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用有三个:
一是为各种数据结构,提供一个统一的、简便的访问接口;
二是使得数据结构的成员能够按某种次序排列;
三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。
上述段落摘自ECMAScript 6 入门(阮一峰),通过上面的描述可知for...of
无法遍历并且报错的原因就是新创建的对象上缺少了iterator
接口
对于iterator
接口主要通过一个next
方法遍历对象,每次调用next
方法就遍历对象中的下一个成员,直到所有的成员都遍历完成,然后返回结束标识符。
iterator的模拟可以参考如下代码:
var it = makeIterator(['a', 'b']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
};
}
返回的是包含value
和done
两个属性的对象,其中value
是当前遍历的成员值,而done
则是整个遍历的标识符,当遍历完成后done
就会被设置成true
,整个for...of
遍历就是通过判断done
属性是否为true
来进行的,如果为true
则结束遍历,否则再调用一次next
方法。
再JavaScript数据结构中,原生具备Iterator
的如下:
- Array
- Map
- Set
- String
- TypedArray
- 函数的arguments对象
- NodeList对象
上述数据结构可以直径使用for...of
而不需要做任何的额外工作。
如果希望让其他的数据结构也支持for...of
,那么就需要定义Symbol.iterator
接口,因为ES6 规定,默认的Iterator
接口部署在数据结构的Symbol.iterator
属性上,或者说,一个数据结构只要具有Symbol.iterator
属性,就可以认为是“可遍历的”(iterable
)。
我添加了Symbol.iterator
之后再使用for...of
进行遍历:
/****** Symbol.iterator代码 ******/
items[Symbol.iterator] = function(){
var nextIndex = 0;
var current = this;
var indexArr = Object.keys(this);
return{
next:function(){
if(nextIndex < indexArr.length){
return {
value:current[indexArr[nextIndex++]],
done:false
}
}
else{
return {
value:undefined,
done:true
}
}
}
}
}
从我的测试结果截图可以看出Symbol.iterator
确实直接影响了for...of
能否正常运行。
除了for...of
之外,JavaScript以下场景依旧是使用iterator
来进行的遍历:
- 解构赋值
- 扩展运算符
- yield*
- Array.from()
- Map(), Set(), WeakMap(), WeakSet()(比如new Map([[‘a’,1],[‘b’,2]]))
- Promise.all()
- Promise.race()
具体细节可以参考ECMAScript 6 入门(阮一峰)
结束
本片文章旨在从更深层次理解for...in
和for...of
的区别,希望能够给大家提供帮助。
如有错误还望斧正