ECMAScript 6(以下簡稱ES6)是JavaScript語言的下一代標准。
1. let 和 const
1 { 2 var a=5; 3 let b=10; 4 } 5 console.log(a); 6 console.log(b);
控制台就是這樣輸出:
也就是說,var聲明的變量由於不存在塊級作用域所以可以在全局環境中調用,而let聲明的變量由於存在塊級作用域所以不能在全局環境中調用。
再來看一個經典例子(閉包):
1 var a=[]; 2 //執行for循環 3 for(var i=0;i<10;i++){ 4 a[i]=function(){ //因為這個是定義,並沒有調用方法,不會執行 5 console.log(i); 6 }; 7 } 8 //for循環之后,此時 i = 10;再次執行a[6]();因為 i 一直被引用,所以不會回收,進入到 a[i] 的方法里面, 打印的是 i ,也就是10 9 a[6](); //輸出10
如果使用 let
1 var a=[]; 2 for(let i=0;i<10;i++){ 3 a[i]=function(){ 4 console.log(i); 5 }; 6 } 7 a[6](); //打印6
a[6]函數(閉包)這個執行環境中,它會首先尋找該執行環境中是否存在 i,沒有找到,因為 i 是塊級作用域,就沿着作用域鏈繼續向上到了其所在的代碼塊執行環境,找到了i=6,於是輸出了6,即a[6]();的結果為6。這時,閉包被調用,所以整個代碼塊中的變量i和函數a[6]()被銷毀。
const 是定義常量:const a = 14; 此后變量 a 的值無法更改覆蓋。
2. 字符串拼接
1 //傳統字符串拼接 2 var s1 = '生物膜系統組裝又拆分,變幻莫測;'; 3 var s2 = '你的好多細胞在分裂,'; 4 var str = '孩子們:請聽我說!'+s2+'有絲,減數,哪管白天和黑夜。'+ 5 '染色體,細胞核時隱時現,'+s1+'核糖體在mRNA上穿梭忙碌,'+'幾千種酶各司其職,將活化能狠狠打折。'; 6 console.log(str); 7 8 // 字符模板的寫法 9 var s1 = '染色體,細胞核時隱時現,'; 10 var s2 = '你的好多細胞在分裂,'; 11 var str = `孩子們:請聽我說!${s2}有絲,減數,哪管白天和黑夜。${s1}生物膜系統組裝又拆分,變幻莫測;核糖體在mRNA上穿梭忙碌,幾千種酶各司其職,將活化能狠狠打折。`; 12 console.log(str);
es6使用 ` ` 包裹字符串,即使斷開,也可以用這個符號包裹起來合並成一個字符串。
3. 解構賦值
1 // 以前我們給變量賦值,只能直接指定值 2 var a = 1; 3 var b = 2; 4 var c = 3; 5 console.log(a,b,c); // 1 2 3 6 7 // 現在用解構賦值的寫法就變得簡單了,只要模式匹配上了就行了,如下 8 // 注意數組是有順序的 9 var [a,b,c] = [11,22,33]; 10 console.log(a,b,c); // 11 22 33 11 12 var [b,a,c] = [11,22,33]; 13 console.log(a,b,c); // 22 11 33 14 15 // 當然解構賦值還有嵌套比較復雜的寫法,如下 16 let [foo,[[bar],[baz]]] = [111,[[222],[333]]]; 17 console.log(foo,bar,baz); // 111 222 333 18 19 let [head,...foot] = [1,2,3,4]; 20 console.log(head,foot); // 1 [2,3,4] 21 22 // 如果解構不成功,變量的值就等於undefined,如下 23 var [bar3,foo3] = [1000]; 24 console.log(bar3,foo3); // 1000 undefined 25 26 // 另一種情況是不完全解構,即等號左邊的模式,只匹配一部分的等號右邊的數組。這種情況下,解構依然可以成功 27 let [x,y] = [10000,20000,30000]; 28 console.log(x,y); // 10000 20000 29 30 // 默認值可以引用解構賦值的其他變量,但該變量必須已經聲明 31 let [a=1,b=a] = [2,3]; 32 console.log(a,b); // 2 3 33 34 // 對象的解構也可以指定默認值 35 var {x,y=5} = {x:1}; 36 console.log(x,y); // 1 5 37 38 //對象的解構賦值解構不僅可以用於數組,還可以用於對象(json) 39 //對象的解構與數組有一個重要的不同。數組的元素是按次序排列的,變量的取值由它的位置決定; 40 //而對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值 41 var {a,b} = {a:'apple',b:'banana'}; 42 console.log(a,b); // apple banana 43 var {b,a} = {a:'apple',b:'banana'}; 44 console.log(a,b); // apple banana 45 46 // 如果變量名與屬性名不一致,必須寫成下面這樣 47 let obj = {first:'hello',last:'world'}; 48 // first ---> f,那么此時f就是first,而不是undefined了,有點類似別名的概念 49 let {first:f,last} = obj; 50 console.log(f,last); // hello world 51 52 //1.也就是說,對象的解構賦值的內部機制,是先找到同名屬性,然后再賦給對應的變量。 真正被賦值的是后者,而不是前者 53 //2.v是匹配的模式,n才是變量。真正被賦值的是變量n,而不是模式v。 54 //注意,采用這種寫法時,變量的聲明和賦值是一體的 55 // v ---> n,那么此時n就是vue,而不是undefined了 56 var {v:n} = {v:'vue',r:'react'}; 57 console.log(n); // vue 58 console.log(v); // Uncaught ReferenceError: v is not defined 59 console.log(r); // Uncaught ReferenceError: r is not defined
4. 復制數組
1 // 數組的淺拷貝,引用之間的拷貝,沒有實現數組的真正復制 2 var arr1 = [1, 2, 3]; 3 var arr2 = arr1; 4 arr2.push(4); 5 console.log(arr1, arr2); //[1, 2, 3, 4] [1, 2, 3, 4] 6 7 // 復制數組深拷貝,傳統做法 8 var arr1 = [1,2,3]; 9 var arr2 = []; 10 //通過for循環遍歷之后將arr1數組的每一項賦值給arr2數組的每一項, 就實現了數組的深拷貝,這時候我再去操作arr2的數組的時候,arr1就不會受影響了 11 for(var i=0;i<arr1.length;i++){ 12 arr2[i] = arr1[i]; 13 } 14 // 數組尾部添加 15 arr2.push(4); 16 console.log(arr1,arr2); 17 18 // ES6實現的數組的深拷貝方法1 19 var arr1 = [1,2,3]; 20 var arr2 = Array.from(arr1); 21 // 數組尾部添加 22 arr2.push(100); 23 console.log(arr1,arr2); // [1, 2, 3] [1, 2, 3, 100] 24 25 // ES6實現的數組的深拷貝方法2 26 var arr1 = [1,2,3]; 27 // 超引用拷貝數組 28 var arr2 = [...arr1]; 29 // 數組尾部添加 30 arr2.push(1000); 31 console.log(arr1,arr2); // [1, 2, 3] [1, 2, 3, 1000] 32 33 function show(...args){ 34 // 此時這個形式參數就是一個數組,我們可以直接push東西進來,如下 35 args.push(5); 36 console.log(args); 37 } 38 // 調用 39 show(1,2,3,4); // [1, 2, 3, 4, 5]
5. 增加了Map對象,傾向於后台
1 var map = new Map(); 2 // 設置 3 // map.set(name,value); 4 map.set('a','apple'); 5 map.set('b','banana'); 6 // 獲取 7 // map.get(name); 8 console.log(map.get('a') + ' ' + map.get('b')); 9 // 刪除之前map對象 10 console.log(map); 11 // 刪除 12 // map.delete(name); 13 map.delete('a'); 14 // 刪除之后map對象 15 console.log(map); 16 17 // 注意for..in是不能循環map對象的,不報錯也無任何反應,所以下一代碼無任何輸出,稍微注意下 18 for(var name in map){ 19 console.log(name); 20 } 21 22 // 實體 map對象的循環輸出 23 for(var name of map){ 24 //循環出來的結果就是:a,apple b,banana 循環key,value 25 console.log(name); 26 } 27 28 //循環出來的結果就是: a,apple b,banana 循環key,value 29 for(var [key,value] of map.entries()){ 30 console.log(key,value); 31 } 32 33 //只循環key 34 for(var key of map.keys()){ 35 console.log(key); 36 } 37 38 //只循環value 39 for(var val of map.values()){ 40 console.log(val); 41 }
6. for-of循環 ,上一例子也說到了for-of循環map對象輸出
1 //for of一個arr對象 2 var arr = ['紅樓夢','西游記','三國演義','水滸傳','火影']; 3 //只循環key 0 1 2 3 4 輸出key值,也就是下標索引 4 for(var key of arr.keys()){ 5 console.log(key); 6 } 7 //只循環value,注意數組是沒有.values() 直接 var value of arr ,輸出 紅樓夢,西游記,三國演義,水滸傳,火影 8 for(var value of arr){ 9 console.log(value); 10 } 11 //循環key,value 12 for(var [key,value] of arr.entries()){ 13 console.log(key,value); 14 } 15 16 //for in循環與for of循環的區別 17 var arr = ['apple','banana','orange','pear']; 18 for(var i in arr){ 19 // i打印出來的就是arr數組對應的索引 20 // 0 1 2 3 21 console.log(i); 22 } 23 for(var i of arr){ 24 // i值打印出來的就是我們想要的數組具體的值 25 // apple banana orange pear 26 console.log(i); 27 } 28 29 //for of不能循環json 30 var json = {'a':'apple','b':'banana','c':'orange','d':'pear'}; 31 for(var name in json){ 32 // a b c d 33 console.log(name); 34 // apple 35 console.log(json.a); 36 // pear 37 console.log(json['d']); 38 } 39 // 注意for..of可以循環arr,但是不可以循環json,會報錯,特別注意下 40 for(var name of json){ 41 Uncaught TypeError: undefined is not a function 42 console.log(json); 43 }
7. 箭頭函數:引入箭頭函數有兩個方面的作用:更簡短的函數並且不綁定this
。
箭頭函數表達式的語法比函數表達式更短,並且不綁定自己的this,arguments,super或 new.target。這些函數表達式最適合用於非方法函數,並且它們不能用作構造函數,不能使用new。
箭頭函數的寫法 function(){ } 變成 ()=>{ }
1 var a = ()=>{ 2 return 1; 3 }
等價於
1 function a(){ 2 return 1; 3 }
如果函數體只有一條語句,可以這樣寫:
1 var fun = ()=>Math.random()*10 2 console.log(fun());
這樣子調用這個箭頭函數就會直接返回這條語句的值
箭頭函數不綁定arguments,取而代之用rest參數…解決
1 function A(a){ 2 console.log(arguments); //[object Arguments] [1, 2, 3] 3 } 4 5 var B = (b)=>{ 6 console.log(arguments); //錯誤:ReferenceError: arguments is not defined 7 } 8 9 var C = (...c)=>{ //...c即為rest參數 10 console.log(c); //[3, 1, 2] 11 } 12 A(1,2,3); 13 B(2,1,3); 14 C(3,1,2);
箭頭函數會捕獲其所在上下文的 this 值,作為自己的 this 值
1 var obj = { 2 a: 10, 3 b: function(){ 4 console.log(this.a); //輸出10 5 }, 6 c: function() { 7 return ()=>{ 8 console.log(this.a); //輸出10,捕獲了上面obj的this作為自己的this 9 } 10 } 11 } 12 obj.b(); 13 obj.c()();
所謂箭頭函數的 this 捕獲的是所在的上下文,比如下面這個例子:b
是一個箭頭函數,然后它的 this
是指向window
,這是為什么呢,因為箭頭函數捕獲的是obj{}
這個對象的環境,然后這個環境的this
指向的是window
,就相當於上一條的例子:在c
方法里面return
的那個箭頭函數捕獲的是c:function(){}
這個環境的this
,而這個環境的this
是obj
1 var obj = { 2 a: 10, 3 b: () => { 4 console.log(this.a); //undefined 5 console.log(this); //window 6 }, 7 c: function() { 8 console.log(this.a); //10 9 console.log(this); //obj{...} 10 } 11 } 12 obj.b(); 13 obj.c();
對於函數的this
指向問題:
- 箭頭函數的
this
永遠指向其上下文的this
,任何方法都改變不了其指向,如call(), bind(), apply()
- 普通函數的
this
指向調用它的那個對象
8. 對象的簡潔語法
1 //傳統對象_單體模式寫法 key-value模式 2 var person = { 3 name:'krry', 4 age:21, 5 showName:function(){ 6 return this.name; 7 }, 8 showAge:function(){ 9 return this.age; 10 } 11 }; 12 // 調用 13 console.log(person.showName()); // krry 14 console.log(person.showAge()); // 21 15 16 //ES6_單體模式寫法 不需要寫key 17 var name = 'krry'; 18 var age = 21; 19 var person = { 20 name, 21 age, 22 showName(){ 23 return this.name; 24 }, 25 showAge(){ 26 return this.age; 27 } 28 }; 29 // 調用 30 console.log(person.showName()); // krry 31 console.log(person.showAge()); // 21
9. 類和繼承(class和extends)
1. 傳統面向對象的寫法:
1 function Person(name,age){ // 類、構造函數 2 this.name = name; 3 this.age = age; 4 } 5 Person.prototype.showName = function(){ 6 return this.name; 7 }; 8 Person.prototype.showAge = function(){ 9 return this.age; 10 }; 11 var p1 = new Person('allen',28); 12 var p2 = new Person('xiaoxiaoyou',101); 13 console.log(p1.showName()); // allen 14 console.log(p2.showAge()); // 101 15 console.log(p1.showName == p2.showName); //true 注意不是調用方法,沒有括號,所以才true 16 console.log(p1.constructor == Person); // true 構造方法相等
2. ES6面向對象寫法:
1 class Person{ 2 // 構造器 3 constructor(name,age){ 4 this.name = name; 5 this.age = age; 6 } 7 showName(){ 8 return this.name; 9 } 10 showAge(){ 11 return this.age; 12 } 13 } 14 var p1 = new Person('aaa',18); 15 var p2 = new Person('bbb',20); 16 console.log(p1.name); // aaa 17 console.log(p1.showName()); // aaa 18 console.log(p2.showAge()); // 20 19 console.log(p1.showAge == p2.showAge); // true 20 console.log(p1.constructor == Person); // true
3. 面向對象給class賦值默認值:
1 class Person{ 2 // 構造器 3 constructor(name='default',age=0){ 4 this.name = name; 5 this.age = age; 6 } 7 showName(){ 8 return this.name; 9 } 10 showAge(){ 11 return this.age; 12 } 13 } 14 15 var p1 = new Person(); 16 console.log(p1.name); // 構造器里面給的默認值 default 17 console.log(p1.age); // 構造器里面給的默認值 0
4. 傳統寫法原型繼承extends
1 //傳統寫法原型繼承 2 function Person(name,age){ // 類、構造函數 3 this.name = name; 4 this.age = age; 5 } 6 Person.prototype.showName = function(){ 7 return this.name; 8 }; 9 Person.prototype.showAge = function(){ 10 return this.age; 11 }; 12 // 工人類 13 function Worker(name,age){ 14 // 屬性繼承過來 15 Person.apply(this,arguments); 16 } 17 // 原型繼承 18 Worker.prototype = new Person(); 19 var p1 = new Person('allen',28); 20 var w1 = new Person('worker',1000); 21 console.log(w1.showName()); // 確實繼承過來了 result:worker
5. ES6中面向對象實現類繼承
1 class Person{ 2 // 構造器 3 constructor(name,age){ 4 this.name = name; 5 this.age = age; 6 } 7 showName(){ 8 return this.name; 9 } 10 showAge(){ 11 return this.age; 12 } 13 } 14 class Worker extends Person{ 15 constructor(name,age,job='啦啦啦'){ 16 // 繼承超父類的屬性 17 super(name,age); 18 this.job = job; 19 } 20 showJob(){ 21 return this.job; 22 } 23 } 24 var p1 = new Person('aaa',18); 25 var w1 = new Person('www',36); 26 var w2 = new Worker('wwwwwwww',90); 27 console.log(w1.showName()); // www 28 console.log(w2.showJob()); // 默認給的值 ‘啦啦啦’
10. 模塊化 export 和 import
import 導入模塊、export 導出模塊
可以直接在任何變量或者函數前面加上一個 export
關鍵字,就可以將它導出。
在一個文件中:
1 export const sqrt = Math.sqrt; 2 export function square(x) { 3 return x * x; 4 } 5 export function diag(x, y) { 6 return sqrt(square(x) + square(y)); 7 }
然后在另一個文件中這樣引用:
1 import { square, diag } from 'lib'; 2 console.log(square(11)); // 121 3 console.log(diag(4, 3));
總結:
1 //mod.js 2 // 第一種模塊導出的書寫方式(一個個的導出) 3 // 導出普通值 4 export let a = 12; 5 export let b = 5; 6 // 導出json 7 export let json = { 8 a, 9 b 10 }; 11 // 導出函數 12 export let show = function(){ 13 return 'welcome'; 14 }; 15 // 導出類 16 export class Person{ 17 constructor(){ 18 this.name = 'jam'; 19 } 20 showName(){ 21 return this.name; 22 } 23 } 24 25 //index.js 26 //導出模塊如果用default了,引入的時候直接用,若沒有用default,引入的時候可以用{}的形式 27 // 導入模塊的方式 28 import { 29 a, 30 b, 31 json, 32 show, 33 Person 34 } from './mod.js'; 35 console.log(a); // 12 36 console.log(b); // 5 37 console.log(json.a); // 12 38 console.log(json.b); // 5 39 console.log(show()); // welcome 40 console.log(new Person().showName()); // jam 41 42 //mod1.js 43 // 第二種模塊導出的書寫方式 44 let a = 12; 45 let b = 5; 46 let c = 10; 47 export { 48 a, 49 b, 50 c as cc // as是別名,使用的時候只能用別名,特別注意下 51 }; 52 53 //index1.js 54 // 導入模塊的方式 55 import { 56 a, 57 b, 58 cc // cc是導出的,as別名 59 } from './mod1.js'; 60 console.log(a); // 12 61 console.log(b); // 5 62 console.log(cc); // 10 63 64 //mod2.js 65 // 第三種模塊導出的書寫方式 ---> default 66 // default方式的優點,import無需知道變量名,就可以直接使用,如下 67 // 每個模塊只允許一個默認出口 68 var name = 'jam'; 69 var age = '28'; 70 export default { 71 name, 72 age, 73 default(){ 74 console.log('welcome to es6 module of default...'); 75 }, 76 getName(){ 77 return 'bb'; 78 }, 79 getAge(){ 80 return 2; 81 } 82 }; 83 84 //index2.js 85 // 導入模塊的方式 86 import mainAttr from './mod2.js'; 87 var str = ' '; 88 // 直接調用 89 console.log(`我的英文名是:${mainAttr.name}我的年齡是${mainAttr.age}`); 90 mainAttr.default(); // welcome to es6 module of default... 91 console.log(mainAttr.getName()); // bb 92 console.log(mainAttr.getAge()); // 2 93 94 //mod3.js 95 var name = 'jam'; 96 var age = '28'; 97 export function getName(){ 98 return name; 99 }; 100 export function getAge(){ 101 return age; 102 }; 103 104 //index3.js 105 // 導入模塊的方式 106 import * as fn from './mod3.js'; 107 // 直接調用 108 console.log(fn.getName()); // jam
11. Promise
在promise之前代碼過多的回調或者嵌套,可讀性差、耦合度高、擴展性低。通過Promise機制,扁平化的代碼機構,大大提高了代碼可讀性;用同步編程的方式來編寫異步代碼,保存線性的代碼邏輯,極大的降低了代碼耦合性而提高了程序的可擴展性。
就是用同步的方式去寫異步代碼。
1 //Promise對象 ---> 用來傳遞異步操作過來的數據的 2 //Pending(等待、處理中) ---> Resolve(完成,fullFilled) ---> Reject(拒絕,失敗) 3 //這里只是定義,還沒開始執行 4 var p1 = new Promise(function(resolve,reject){ 5 resolve(1); // 成功了,返回一個promise對象1 6 // reject(2); // 失敗了 7 }); 8 9 // 接收成功和失敗的數據,通過then來傳遞 10 // then也是返回一個promise對象,會繼續往下傳遞數據,傳遞給下一個then 11 p1.then(function(value){ 12 // resolve 13 console.log(value); //執行打印1 14 return value + 1; // 1 15 alert(`成功了:${value}`); 16 },function(value){ 17 // reject 18 alert(`失敗了:${value}`); 19 }).then(function(value){ 20 console.log(value); // 2 21 }); 22 23 //catch捕獲異常錯誤 24 var p1 = new Promise(function(resolve,reject){ 25 resolve('成功了'); //返回一個promise對象“成功了” 26 }); 27 //then也是返回一個promise對象,會繼續往下傳遞數據 28 p1.then(function(value){ 29 console.log(value); //打印“成功了” 30 // throw是用來拋錯誤的 31 throw '發生了點小意外'; 32 }).catch(function(e){ 33 // catch用來捕獲這個錯誤的 ---> 追蹤 34 console.log(e); 35 }); 36 37 //all ---> 全部,用於將多個promise對象,組合,包裝成 38 //Promise.all([p1,p2,p3,...]); 所有的promise對象,都正確,才走成功 39 //否則,只要有一個錯誤,就走失敗 40 var p1 = Promise.resolve(1); 41 var p2 = Promise.reject(0); 42 Promise.all([true,p1,p2]).then(function(obj){ 43 console.log(`成功了:${obj}`); 44 },function(obj){ 45 console.log(`失敗了:${obj}`); 46 }); 47 48 // race ---> 返回的也是一個promise對象 49 //最先執行的的promise結果,哪個最快我用哪個,所以下面打印的是one 50 var p1 = new Promise(function(resolve,reject){ 51 setTimeout(resolve,50,'one'); 52 }); 53 var p2 = new Promise(function(resolve,reject){ 54 setTimeout(resolve,100,'two'); 55 }); 56 Promise.race([p1,p2]).then(function(val){ 57 console.log(val); 58 }); 59 60 //resolve ---> 生成一個成功的promise對象 61 //語法規則:Promise.resolve(val); // 普通值 62 // Promise.resolve(arr); // 數組之類 63 //Promise.resolve(promise); // 傳遞另一個promise對象 64 //傳遞普通值 65 Promise.resolve('success').then(function(val){ 66 // 注意resolve,走得是這里 67 console.log(val); // success 68 },function(err){ 69 console.log("err:"+ err); 70 }); 71 //傳遞數組 72 Promise.resolve([1,2,3]).then(function(val){ 73 // 注意resolve,走得是這里 74 console.log(val); // [1,2,3] 75 },function(err){ 76 console.log(err); 77 }); 78 //傳遞一個promise對象 79 var p1 = Promise.resolve(520); 80 var p2 = Promise.resolve(p1); 81 p2.then(function(val){ 82 //從p1那邊傳遞過來的 83 console.log(val); // 520 84 });
再來一道經典面試題:
1 setTimeout(function() { 2 console.log(1) 3 }, 0); 4 new Promise(function executor(resolve) { 5 console.log(2); 6 for( var i=0 ; i<10000 ; i++ ) { 7 i == 9999 && resolve(); 8 } 9 console.log(3); 10 }).then(function() { 11 console.log(4); 12 }); 13 console.log(5);
首先先碰到一個 setTimeout,於是會先設置一個定時,在定時結束后將傳遞這個函數放到任務隊列里面,因此開始肯定不會輸出 1 。
然后是一個 Promise,里面的函數是直接執行的,因此應該直接輸出 2 3 。
然后,Promise 的 then 應當會放到當前 tick 的最后,但是還是在當前 tick 中。
因此,應當先輸出 5,然后再輸出 4 。
最后在到下一個 tick,就是 1 。
“2 3 5 4 1”
12. Generator、yield
生成器( generator)是能返回一個迭代器的函數。生成器函數也是一種函數,最直觀的表現就是比普通的function多了個星號*,在其函數體內可以使用yield關鍵字,有意思的是函數會在每個yield后暫停。
這里生活中有一個比較形象的例子。咱們到銀行辦理業務時候都得向大廳的機器取一張排隊號。你拿到你的排隊號,機器並不會自動為你再出下一張票。也就是說取票機“暫停”住了,直到下一個人再次喚起才會繼續吐票。
當你調用一個generator時,它將返回一個迭代器對象。這個迭代器對象擁有一個叫做next的方法來幫助你重啟generator函數並得到下一個值。next方法不僅返回值,它返回的對象具有兩個屬性:done和value。value是你獲得的值,done用來表明你的generator是否已經停止提供值。繼續用剛剛取票的例子,每張排隊號就是這里的value,打印票的紙是否用完就這是這里的done。
// 生成器 function *createIterator() { yield 1; yield 2; yield 3; } // 生成器能像正規函數那樣被調用,但會返回一個迭代器 let iterator = createIterator(); console.log(iterator.next().value); // 1 console.log(iterator.next().value); // 2 console.log(iterator.next().value); // 3 console.log(iterator.next().value); // undefined 因為generator已經停止提供值
1 //Generator ---> 生成器就是一個函數 2 //特點: 3 //1.函數名前面帶一個*,和普通函數做區分 4 //2.內部使用yield語句 5 //調用方式,如下var res = show(); 與普通函數一樣 6 //value指的是generator函數內容yield定義的值,done:false表示還沒遍歷完 7 //直接找到返回值return了,那么此時done才會為true 8 //console.log(res.next());{value:'值1',done:false} 9 function* show(){ 10 yield 'Hello'; 11 yield 'World'; 12 yield 'ES6'; 13 return 'xx'; 14 } 15 var res = show(); 16 console.log(res.next()); // {value: "Hello", done: false} 17 console.log(res.next()); // {value: "World", done: false} 18 console.log(res.next()); // {value: "ES6", done: false} 19 console.log(res.next()); // {value: "allen", done: true} 20 // 已經找到return返回值了,繼續下去就沒有意義了 21 // console.log(res.next()); // {value: "undefined", done: true} 22 23 //yield本身沒有返回值,或者可以說每次給你返回的是undefined 24 function* show(){ 25 var a = yield 'Hello'; 26 return a; 27 } 28 var res = show(); 29 console.log(res.next()); // {value: "Hello", done: false} 30 console.log(res.next()); // {value: "undefined", done: true} 31 32 //next方法是可以帶參數的,死循環的generator函數 33 function* fn(){ 34 for(var i=0;true;i++){ 35 // 如果里面傳了一個值,那么它會把這個參數賦給最近的一個yield 36 var a = yield i; 37 if(a) i = -1; 38 } 39 } 40 var d = fn(); 41 console.log(d.next()); // {value: 0, done: false} 42 console.log(d.next()); // {value: 1, done: false} 43 console.log(d.next()); // {value: 2, done: false} 44 // 如果里面傳了一個值,那么它會把這個參數賦最近的一個yield 45 console.log(d.next(true)); // {value: 0, done: false} 46 console.log(d.next()); // {value: 1, done: false} 47 console.log(d.next()); // {value: 2, done: false} 48 console.log(d.next()); // {value: 3, done: false} 49 50 // for..0f循環generator函數 51 function* fn(){ 52 yield 1; 53 yield 2; 54 yield 3; 55 yield 4; 56 yield 5; 57 return 6; 58 } 59 //for..0f循環generator函數,可以取值 60 for(let val of fn()){ 61 document.write(val); // 12345 62 } 63 64 // 對象里使用generator函數的特殊寫法,注意下 65 var json = { 66 *show(){ 67 yield 'a'; 68 yield 'b'; 69 return 'c'; 70 } 71 }; 72 var res = json.show(); 73 console.log(res.next()); // {value: "a", done: false} 74 console.log(res.next()); // {value: "b", done: false} 75 console.log(res.next()); // {value: "c", done: true}
自動調用生成器並啟動迭代器的方法:
1 function run(taskDef) { //taskDef即一個生成器函數 2 3 // 創建迭代器,讓它在別處可用 4 let task = taskDef(); 5 6 // 啟動任務 7 let result = task.next(); 8 9 // 遞歸使用函數來保持對 next() 的調用 10 function step() { 11 12 // 如果還有更多要做的 13 if (!result.done) { 14 console.log(result.value); //這里就執行該做的事 15 result = task.next(); 16 step(); 17 } 18 } 19 20 // 開始處理過程 21 step(); 22 23 } 24 //生成器 25 function *createIterator() { 26 yield 1; 27 yield 2; 28 yield 3; 29 } 30 //啟動 31 run(createIterator);
相關鏈接:
GitHub:https://github.com/Krryxa