前言
從家里回來了,翻了翻書,發現es6的知識點,忘了不少,還好記了點筆記,今年需要更加努力,看著以前的同學都開始娶妻生子,買車買房,我也要努力為自己的未來考慮
我的博客地址 :http://www.aymfx.cn/
迭代器
一個迭代器對象 ,知道如何每次訪問集合中的一項, 并跟蹤該序列中的當前位置。在 JavaScript 中 迭代器是一個對象,它提供了一個next() 方法,用來返回序列中的下一項。這個方法返回包含兩個屬性:done和 value。
//這是模仿es6迭代器的方式
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]);
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
//{done: false, value: 1}
//VM42:18 {done: false, value: 2}
//VM42:19 {done: false, value: 3}
//VM42:20 {done: true, value: undefined}
//VM42:21 {done: true, value: undefined}
這是個簡單的es5的迭代器,在es6的迭代器更加復雜
什么是生成器
雖然自定義的迭代器是一個有用的工具,但由于需要顯式地維護其內部狀態,因此需要謹慎地創建。Generators提供了一個強大的選擇:它允許你定義一個包含自有迭代算法的函數, 同時它可以自動維護自己的狀態。它是這樣表示的
//生成器
function *createIterator(){
yield 1;
yield 2;
yield 3;
}
//生成器的調用方式與普通函數相同,只不過返回的是一個迭代器
var iterator= createIterator();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
//{value: 1, done: false}
//VM55:12 {value: 2, done: false}
//VM55:13 {value: 3, done: false}
每個yield相當于一個調用一次next方法,而且每次執行yield都會暫停一次。函數名前面的*代表它是一個生成器
yield只能在生成器中用,不能在普通函數使用,生成器的內部函數也不能使用(語法錯誤),另外yield關鍵字可以返回任何值或者表達式
function * createIterator(itmes){
for(let i = 0; i< items.length;i++){
yield items[i]
}
}
var iterator= createIterator([1,2,3]);
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
//{value: 1, done: false}
//VM98:8 {value: 2, done: false}
//VM98:9 {value: 3, done: false}
//VM98:10 {value: undefined, done: true}
//VM98:11 {value: undefined, done: true}
生成函數表達式
let createIterator = function *(items) {
for (let i = 0; i< items.length ; i++){
yield items[i];
}
}
var iterator= createIterator([1,2,3]);
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
//VM108:10 {value: 1, done: false}
//VM108:11 {value: 2, done: false}
//VM108:12 {value: 3, done: false}
//VM108:13 {value: undefined, done: true}
//VM108:14 {value: undefined, done: true}
不能用箭頭函數來創建生成器
生成器對象的方式
let o = {
createIterator:function *(items) {
for (let i = 0; i< items.length ; i++){
yield items[i];
}
}
}
let o.createIterator([1,2,3])
也可以用es6的寫法
let o = {
*createIterator(items) {
for (let i = 0; i< items.length ; i++){
yield items[i];
}
}
}
let o.createIterator([1,2,3])
可迭代對象和for-of循環
es6中所有的集合對象(數組,Set集合以及Map集合)和字符串都是可迭代對象,可迭代對象都是具有Symbol.iterator屬性,通過指定的函數可以返回一個作用于附屬對象的迭代器
由生成器創建的迭代器都是可迭代對象,因為它會默認為Symbol.iterator屬性賦值。
for -of 每次執行都會調用next()方法,并將返回的結果對象的value存儲在變量中,直到遇到對象的done屬性為true
let values = [1,2,3];
for(let value of values){
console.log(value);
}
//返回結果
// 1
// 2
// 3
訪問可迭代對象的默認的迭代器
let values = [1,2,3];
let iterator = values[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
//運行結果
//{value: 1, done: false}
//VM57:5 {value: 2, done: false}
//VM57:6 {value: 3, done: false}
//VM57:7 {value: undefined, done: true}
創建可以迭代的對象
默認情況下開發者定義的對象是不能迭代的,但是我們可以給Symbol.iterator屬性加一個生成器
let arr = {
items:[],
*[Symbol.iterator](){
for(let item of this.items){
yield item;
}
}
}
let a = [1,2,3];
a.forEach(value => arr.items.push(value));
for(let x of arr){
console.log(x);
}
// 1
//2
//3
內建迭代器
es6自己定義了一些迭代器,我們只有在無法用這些內建迭代器實現功能時才可能自己創建
集合對象迭代器
es6的三種對象數組,Set集合,Map集合都內置了下列三種迭代器
- entries() 返回一個迭代器,其值為多個鍵值對
- values() 返回一個迭代器,其值為集合的值
- keys() 返回一個迭代器,其值為集合中的所有鍵名
entries()栗子 三種表現
let colors = ['red','blue','green'];
let tracking = new Set([123,567,9012]);
let data = new Map();
data.set('title','es6教程');
data.set('format','ebook');
for (let entry of colors.entries()){
console.log(entry);
}
for (let entry of tracking .entries()){
console.log(entry);
}
for (let entry of data.entries()){
console.log(entry);
}
values()迭代器
let colors = ['red','blue','green'];
let tracking = new Set([123,567,9012]);
let data = new Map();
data.set('title','es6教程');
data.set('format','ebook');
for (let entry of colors.values()){
console.log(entry);
}
for (let entry of tracking .values()){
console.log(entry);
}
for (let entry of data.values()){
console.log(entry);
}
數組的values()谷歌暫不支持,所以我們需要小心使用這些方法,有的瀏覽器還是支持的,谷歌不支持我肯定不會用
keys()迭代器
let colors = ['red','blue','green'];
let tracking = new Set([123,567,9012]);
let data = new Map();
data.set('title','es6教程');
data.set('format','ebook');
for (let entry of colors.keys()){
console.log(entry);
}
for (let entry of tracking .keys()){
console.log(entry);
}
for (let entry of data.keys()){
console.log(entry);
}
不同的集合類型在使用for-of迭代有各自默認的迭代器
let colors = ['red','blue','green'];
let tracking = new Set([123,567,9012]);
let data = new Map();
data.set('title','es6教程');
data.set('format','ebook');
//與colors.values()方法相同
for(let value of colors){
console.log(value);
}
//與tracking .values()方法相同
for(let value of tracking ){
console.log(value);
}
//與data.entries()方法相同
for(let value of data){
console.log(value);
}
WeakMap和WekSet因為需要管理弱引用,因此無法切確知道集合中存在的值,所以不能被迭代
解構的方式來用for-of
let data = new Map();
data.set('title','es6教程');
data.set('format','ebook');
for(let [key,value] of data){
console.log(key+":"+value);
}
//title:es6教程
//format:ebook
字符串迭代器
es5發布以后,字符串的慢慢變的像數組,于是我們有些方式可以用了,例如我們可以通過[]來獲取字符串的中的字符。但是我們怎么訪問雙字節,就如下面這種情況
var message = 'A ?? B';
for(let i = 0;i<message.length;i++){
console.log(message[i]);
}
我們可以用 for-of來做迭代
var message = 'A??B';
for(let i of message){
console.log(i);
}
NodeList迭代器
雖然在es6之前Nodelist和數組在內部差異表現不一致,容易造成困擾,但是es6之后,nodelist也有了自己的默認的迭代器,并且實現方式一致,因此我們可以這樣寫了
var divs = document.getElementsByTagName('div');
for(let nodeEle of divs){
console.log(nodeEle);
}
展開運算符與非數組可迭代對象
//Set
let set = new Set([1,3,3,5,6]),arr = [...set];
console.log(arr); // [1, 3, 5, 6]
//Map
let map= new Map([['name','ly'],['sex','男']]),arr = [...map];
console.log(arr);
//0:(2) ["name", "ly"]
//1:(2) ["sex", "男"]
//數組字面量
let small = [1,2.3,4],mid = [2,3,4,6],all = [0,...small,...mid];
console.log(all); //[0, 1, 2.3, 4, 2, 3, 4, 6]
高級迭代器功能
給迭代器傳參數
function *createIterator(){
let first = yield 1;
let second = yield first+2;
let third= yield second +3;
}
let iterator = createIterator();
console.log(iterator.next(1));
console.log(iterator.next(2));
console.log(iterator.next(3));
console.log(iterator.next(1));
第一個next和最后一個next,傳的值都會被丟棄,因為之前和之后都不存在可以用的值,所以傳參毫無意義,每次傳的參數都是作為上一個定義參數的值.
在迭代器中拋出錯誤
有時候我們需要增強生成器內部的編程彈性,需要將一些錯誤拋出去,讓迭代器繼續運行。
function *createIterator(){
let first = yield 1;
let second = yield first+2;
let third= yield second +3;
}
let iterator = createIterator();
console.log(iterator.next(1));
console.log(iterator.next(4));
console.log(iterator.throw(new Error("boom")));
我們如何處理這些錯誤呢,可以這么寫
function *createIterator(){
let first = yield 1;
let second;
try{
second = yield first+2;
}catch(x){
second = 3
}
yield second +3;
}
let iterator = createIterator();
console.log(iterator.next(1));
console.log(iterator.next(4));
console.log(iterator.throw(new Error("boom")));
console.log(iterator.next());
通過return 可以提前結束函數的執行,再一次調用,屬性done將會被置為true
function *createIterator(){
yield 1;
return;
yield 2;
yield 3;
}
let iterator = createIterator();
console.log(iterator .next());
console.log(iterator .next());
return 返回的值也可以作為一次執行的結果
function *createIterator(){
yield 1;
return 20;
yield 2;
yield 3;
}
let iterator = createIterator();
console.log(iterator .next());
console.log(iterator .next());
console.log(iterator .next());
委托生成器
如果我們需要將兩個迭代器分別執行,但是又想只調用一個同樣的生成器,我們可以用委托生成器
function *createIteratorOne(){
yield 1;
yield 2;
}
function *createIteratorTwo(){
yield 'blue';
yield ';white';
}
function *createIterator(){
yield *createIteratorOne();
yield *createIteratorTwo();
}
let iterator = createIterator();
console.log(iterator .next());
console.log(iterator .next());
console.log(iterator .next());
yield * 也可以用于字符串 例如 yield * 'hello',這時會調用字符串的默認迭代器
function *createIterator(){
yield * 'hello'
}
let iterator = createIterator();
console.log(iterator .next());
console.log(iterator .next());
console.log(iterator .next());
異步任務執行
生成器支持在代碼中暫停代碼的執行,因此我們可以挖掘一些用法
簡單任務生成器
function run(taskDef){
//創建一個無使用限制的迭代器
let task = taskDef();
//開始執行任務
let result = task.next();
//循環調用next()的函數
function step(){
if(!result.done){
result = task.next();
step();
}
}
//開始執行迭代
step();
}
//調用run()函數
run(function *(){
console.log(1);
yield;
console.log(2);
yield;
console.log(3);
})
//逐步輸出 1 2 3
向任務執行器傳遞數據
function run(taskDef){
//創建一個無使用限制的迭代器
let task = taskDef();
//開始執行任務
let result = task.next();
//循環調用next()的函數
function step(){
if(!result.done){
result = task.next(result.value);
step();
}
}
//開始執行迭代
step();
}
//調用run()函數
run(function *(){
let value = yield 1;
console.log(value );
value = yield value + 1;
console.log(value );
})
//逐步輸出 1 2
異步任務執行器
function run(taskDef){
//創建一個無使用限制的迭代器
let task = taskDef();
//開始執行任務
let result = task.next();
//循環調用next()的函數
function step(){
if(!result.done){
if(typeof result.value ==='function'){
result.value(function(err,data){
if(err){result = task.throw(err)};
return;
})
} else {
result = task.next();
step();
}
}
}
//開始執行迭代
step();
}
//讀取文件函數
let fs = require("fs");
function readFile(filename){
return function(callback){
fs.readFile(filename.,callback)
}
}
//調用
run(function *(){
let contents = yield readFile("config.json");
doSomethingWith(contents);
console.log("Done")
})