迭代器与生成器

迭代器与生成器

Symbol

Symbol是什么

ECMAScript 6新增了第六种原始类型Symbol(符号)类型。Symbol类型是唯一的并且是不可修改的,并且也可以用来作为Object 的 key 值。

Symbol类型的变量是通过调用Symbol()函数生成的:

Symbol([description])
  • description:可选的字符串。Symbol的描述,可用于调试但不能访问Symbol本身。

值得注意的是,由于Symbol类型是原始类型,是不能通过new Symbo()来创建对象的。

const symbol1 = Symbol();
const symbol2 = Symbol(42);
const symbol3 = Symbol('foo');

console.log(tpeof symbol1);// "symbol"
console.log(symbol3.toString());// "symbol(foo)"
console.log(Symbol('foo') === Symbol('foo'));// false

Symbol的注意事项

当使用Symbol值进行类型转换时需要注意一些事情:

  • 尝试将一个symbol值转换为一个number值时,会抛出一个工ypeEcor错误。
  • Object(symbol) == symbol表达式的结果返回true。
  • 阻止从一个symbol值隐式地创建一个新的string类型的属性名。

Symbol的方法

  • Symbol.for()方法

    该方法会根据给定的键key,来从运行时的symbol注册表中找到对应的symbol。如果找到了,则返回它。否则,新建一个与该键关联的symbol,并放入全局symbol注册表中。

    Symbol.for(key);
    
    • key:一个字符串,作为symbol注册表中与某 symbol关联的键。

      Symbol.for("foo");//创建一个symbol并放入symbol注册表中,键为"foo"
      Symbol.for("foo");//从symbol注册表中读取键为"foo"的 symbol
      

      和Symbol)不同的是,用Symbol.for()方法创建的的symbol会被放入一个全局symbol注册表中。

  • Symbol.keyEor()方法

    该方法用于获取 symbol注册表中与某个symbol关联的键。

    Symbol.kevor(sym);
    
    • sym:存储在symbol注册表中的某个symbol
    // 创建一个symbol并放入Symbol注册表,key为"foo"
    var. globalSym = Symbol.for("foo");
    Symbol.kevFor(globalSym); // "foo"
    
    // 创建一个symbol,但不放入symbol注册表中
    var localSym = Symbol();
    Symbol.kexEor(localSym); // undefined,所以是找不到 key 的
    

Symbol与for…in

Symbols在for …in 迭代中不可枚举。另外,Object.getOwnPropertyNames()不会返回symbol对象的属性,但是可以使用Object.getQwnPropertySymbols()得到它们。

var obi= {};

