Open YvetteLau opened 5 years ago
可迭代对象可以采用统一的接口对对象进行遍历,比如for...of, 而不需要关注对象内部的具体结构。在JS中可迭代的对象需要返回一个包含next方法的对象,next方法再返回一个包含value和done两个属性的对象。value是指每次迭代的值,而done用来表示是否已经迭代完毕。
给一个对象定义一个迭代器,使之可以采用for...of来迭代属性所对应的值.
let obj = {
name: 'age',
age: 20,
sex: 'female',
[Symbol.iterator]() {
var self = this;
var i = 0;
var keys = Object.keys(this);
return {
next() {
if (i < keys.length) {
return {
value: self[keys[i++]],
done: false
}
} else {
return {
value: undefined,
done: true
}
}
}
}
}
}
for (let item of obj) {
console.log(item); // age 20 female
}
数据结构: a:Array b:Object c:Map d:Set 可迭代的对象 => 数据结构只要部署了迭代器的接口 在原生的数据类型中(有Symbol.iterator)
特点: a:获取value与done b:next()方法可以指向下一个指针
迭代器(Iterator):其内部都返回了next()方法,每次调用next方法都返回了一个对象,这个对象有两个属性,分别是value和done,value表示下一个将要返回的值,如果没有更多数据,done值为true。 迭代对象(Iterable):具有迭代器的属性,也就是能够通过一个函数返回一个迭代器。
可迭代对象可通过 for...of
进行循环。
模拟迭代器:
function makeIterator(arr) {
var nextIndex = 0;
return {
next: function () {
return nextIndex < arr.length ? {
done: false,
value: arr[nextIndex++]
} : {
done: true
};
}
}
}
var it = makeIterator(['a', 'b', 1, 6, NaN, null, undefined]);
var res;
do {
res = it.next();
console.info(res.value);
} while (!res.done);
为了实现可迭代,一个对象必须实现 iterator 方法,这意味着这个对象(或其原型链中的一个对象)必须具有带 Symbol.iterator 键的属性
判断当前对象是否为可迭代对象,检测该对象是否具备 Symbol.iterator
属性。
// 以数组为例
Array.prototype.hasOwnProperty(Symbol.iterator); // => true
可通过检测当前对象是否具有Symbol.iterator
属性来判断当前对象是否为可迭代对象。Symbol.iterator
是一个函数,所以通过typeof
来检测返回值,如果返回值为'function'
则为可迭代对象,如果返回值为'undefined'
则为不可迭代对象。
typeof [][Symbol.iterator]; // => 'function'
typeof {}[Symbol.iterator]; // => 'undefined'
typeof new Set()[Symbol.iterator]; // => 'function'
typeof new Map()[Symbol.iterator]; // => 'function'
typeof ''[Symbol.iterator]; // => 'function'
var list = document.querySelectorAll('p'); // 获取dom节点
typeof list[Symbol.iterator]; // => 'function'
typeof new Int8Array()[Symbol.iterator] // => 'function'
(function(){
typeof arguments[Symbol.iterator]; // => 'function'
})();
原生具备 Iterator 接口的数据结构如下。
数组如何使用.next()
方法:
var arr = [1,2,3];
var si = arr[Symbol.iterator]();
si.next(); //=> {value: 1, done: false}
si.next(); //=> {value: 2, done: false}
si.next(); //=> {value: 3, done: false}
si.next(); //=> {value: undefined, done: true}
如何实现一个自定义的可迭代对象:
var myIterator = {};
myIterator[Symbol.iterator] = function* () {
yield 'Bryan';
yield 'programmer';
yield 'bachelordom';
}
for (let val of myIterator) {
console.info(val);
}
// Bryan
// programmer
// bachelordom
总结
可迭代对象的特点:
for...of
进行循环; Symbol.interator
属性; 首先了解遍历器的概念(iterator) 它是一种接口,为各种不同的数据结构提供统一的访问机制。 任何数据结构,只要部署了Iterator接口,就可以完成遍历操作。 Iterator的作用有三个: 一个是为各种数据提供一个统一的,简便的访问接口 二是使得数据结构的成员能够按照某种次序排列 三是ES6创造了一种新的遍历命令,for...of循环,Iterator接口主要供for...of消费
Iterator的遍历过程如下: 1.创建一个指针对象,指向当前数据结构的起始位置。 2.第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员 3.第二次调用指针对象的next方法,指针就指向数据结构的第二个成员 4.不断调用指针对象的next方法,直到它指向数据结构的结束位置 每次调用next方法都会返回数据结构的当前成员信息。具体来说,就是返回一个包含value和done两个属性的对象。其中value是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
var it=makeIterator(['a','b']);
it.next()
it.next()
it.next()
function makeIterator(array) {
var nextIndex=0;
return {
next: function(){
return nextIndex<array.length?
{value:array[nextIndex++],done:false}:
{value:undefined,done:true};
}
}
}
数据结构只要部署了Iterator接口,我们就称这种数据结构为可遍历的或者可迭代的。 ES6规定,默认的Iterator接口部署在数据结构的Symbol.iterator属性,或者说一个数据结构只要具有Symbol.iterator属性,就可以认为是可遍历的(iterable)。 原生具备Iterator接口的数据结构如下:
Array
Map
Set
String
TypeArray
函数的arguments对象
NodeList对象
为一个对象添加Iterator 接口
let obj={
data: ['hello','world'],
[Symbol.iterator](){
const self=this;
let index=0;
return {
next(){
if(index<self.data.length){
return {
value:self.data[index++],
data: false
}
}else{
return {value:undefined,done:true}
}
}
}
}
}
Symbol.iterator方法最简单的实现还是用Generator函数
let obj={
*[Symbol.iterator](){
yield 'hello';
yield 'world';
}
}
for(let x of obj){
console.log(x);
}
引用:ES6标准入门-阮一峰
数组的可迭代方法:
对象的可迭代方法
实现 copy form https://github.com/luohong123
function makeIterator(array){
var nextIndex = 0;
return {
next: function(){
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{done: true};
}
};
}
// 使用 next 方法依次访问对象中的键值
var it = makeIterator(['step', 'by','step']);
console.log(it.next().value); // 'step'
console.log(it.next().value); // 'by'
console.log(it.next().value); // 'step'
console.log(it.next().value); // undefined
console.log(it.next().done); // true
定义: 他是一种接口, 为各种不同的数据结构提供统一的访问机制。 任何数据只要部署interator接口,就可以完成遍历操作。其主要供for...of 消费。(w3c定义)
遍历过程:
function createIterator (items) {
var i = 0
return {
next: function () {
var done = (i >= items.length)
var value = !done ? items[i++]: 'undefined'
return {
done: done,
value: value
}
}
}
}
var iterator = createIterator([1,2, 3])
模拟next返回的例子
原生具备可迭代(遍历的对象)(这边以for...of 区分):
function foo () {
for (var i of arguments) { console.log(i) }
}
foo(1, 2, 3, 4)
function *aGenerator () {
yield 1
yield 2
yield 3
}
var agen = aGenerator()
for (var i of agen) {
console.log(i)
}
Object.prototype[Symbol.iterator] = function* () {
for (const key in this) {
yield [key , this[key]]
}
}
var a = { name: 'Jimmy', age: 18, job: 'actor' } for (var i of a) { console.log(i) }
或者也可采用
Object.prototype[Symbol.iterator] = function () { var i = 0 const names = Object.entries(this) return { next: function () { var done = (i >= names.length) var value = !done ? names[i++]: undefined return { done: done, value: value } } } } var a = { name: 'Jimmy', age: 18, job: 'actor' } for (var i of a) { console.log(i) }
ES6 规定,默认的 Iterator
接口部署在数据结构的 Symbol.iterator
属性,换个角度,也可以认为,一个数据结构只要具有 Symbol.iterator
属性(Symbol.iterator
方法对应的是遍历器生成函数,返回的是一个遍历器对象),那么就可以其认为是可迭代的。
Symbol.iterator
属性,Symbol.iterator()
返回的是一个遍历器对象for ... of
进行循环let arry = [1, 2, 3, 4];
let iter = arry[Symbol.iterator]();
console.log(iter.next()); //{ value: 1, done: false }
console.log(iter.next()); //{ value: 2, done: false }
console.log(iter.next()); //{ value: 3, done: false }
Iterator
接口的数据结构:上面我们说,一个对象只有具有正确的 Symbol.iterator
属性,那么其就是可迭代的,因此,我们可以通过给对象新增 Symbol.iterator
使其可迭代。
let obj = {
name: "Yvette",
age: 18,
job: 'engineer',
*[Symbol.iterator]() {
const self = this;
const keys = Object.keys(self);
for (let index = 0; index < keys.length; index++) {
yield self[keys[index]];//yield表达式仅能使用在 Generator 函数中
}
}
};
for (var key of obj) {
console.log(key); //Yvette 18 engineer
}
可迭代对象的特点就是可迭代。。。
一个具有 @@iterator
方法的对象,当然这个可以通过 Symbol.iterator
去生成
遍历器(Iterator)它是一种借口,为各种不同的数据结构提供统一的访问机制。 任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。 它的作用有三个: 一是为各种数据结构,提供一个统一的、简便的访问接口; 二是使得数据接口的成员能够按照某种次序排列; 三是ES6创造了一种新的遍历命令
for...of
循环,Iterator接口主要提供for...of
消费遍历过程,创建一个指针对象,调用指针的
next
方法,每调用依次next
方法,就会返回value
和done
两个属性对象,value
属性是当前成员的值,done
属性是一个布尔值,表示循环可以结束
var it = makeIterator(['a','b']);
console.log(it.next());
console.log(it.next());
console.log(it.next());
function makeIterator(array){
var nextIndex = 0;
return {
next: function(){
return nextIndex < array.length ?
{ value: array[nextIndex++],done: false }:
{value:undefined,done: true}
}
}
}
具有原生Iterator接口的数据结构
Array
Map
Set
String
TypedArray
函数的 arguments 对象
NodeList 对象
凡是部署了Symbol.iterator
属性,都称之为部署了遍历器接口,返回一个遍历器对象;对于原生部署了Iterator
接口,for...of
会自动去遍历,如果没有的话(比如对象),都需要自己在Symbol.iterator
属性上面部署
总结: 一个数据结构(数组、Map、Set,某些类似数组的对象——比如arguments对象,DOM NodeList对象,Generator对象,字符串等),才可以被for...of
循环遍历。 换句话说就是for...of
循环内部调用的是数据结构Symbol.iterator
方法
[更多请参考参考阮一峰](http://es6.ruanyifeng.com/#docs/iterator)
可迭代对象的特点
代码如下:
参考文章