JavaScript详解类数组与可迭代对象的实现原理
可迭代对象(Iterable object)
数组是一个特殊的对象,它和普通对象的区别不仅仅在于元素的顺序访问、存储。另外一个重要的区别是:数组是可迭代的,也就是可以使用for ... of
语句访问(迭代)所有的元素。
我们可以简单的做一个小实验:
let arr = [1,2,3,4,5]
for(let val of arr){
console.log(val)
}
代码执行结果:
以上代码就简单的使用了数组的迭代特性,我们在访问数组元素的时候,不必使用元素的下标。
如果我们对一个普通对象使用for ... of
语句会发生什么呢?
let obj = {
name:'xiaoming',
age:12,
}
for(let para of obj){ //代码会报错
console.log(para)
}
执行效果如下:
这就证明普通的对象和数组之间还有一个可迭代的差距,我们称具备迭代功能的对象为可迭代对象。
Symbol.iterator
如果我们希望一个对象可以迭代,必须为对象添加一个名为Symbol.iterator
的方法(一个专门使对象可迭代的内建Symbol
)。
方法作用包括:
- 当使用
for ... of
循环迭代对象时,就会调用Symbol.iterator
方法,这个方法必须返回一个迭代器(一个有next()
方法的对象)。 - 得到迭代器后,
for ... of
会不断的调用迭代器的next()
方法获得下一个元素 next()
方法返回的内容必须符合格式:{done:Boolean,value:any}
,当done:true
时,循环结束,否则value
就是下一个值。
迭代器:
迭代器是借鉴C++
等语言的概念,迭代器的原理就像指针一样,它指向数据集合中的某个元素,你可以获取它指向的元素,也可以移动它以获取其它元素。迭代器类似于数组中下标的拓展,各种数据结构,如链表(List
)、集合(Set
)、映射(Map
)都有与之对应的迭代器。
JS
中的迭代器是专门为了遍历这一操作设计的。每次获取到的迭代器总是初始指向第一个元素,并且迭代器只有next()
一种行为,直到获取到数据集的最后一个元素。我们无法灵活移动迭代器的位置,所以,迭代器的任务,是按某种次序遍历数据集中的元素。
可迭代对象
实现一个可迭代对象:
let obj = {
from:1,
to:5,
}
obj[Symbol.iterator] = function(){
//返回一个迭代器
return {
current:this.from,
last:this.to,
next(){
if(this.current<this.last){
return {done:false,value:this.current++}
}else{
return {done:true}//迭代结束
}
}
}
}
for(let para of obj){
console.log(para)
}
代码执行效果:
注意,以上对象虽然可以进行迭代了,但是,迭代使用使用的材料并非对象,而是Symbol.iterator
返回的迭代器(也是一个对象)。
把对象本身构造成迭代器
以上代码构造了一个内建函数Symbol.iterator()
,这个函数返回了一个迭代器对象。我们还可以采用另外一种实现迭代器的方式:把对象本身做成迭代器:
let obj = {
from:1,
to:5,
[Symbol.iterator](){
this.current = this.from;
return this;//返回对象本身
},
next(){//给对象添加一个next方法
if(this.current<this.to){
return {done:false,value:this.current++}
}else{
return {done:true}
}
}
}
for(let para of obj){
console.log(para)
}
代码执行效果和上面的图片展示相同。
这么做虽然代码更加简洁了,但是由于并没有新的可迭代对象产生,我们就没有办法同时执行两个for ... of
循环迭代同一个对象了,但是两个并行的迭代在同一个对象上是非常罕见的。
我们可以总结可迭代对象的概念:
所谓可迭代对象,就是比普通对象多了一个名为Symbol.iterator
方法的普通对象,这个方法返回一个迭代器。
或者,一个具备Symbol.iterator
同时具备next
方法的对象也是一个可迭代的对象。
String也是可迭代的
数组和字符串都是可以迭代的,我们可以很方便的使用for...of
语句迭代数组中的字符元素:
let str = '123'
for(let c of str){
console.log(c)
}
这对于代理对(UTF-16
扩展字符)同样是有效的:
let str = '??????❤????'
for(let c of str){
console.log(c)
}
执行效果如下:
String的迭代器
并非只有for...of
语句能够使用迭代器,我们还可以显式的调用迭代器:
let str = '12345'
let itr = str[Symbol.iterator]()
while(true){
let result = itr.next()
if(result.done)break;
console.log(result.value)
}
代码执行效果:
以上代码执行了遍历字符串字符的操作,是不是觉得可迭代对象就没有这么神秘了!
类数组对象和可迭代对象
类数组和可迭代在遍历功能上非常相似,都可以方便的方式内部元素,但是二者仍然有明显的区别:
iterable
可迭代对象:实现了Symbol.iterator
的对象;array-like
类数组对象:具有数字索引,并且有length
属性;
字符串就是一个即使类数组又是可迭代的对象。
可迭代和类数组对象通常都不是数组,如果我们想把一个可迭代或者类数组对象转为数组,需要使用Array.from
方法。
Array.from
使用Array.from
将字符串转为数组:
let str = '123'
let arr = Array.from(str)
console.log(arr)
代码执行效果如下:
把自定义的类数组对象转为数组:
let obj = {
0:'0',
1:'1',
2:'2',
length:3
}
let arr = Array.from(obj)
console.log(arr)
代码执行结果:
Array.from
的完整语法:
Array.from(obj[, mapFunc, thisArg])
mapFunc
方法会在生成数组之前对每个可迭代或类数组元素调用,如果mapFunc
是一个成员方法,可以使用thisArg
提供this
指针。
举个例子:
let str = '12345'
let arr = Array.from(str,itm=>+itm)
console.log(arr)
代码执行结果:
这里通过映射函数,将本应该生成字符数组转为数字数组。
总结
- 可以使用
for...of
语法的对象被称为可迭代对象 - 可迭代对象是在普通对象的基础上实现了
Symbol.iterator
方法的对象 Symbol.iterator
方法返回了一个迭代器;- 迭代器包含一个
next
方法,该方法返回下一个元素的值; next
方法返回值需要满足格式{done:Boolean,value:nextVal}
,当done:true
时,迭代结束Array.from
可以把类数组和可迭代对象转为数组;
到此这篇关于JavaScript详解类数组与可迭代对象的实现原理的文章就介绍到这了,更多相关JavaScript 类数组与可迭代对象内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341