obij[Symbol("a")] = "a";obi[Symbol.for("b")] = "b";obj["c"] = "c";
obj.d = "d";
l
for (var i in obi){
console.log(i); // logs "c" and "d"

迭代器

迭代器是什么

ECMAScript 5规范表示集合的数据结构有数组(Array)和对象(Object ),ECMAScript 6规范又新增了Set和Map 两种集合。这样在 JavaScript 中就具有四种集合,需要一种统一的机制进行操作。

迭代器( lterator)就是这样的一种机制,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署lterator接口,就可以完成遍历操作。

迭代器( lterator )具有三种作用:

  • 为各种数据结构,提供一个统一的、简便的访问接。
  • 使得数据结构的成员能够按某种次序排列。
  • ECMAScript 6新增了for…of循环语句,用于遍历迭代器。

Iterator接口

在JavaScript 中迭代器(lterator )是一个对象,该对象提供 next()方法用于返回序列中的下一项。该方法返回包含done和 value 两个属性的对象。

/*
    构建迭代器 -> 返回迭代器对象
    * 该迭代器对象必须具备 next() 方法
      * 作用 - 用于返回序列中的下一项
      * 返回值 - 是一个对象
        * done属性 - 表示是否迭代完毕
          * false - 表示当前没有迭代完毕
          * true - 表示当前迭代完毕
        * value属性 - 表示当前迭代的值
*/
function fn(array) {
  var index = 0;
  return {
    next: function () {
      return index < array.length
        ? {
            done: false,
            value: array[index++],
          }
        : { done: true };
    },
  };
}

迭代器对象一旦被创建,就可以反复调用next()方法用于依次访问对象中的键值。

let arr = ["tom", "king", "lucy", "mary"];

let iterator = fn(arr);
console.log(iterator.next()); // { done: false, value: 'tom' }
console.log(iterator.next()); // { done: false, value: 'king' }
console.log(iterator.next()); // { done: false, value: 'lucy' }
console.log(iterator.next()); // { done: false, value: 'mary' }
console.log(iterator.next()); // { done: true }

一种数据结构只要部署了lterator接口,就可以称这种数据结构是“可遍历的”。ECMAScript 6规范规定默认的lterator接口部署在数据结构的 Symboliterator属性。换句话讲,一个数据结构只要具有 Symbol.iterator属性,就可以认为是“可遍历的”。
Symbol.iterator属性本身是一个函数,就是当前数据结构默认的迭代器生成函数。执行这个函数,就会返回一个迭代器。

const obj= {
	[Symbpl.iterator]: function (){
		return {
			next: function() {
				return { value: 1, done: true };
            }
        };
    }
};

JavaScript 中原生具有lterator接口的数据结构如下:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的arguments 对象
  • NodeList 对象

迭代协议

ECMAScript 6新增了几种协议,这些协议可以被任何遵循某些约定的对象来实现。

  • 可迭代协议

    可迭代协议允许JavaScript 对象去定义或定制它们的迭代行为。为了变成可迭代对象,对象必须实现 @@iterator方法。其实就是这个对象(或者它原型链上的某个对象)必须有一个名字
    是Symboliteratoc的属性。

    属性名称描述
    [Symbol.iterator]返回一个对象的无参函数,被返回对象符合迭代器协议

    当一个对象需要被迭代的时候,它的@@iterator方法被调用并且无参数,然后返回一个用于在迭代中获得值的迭代器。

  • 迭代器协议
    该迭代器协议定义了一种标准的方式来产生一个有限或无限序列的值。
    当一个对象被认为是一个迭代器时,它实现了一个next()的方法并且拥有以下含义:

    属性名称描述
    next返回一个对象的无参函数,被返回对象拥有两个属性:
    - done(boolean)
    - 如果迭代器已经经过了被迭代序列时为true。这时 value 可能描述了该迭代器的返回值。
    - 如果迭代器可以产生序列中的下一个值,则为false。这等效于连同done属性也不指定。
    - value:迭代器返回的任何JavaScript值。done 为true时可省略。

for…of 语句的用法

ECMAScript 6引入了 for …of语句用于遍历迭代器。一个数据结构只要部署了Symboliterator属性,就被视为具有Iterator 接口,就可以用for…of循环遍历它的成员。

for (variable of iterable) {
	//statements
}
  • variable:在每次迭代中,将不同属性的值分配给变量。

  • iterable:被迭代枚举其属性的对象。

let iterable = [10, 20, 30];
for (let value of iterable){
	console.log(value);
}

ECMAScript 6中的数组(Array )、Set和Map都具有以下方法,该方法返回一个迭代器对象。

  • entries()方法:返回一个新的迭代器对象,这个对象的元素是用来遍历[键名,键值]组成的数组。
  • keys()方法:返回一个新的迭代器对象,用来遍历所有的键名。
  • values()方法:返回一个新的迭代器对象,用来遍历所有的键值。
let arr = ['a', 'b', 'c'];
for (let pair of arr.entries()){
	console.log(pair);
}
//[0, 'a']
//[1, 'b']
//[2, 'c']

与forEach() 方法的区别

ECMAScript 6中的数组(Array ) 、Set和 Map都具有forEach()方法用于遍历,该方法与for…of语句的区别如下:

  • farEach()方法无法跳出循环。换句话讲,break 语句和continue 语句无效。
  • for…of语句不仅可以使用break语句和continue 语句,还可以配合使用return语句。
let arr = ['a', 'b', 'c'];

arr.forEach(function(element, index, array){
	if(index > 0){
		break;// SyntaxError: Illegal break statement}
    }
});
for(const index of arr.kevs()) {
	if (index > 0){
		break;
    }
}

与for…in 语句的区别

for …in语句主要用于遍历对象。与for…of语句对比,for…in语句具有以下几个缺点:

  • for. …i不仅遍历自身,还会遍历手动添加的,甚至包括原型链的。
  • 如果用于遍历数组的话,遍历得到的键名为字符串类型的数字值。
Obiect.prototype.obiCustom = function(){};
Array.prototype.arrCustom = function() {};

let iterable = [3,5,7];
iterable.foo = 'hello';

for (let i in iterable){
	console.log(i); // logs 0,1, 2, "foo", "arrCustom", "obiCustom"
}
for(let i of iterable){
	console.log(i); // logs 3,5,7
}

生成器

Generator函数是什么

虽然可以自定义一个迭代器,但自定义的迭代器需要显式地维护其内部状态。而生成器提供了另一个强大的选择,其提供了允许定义一个包含自有迭代算法的函数,同时可以自动维护其内部状态。

Generator函数可以作为生成一个迭代器的特殊函数,该函数被调用时返回一个Generator对象,该对象是符合可迭代协议和迭代器协议的。

function* gen(){
	yield 'wolongxueyuan';
let g = gen();// "Generator {}"

Generator函数与普通函数的区别在于:

  • function*这种声明方式会定义一个生成器函数,它返回一个 Generator对象。
  • yield关键字用来暂停和恢复一个生成器函数。
Generator函数与解构赋值

yield表达式

yield关键字用来暂停和恢复一个生成器函数。

[rv] = yield [expression];
  • expression:定义通过迭代器协议从生成器函数返回的值。如果省略,则返回undefined.
  • rv:返回传递给生成器的next()方法的可选值,以恢复其执行。
function* countAppleSales() {
	var saleList = [3,7,5];
	for (var i = o; i< saleList.length; i++) {
		yield saleList[i];
    }
}
var appleStore = countAppleSales(); // Generator {}
console.log(appleStore.next()); // { value: 3, done: false }
console.log(appleStore.next()); // { value: 7, done: false }
console.log(appleStore.next()); // { value: 5, done: false }
console.log(appleStore.next()); // { value: undefined, done: true }

yield* 表达式

yield*表达式用于委托给另一个Generator 或可迭代对象。

yield*[[expression]];

expression:返回一个可迭代对象的表达式。

function* g1() { yield 2; yield 3; }
function* g2() {
	yield 1;
    yield* g1();
    yield 4;
}

var iterator = g2();
console.log(iterator.next());// { value: 1, done: false }
console.log(iterator.next());// {'value: 2, done: false }
console.log(iterator.next());// { value: 3, done: false }
console.log(iterator.next());// { value: 4, done: false }
console.log(iterator.next());// { value: undefined, done: true }

Generator对象的方法

生成的 Generator 对象具有以下几个原型方法:

方法名称描述
next()方法返回一个包含属性done和value的对象。该方法也可以通过接受一个参数用以向生成器传值
return()方法返回给定的值并结束生成器
throw()方法用于向生成器抛出异常,并恢复生成器的执行,返回带有done及 value两个属性的对象

next(、throw()、return()这三个方法本质上是同一件事,可以放在一起理解。它们的作用都是让Generator 函数恢复执行,并且使用不同的语句替换yield表达式。

function* gen(){
	yield 'wolongxueyuan';
}
var.g = gen();

console.log(g.next());// { value: 'wolongxueyuan', done: false }
console.log(g.return("卧龙学苑")); // { value: '卧龙学苑', done: true }
g.throw(new Error("出错啦...")); // Error:出错啦...
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码小余の博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值