Iterator

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是一个对象,而不是函数
// "姓名:李四"

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容