在ES6中引入了很多新的语法,三个点...的语法就是其中之一。该语法有两个用处,一个是作为展开运算符,还可以作为rest参数。其作为剩余参数的方法用法比较简单,也不是本文的关注内容,所以下边说一下关于展开运算符的一些操作以及引出迭代器与生成器。
基础用法
展开运算符正如其名,就是可以将目标展开,常用于展开数组。
1 2
| let arr = [1,2,3,4,5] console.log(...arr)
|
不过我使用展开运算符最多的是用来合并两个数组
1 2 3
| let arr1 = [1,2] let arr2 = [...arr1, 3] console.log(arr2)
|
当我们传递参数的时候,有时候也能用到它
1 2 3 4 5
| function sum(a, b, c){ return a + b + c } let arr = [1, 2, 3] let res = sum(...arr)
|
展开运算符就只能用于展开数组吗?当然不是,在对象中也是可用的
1 2 3 4 5 6 7 8 9
| let obj1 = { name: 'tom', age: 19 } let obj2 = { ...obj1, gender: 'male' } console.log(obj2)
|
自然,他也可以用来拷贝一个对象,不过要注意这并不是一个深拷贝
1 2
| let obj = {name: 'tom'} let objCopy = {...obj}
|
既然能合并数组,自然也可以合并对象了。
1 2 3
| let obj1 = {name: 'tom'} let obj2 = {age: 19, name: 'tony'} let obj = {...obj1, ...obj2}
|
发现了吗?好像跟数组的合并不太一样了,有重复的属性怎么办呢
我希望你可以自己去试一下然后得出这个答案,不过在这里我就直说了,合并有重复的属性时会以右边的对象属性为准哦。
所以obj = {age: 19, name: 'tony'}
不过这里的坑不止这一个,我们合并对象跟拷贝对象是一样的,合并操作执行的也是一个浅拷贝,如果源对象中还有引用数据类型,使用过程中要注意。
展开运算符可以操作数组和对象,但不仅限于此。
我们将字符串展开为数组会怎么做呢?第一时间肯定想到了split()方法吧,其实展开运算符也可以实现这个效果。
1 2
| let str = 'abc' let arr = [...str]
|
常用操作就说这些了,展开运算符是不是很简单呢?好像是的
那,[...undefined]会输出什么呢?
可迭代对象
我们打开控制台输入[...undefined]会发生什么呢
1 2 3
| [...undefined]
Uncaught TypeError: undefined is not iterable
|
控制台告诉我们undefined是不可迭代的
什么是可迭代对象呢?
在ES6中常用的集合对象和字符串都是可迭代对象
通过生成器创建的迭代器也是可迭代对象
要成为可迭代对象, 一个对象必须实现 iterator 方法。可迭代对象具有Symbol.iterator属性,即具有Symbol.iterator属性的对象都有默认迭代器。
我们可以用Symbol.iterator来访问对象的默认迭代器
迭代器
迭代器是一种特殊对象,每一个迭代器都有一个next方法,该方法会返回一个对象,对象包括value和done属性。
我们实现一个简单的迭代器来看一下方便理解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function createIterator(items){ let i = 0; return { next(){ let done = ( i >= items.length) let value = !done ? items[i++] : undefined; return { done, value } } } }
const a = createIterator([1,2,3]) console.log(a.next()) console.log(a.next()); console.log(a.next()); console.log(a.next());
|
生成器
迭代器是一个对象,我们前边通过一个函数来生成了迭代器
提到迭代器就不可避免的印出来生成器
生成器是一个函数,用来返回迭代器。
生成器函数是ES6提出来的,声明的时候需要声明一个带有*星号的函数,同时你还会用到yield。
还是拿到代码中看一下吧
1 2 3 4 5 6 7 8 9 10 11 12 13
| function *createIterator(){ yield 1; yield 2; yield 3; }
const a = createIterator();
console.log(a.next()); console.log(a.next()); console.log(a.next()); console.log(a.next());
|
我们可以发现生成器与我们自己定义一个迭代器可以实现相同的效果,这就是yield的作用
生成器的yield关键字有一个神奇的功能,就是当你执行一次next(),那么就会执行一个yield后边的内容,然后语句终止运行
yield使用限制
当然yield使用是有限制的,只能在生成器函数内部使用,不能在非生成器的其他函数内部使用。
1 2 3 4 5 6 7 8 9
| function *createInerator(items){ items.map((value,key) => { yield value; }) } const a = createIterator([1,2,3]) console.log(a.next())
|
生成器函数表达式
函数表达式就是匿名函数,就这么叫没什么好纠结的
1 2 3 4 5
| const createIterator = function *(){ yield 1; yield 2; } const a = createIterator()
|
对象中添加生成器函数
一个普通对象通常是长这个样子的
我们可以在对象中添加一个生成器,也就是一个带星号的方法
1 2 3 4 5 6 7 8
| const obj = { a: 1, *createIterator(){ yield this.a } } const a = obj.createIterator() console.log(a.next)
|
理解可迭代对象
前边我们说到过什么是可迭代对象,但描述并不容易理解,所以由此引出了迭代器与生成器,现在我们应该可以好好理解什么是可迭代对象了。
再次强调一遍,迭代器是一个对象,生成器是一个返回迭代器的函数
凡是通过生成器生成的迭代器,都是可迭代对象,也就可以通过for..of遍历。
1 2 3 4 5 6 7 8 9
| function *createIterator(){ yield 1; yield 2; yield 3; } const a = createIterator() for (let value of a){ console.log(value) }
|
我们前边也提到过可迭代对象都具有Symbol.iterator属性,我们可以通过这个属性访问迭代器
1 2 3 4 5 6 7 8
| function *createIterator() { yield 1; yield 2; yield 3; } const a = createIterator(); const s = a[Symbol.iterator](); console.log(s.next())
|
通过Symbol.iterator来检测一个对象是否可迭代也是可行的
1
| typeof obj[Symbol.iterator] === "function"
|
创建可迭代对象
前边已经提到过了,在es6中,数组、set、map、字符串都是可迭代对象。
但是默认情况下定义的对象是不可迭代的
1 2 3 4 5 6 7
| cosnt obj = { items: [] } obj.items.push(1) for(let x of obj){ console.log(x) }
|
所以就需要我们手动的给对象添加[Symbol.iterator]属性,使其成为一个可迭代的对象
1 2 3 4 5 6 7 8 9 10 11 12
| const obj = { items: [], *[Symbol.iterator](){ for (let item of this.items){ yield item } } } obj.items.push(1) for(let x of obj){ console.log(x) }
|
内建迭代器
我们上边提到过,数组、set、map默认都是可迭代对象,也就是说他们内部已经实现了迭代器,并且其提供了3中迭代器的函数调用
entries():返回键值对
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const arr = ['a', 'b', 'c'] for(let v of arr.entries()){ console.log(v) }
const arr = new Set(['a', 'b', 'c']) for(let v of arr.entries()) { console.log(v) }
const arr = new Map(); arr.set('a', 'a'); arr.set('b', 'b'); for(let v of arr.entries()) { console.log(v) }
|
values():返回键值对的value
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const arr = ['a', 'b', 'c']; for(let v of arr.values()) { console.log(v) }
const arr = new Set(['a', 'b', 'c']); for(let v of arr.values()) { console.log(v) }
const arr = new Map(); arr.set('a', 'a'); arr.set('b', 'b'); for(let v of arr.values()) { console.log(v) }
|
keys:返回键值对的key
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const arr = ['a', 'b', 'c']; for(let v of arr.keys()) { console.log(v) }
const arr = new Set(['a', 'b', 'c']); for(let v of arr.keys()) { console.log(v) }
const arr = new Map(); arr.set('a', 'a'); arr.set('b', 'b'); for(let v of arr.keys()) { console.log(v) }
|
for…of循环解构对象
对象本身不支持迭代,我们前边给对象添加生成器使其成为可迭代对象。这次我们给对象添加新的生成器使其可以使用for…of循环解构key和value
1 2 3 4 5 6 7 8 9 10 11 12 13
| const obj = { a: 1, b: 2, *[Symbol.iterator](){ for(let i in obj){ yield [i, obj[i]] } } }
for (let [key, value] of obj){ console.log(key, value) }
|
高级功能
高级功能就是涉及到一些传参、抛出异常、委托生成器等等功能,由于自己也不是特别理解,避免误导就不写太多,简单写一下传参和生成器的返回语句的用法吧,具体原理不太懂,如果后续有了比较好的理解,会修改这篇文章的。
传参
不是特别理解这个概念,给大家看一下示例
1 2 3 4 5 6 7 8 9
| function *createIterator(){ let first = yield 1; yield first + 2 } const a = createIterator() console.log(a.next())
console.log(a.next()) console.log(a.next())
|
我们可以给next传入参数,它会替代掉上一个next的yield返回值
1 2 3 4 5 6 7 8 9
| function *createIterator(){ let first = yield 1; yield first + 2 } const a = createIterator() console.log(a.next())
console.log(a.next(5)) console.log(a.next())
|
生成器返回语句
生成器中添加return表示退出操作,函数中使用return应该没什么好说的。
1 2 3 4 5 6 7 8
| function *createIterator(){ yield 1; return; yield 2; } cosnt a = createIterator() console.log(a.next()) console.log(a.next())
|
委托生成器
委托生成器就是生成器之间进行嵌套
只能给一个简单示例,太深入的用法不懂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function *aIterator(){ yield 1; yield 2; }
function *bIterator(){ yield 3; }
function *cIterator(){ yield *aIterator() yield *bIterator() }
const i = cIterator() console.log(i.next()); console.log(i.next()); console.log(i.next()); console.log(i.next());
|
好吧,就先到这里了,后续有新理解再更新内容吧。