Iterator遍历器
Iterator遍历器的作用:
- (1) 为不同的数据结构,提供统一的访问接口
- (2) 使得数据接口的成员,能够按某种次序排列
- (3) 只要部署Iterator接口的数据结构,都能被
for....of
遍历
Iterator接口的目的
- iterator接口的目的就是为不同的数据结构,提供统一访问机制,
for...of 循环
- 当使用
for...of循环
遍历某种数据结构时,该循环会自动的去寻找 iterator 接口 - 一种数据结构,只要部署了 iterator 接口,我们就认为这种数据结构是可遍历的
- iterable:是可遍历的意思
- 有了遍历器接口,数据结构就可以用for...of循环遍历(详见下文),也可以使用while循环遍历。
iterator: 遍历器
iterable: 可遍历的
遍历器生成函数
- 遍历器生成函数 的作用时返回一个 遍历器对象
- 遍历器对象 的本质就是一个指针对象
- 遍历器对象的特点是 里面有 next的方法,执行next方法,会返回一个对象,该对象包含数据结构的当前成员信息 value 和 done,并且将指针指向数据结构的下一个成员
- value是当前成员的值
- done是一个布尔值,表示遍历是否结束
- 总之,调用指针对象
(遍历器对象)
的next方法,就可以遍历事先给定的数据结构。
遍历器对象
- 遍历器对象的本质就是指针对象
- 遍历器对象 的本质就是一个指针对象
- 遍历器对象的特点是 里面有 next的方法,执行next方法,会返回一个对象,该对象包含数据结构的当前成员信息 value 和 done,并且将指针指向数据结构的下一个成员
- value是当前成员的值
- done是一个布尔值,表示遍历是否结束
- 总之,调用指针对象
(遍历器对象)
的next方法,就可以遍历事先给定的数据结构。
模拟 - 遍历器生成函数
componentDidMount() {
function makeIterator(arr) { // 遍历器生成函数,返回一个遍历器对象 ( 即返回一个指针对象 )
let indicator = 0;
return {
next: function() {
// 遍历器对象的 next 方法,用来移动指针,返回数据结构相关信息的 对象
return indicator < arr.length
?
{ 'value': arr[indicator++], done: false}
:
{'value': undefined, done: true }
}
}
}
let it = makeIterator([1,2]) // 执行遍历器生成函数,返回遍历器对象,即指针对象 it
console.log( it.next() ); // next方法,用来移动指针,并返回数据结构信息对象
console.log( it.next() );
console.log( it.next() );
}
// Object {value: 1, done: false}
// Object {value: 2, done: false}
// Object {value: undefined, done: true}
----------------------------------------------------------
对于遍历器对象来说, done: false 和 value: undefined 属性都是可以省略的,
因此上面的makeIterator函数可以简写成下面的形式。
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++]} :
{done: true};
}
};
}
默认 Iterator 接口
iterator接口的目的,就是为不同的数据结构,提供统一的访问机制,for ... of 循环,当使用 for...of循环 遍历某种数据结构时,该循环会自动的去寻找 iterator 接口,一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”(iterable)。
- ES6规定,默认的 iterator 接口部署在数据结构的
Symbol.iterator属性
上 - 一个数据结构只要具有
Symbol.iterator属性
就认为是可遍历的( iterable ) - Symbol.iterator属性 ------- 本身是一个函数,就是当前数据结构默认的遍历器生成函数
- Symbol.iterator属性 是 遍历器生成函数
- 执行遍历器生成函数
Symbol.iterator()
,返回一个遍历器 -
注意注意:
Symbol.iterator本身返回的是Symbol对象的Iterator属性,是一个预设好的Symbol值,作为对象的属性时需要放在[]
括号内
const obj = { // obj对象
[Symbol.iterator] : function () { // Symbol.iterator是一个Symbol类型的值,作为对象属性要加 []
return { // 返回一个遍历器对象
next: function () { // 遍历器对象的next()方法,返回带有数据机构信息的对象,将指针指向下一个成员
return {
value: 1, // 本例中并没有传入数据结构,而是遍历器对象本身的结构
done: true
};
}
};
}
};
因为 obj对象 署了 Symbol.iterator 属性,该属性是一个方法,即遍历器生成函数,所以 obj 是可遍历的
原生具有 Iterator 接口的数据结构
原生具有iterator接口的数据结构有:数组,字符串,map,set,类数组对象
原生具有Iterator接口的数据结构,可以直接使用 for...of 循环去遍历
凡是部署了Symbol.iterator属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。
- array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList 对象
注意!!!!!!!!!!
(1) 凡是具有 ( Iterator ) 接口的 数据结构 都可以被 ( for...of ) 遍历
(2) 具有 ( iterator ) 接口的数据结构 可以调用 Symbol.iterator属性 ,是一个方法,返回 遍历器对象
重要!
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator](); // 调用数组的 Symbol.iterator 属性方法,返回遍历器对象
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true
- 对于原生部署了 iterator 接口的数据结构,不用自己编写遍历器生成函数,for...of循环会自动遍历他们
- 对于原生没有部署iterator接口的数据结构( 主要是对象 ),都需要自己在Symbol.iterator属性上面部署,这样才会被for...of循环遍历
对象添加 Iterator 接口
如果对象想要具有 for...of 循环 调用的 iterator 接口,就必须在 Symbol.iterator 属性上 部署 遍历器生成方法(原型链上具有该方法也可以)
class RangeIterator {
constructor(start, stop) { // 构造函数,传入两个参数
this.value = start; // this代表实例对象
this.stop = stop;
}
[Symbol.iterator]() { return this; } // 在Symbol.iterator属性上部署了一个方法,返回实例对象
next() { // 类的next() 方法
var value = this.value;
if (value < this.stop) {
this.value++;
return {done: false, value: value};
}
return {done: true, value: undefined};
}
}
function range(start, stop) { // 定义一个外层函数,返回 构造函数的 实例对象
return new RangeIterator(start, stop);
}
for (var value of range(0, 3)) { // for...of循环,部署了 iteratro接口的 实例对象
console.log(value); // 0, 1, 2
}
为对象添加 iterator 接口
componentDidMount() {
let obj = {
data: ['hello', 'word'],
[Symbol.iterator]: function() { // 在对象的Symbol.iterator属性上部署遍历器生成函数
const self = this; // 绑定里层的this为外层的this,this指向函数运行时所在的对象
let index = 0;
return {
next(){ // 是简写形式,原来是next: function() {......}
if(index < self.data.length) {
return {
value: self.data[index++],
done: false
}
} else {
return {value: undefined, done: true }
}
}
}
}
}
let xxx = obj[Symbol.iterator](); // 注意,这里先执行函数,执行时this执行obj,在赋值
console.log( xxx.next() ); // Object {value: "hello", done: false}
}
类似数组对象添加 Iterator
类似数组的对象,部署 Iterator 接口,简便方法是:Symbol.iterator属性方法 直接引用 数组的 Iterator 接口
- 什么是类似数组对象?
对象的键名是数字,并且具有 length 属性 - 注意,普通对象部署数组的Symbol.iterator方法,并无效果。
componentDidMount() {
let arrObj = {
'0': 'name',
'1': 'age',
'length': 2,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
// 类数组对象部署iterator接口的简便方法:
// Symbol.iterator方法 直接引用数组的 iterator 接口
}
let xx = arrObj[Symbol.iterator](); // 调用方法
console.log( xx.next() , 'xx.next()') // Object {value: "name", done: false}
console.log( xx.next() , 'xx.next()') // Object {value: "age", done: false}
console.log( xx.next() , 'xx.next()') // Object {value: undefined, done: true}
for(let i of arrObj) {
console.log(i,'i') // name // age
}
}
- 如果Symbol.iterator方法对应的不是遍历器生成函数(即会返回一个遍历器对象),解释引擎将会报错。
var obj = {};
obj[Symbol.iterator] = () => 1;
[...obj] // TypeError: [] is not a function
上面代码中,变量obj的Symbol.iterator方法对应的不是遍历器生成函数,因此报错。
---------------------------------------------------------
componentDidMount() {
let obj = {
'0': 'name',
'1': 'age',
'length':2
};
obj[Symbol.iterator] = Array.prototype[Symbol.iterator];
for(let i of obj) {
console.log(i,'i')
}
// name i
// age i
}
字符串的 iterator 接口
字符串是类似数组对象,也原生具有 Iterator 接口
var someString = "hi";
typeof someString[Symbol.iterator]
// "function"
var iterator = someString[Symbol.iterator]();
iterator.next() // { value: "h", done: false }
iterator.next() // { value: "i", done: false }
iterator.next() // { value: undefined, done: true }
iterator接口 和 generator函数
let myIterable = {
[Symbol.iterator]: function* () { // generator函数,关键词 * 和 yield ,返回的是遍历器对象
yield 1;
yield 2;
yield 3;
}
}
[...myIterable] // [1, 2, 3]
--------------------------------------------------------
// 或者采用下面的简洁写法
let obj = {
* [Symbol.iterator]() {
yield 'hello';
yield 'world';
}
};
for (let x of obj) {
console.log(x);
}
// "hello"
// "world"
遍历器对象的return() 和 throw()
遍历器对象除了具有 next()
方法,还可以具有 return()
和 throw()
方法
如果你自己写遍历器对象生成函数,那么next方法是必须部署的,return方法和throw方法是否部署是可选的。
return() 方法的使用场合
- 如果 for...of 循环提前退出 ( 通常是因为出错 或者 有break语句,continue语句 ),就会调用 return() 方法
- 如果一个对象在完成遍历前,需要清理或者释放资源,就可以部署return 方法
throw() 方法的使用场合
- throw方法主要是配合 Generator 函数使用,一般的遍历器对象用不到这个方法。
function readLinesSync(file) {
return { // 返回一个对象
[Symbol.iterator]() { // Symbol.iterator属性:是一个遍历器生成函数
return { // 遍历器生成函数返回的是一个遍历器对象,里面有next,return等方法广发
next() {
return { done: false };
},
return() {
file.close();
return { done: true };
}
};
},
};
}
上面代码中,
函数readLinesSync接受一个文件对象作为参数,返回一个遍历器对象,其中除了next方法,还部署了return方法。
下面的三种情况,都会触发执行return方法。
// 情况一
for (let line of readLinesSync(fileName)) {
console.log(line);
break; // for..of循环中有breack语句,循环提前退出
}
// 情况二
for (let line of readLinesSync(fileName)) {
console.log(line);
continue; // for..of循环中有continue语句
}
// 情况三
for (let line of readLinesSync(fileName)) {
console.log(line);
throw new Error(); // 出错
}
上面代码中,
情况一输出文件的第一行以后,就会执行return方法,关闭这个文件;
情况二输出所有行以后,执行return方法,关闭该文件;
情况三会在执行return方法关闭文件之后,再抛出错误。
// 注意,return方法必须返回一个对象,这是 Generator 规格决定的。
// throw方法主要是配合 Generator 函数使用,一般的遍历器对象用不到这个方法。
for...of循环内部调用的是数据结构的Symbol.iterator方法。
- for...of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、 Generator 对象,以及字符串。
数组
for...of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。
重要!!!
let arr = [3, 5, 7];
arr.foo = 'hello';
for (let i in arr) {
console.log(i); // "0", "1", "2", "foo"
}
for (let i of arr) { // for...of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性!!!!
console.log(i); // "3", "5", "7"
}
- for...of循环可以代替数组实例的forEach方法。
const arr = ['red', 'green', 'blue'];
for(let v of arr) {
console.log(v); // red green blue
}
const obj = {};
obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr); // 对象绑定数组的iterator接口,( 重要!!)
for(let v of obj) {
console.log(v); // red green blue
}
- for...of 和 for..in 的区别
在数组中
for...in循环读取键名。
for...of循环读取键值。
var arr = ['a', 'b', 'c', 'd'];
for (let a in arr) {
console.log(a); // 0 1 2 3
}
for (let a of arr) {
console.log(a); // a b c d
}
// 在数组中,for...in循环读取键名,for...of循环读取键值
- for...of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。这一点跟for...in循环也不一样。
let arr = [3, 5, 7];
arr.foo = 'hello';
for (let i in arr) {
console.log(i); // "0", "1", "2", "foo"
}
for (let i of arr) {
console.log(i); // "3", "5", "7"
}
2018/4/22
this指向注意点
// this是函数运行时,所在的对象!!!!!!!!
var A = {
name: '张三',
describe: function () {
return '姓名:'+ this.name;
}
};
var B = {
name: '李四'
};
B.describe = A.describe;
B.describe() // describe函数运行时,所在的对象是B, this指向B
// "姓名:李四"
---------------------------------------------------------------------------------
var A = {
name: '张三',
describe: function () {
return '姓名:'+ this.name;
}
};
var B = {
name: '李四'
};
B.describe = A.describe(); // 先运行,在赋值,describe()函数运行时所在对象是A,this指向A
console.log( B.describe ); // 注意,此时 B.describe是一个对象,而不是函数
// "姓名:李四"