轉載請註明出處javascript
原文鏈接 http://blog.huanghanlian.com/article/5c7aa6c7bf3acc0864870f9dhtml
首先弄明白ECMA和js的關係。ECMA是標準,Javascript是ECMA的實現。由於js也是一種語言,但凡語言都有一套標準,而ECMA就是javascript的標準。 在2015年正式發佈了ECMAscript6.0,簡稱ES6,又稱爲ECMAscript2015。前端
歷史java
ECMAScript和Javascriptnode
ECMA是標準,JS是實現react
目前版本git
時間 | ECMA | JS | 解釋 |
---|---|---|---|
1996.11 | EC 1.0 | JS穩定 | Netscript將js提交給ECMA組織,ES正式出現 |
1998.06 | ES 2.0 | ES2正式發佈 | |
1999.12 | ES 3.0 | ES3被普遍接受 | |
2007.10 | ES 4.0 | ES4過於激進,被廢了 | |
2008.07 | ES 3.1 | 4.0退化爲嚴重縮水版的3.1<br/>由於吵得太厲害,因此ES3.1代號爲Harmony(和諧) | |
2009.12 | ES 5.0 | ES5正式發佈<br/>同時公佈了JavaScript.next也就是後來的ES6.0 | |
2011.06 | ES 5.1 | ES5.1成爲了ISO國際標準 | |
2013.03 | ES 6.0 | ES6.0草案定稿 | |
2013.12 | ES 6.0 | ES6.0草案發布 | |
2015.06 | ES 6.0 | ES6.0預計發佈正式版<br/>JavaScript.next開始指向ES 7.0 |
es5兼容性es6
http://kangax.github.io/compat-table/es5/github
es6兼容性ajax
http://kangax.github.io/compat-table/es6/
ES6(ES2015)-- IE10+,Chrome,FireFox,移動端,NodeJS。這些環境基本上都是認得,都能兼容
可是有需求兼容ie怎麼辦
有兩種辦法
比方說在移動端或者是混合開發當中,多去用用ES6,在老的版本中不用。惹不起咋躲得起。編譯,轉換
在線轉換
ES6的到底有什麼樣的東西?
回顧ES5是怎麼生明變量的,有什麼樣的缺點
var
的缺點
能夠重複聲明
最大的問題
var a=12; var a=5; alert(a);//彈窗5
會發現5能出來,沒有報錯,沒有警告,什麼都沒有
這在其餘語言是不可出現的。
沒法限制修改
在程序中,有些東西是永遠不變的。
比方說常量PI=3.1415926
是不會發生改變
在不少語言中都有常量
的概念。在js中沒有
至少var
不是一個常量。
換句話說,要不要改,能不能讓別人別動這個值,不要改這個值。全憑自覺。
爲何java是全世界最流行的一門語言
緣由很簡單,由於他很是的嚴謹,他很是的死板。
相信一件事,越是容易的語言,越是簡單的語言。其實是不嚴謹。就無法去開發大型項目
反過來他可能讓你以爲很難受的語言java,對你限制很嚴格。可是你掌握了呀以後,開發起大型應用會很是的駕輕就熟。
沒有塊級做用域
es5 只在函數中支持塊級做用域
{ //這就是語法塊 } if(){ 變量=xxxx } //變量出來就用不了了,這就是塊做用域 for(){ }
體現塊級做用域做用
if(true){ var a=12; } alert(a); //在塊級做用域內聲明變量。在外部依然可以訪問
在ES6中有了兩種新的定義變量的方式
let
和const
let
const
let 不能重複聲明例子
let a=12; let a=5; console.log(a); //報錯 //Uncaught SyntaxError: Identifier 'a' has already been declared //不能重複聲明
const 不能重複聲明例子
const a=12; const a=5; console.log(a); //報錯 //Uncaught SyntaxError: Identifier 'a' has already been declared //不能重複聲明
在大型項目中,重複聲明這件事,指不定你定義了什麼東西別人也定義了。還不報錯,到時候定位bug很難找。
變量和常量
變量
let a=12;//聲明變量賦值 a=5;//給變量a賦值 console.log(a);//你會明確的發現它變成了5
常量
const a=12; a=5; console.log(a);
報錯,不能對常量賦值
塊級做用域
var 塊級做用域只在函數中體現,也就是說在函數中var
聲明的變量不會在全局做用域中體現
function aa(){ var a=1; console.log(a) } aa(); console.log(a)
let和const只在塊級做用域,或者在語法塊以內起做用
if(true){ let a=12; } console.log(a);//Uncaught ReferenceError: a is not defined
if(true){ const a=12; } console.log(a);//Uncaught ReferenceError: a is not defined
語言推出一個新的版本,一個更好的版本,他必定是要解決一些原來有的問題,ES6也不例外。
塊級做用域有什麼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <input type="button" value="按鈕1"> <input type="button" value="按鈕2"> <input type="button" value="按鈕3"> </body> <script type="text/javascript"> window.onload=function(){ var aBtn=document.getElementsByTagName('input'); for (var i = 0; i < aBtn.length; i++) { aBtn[i].onclick=function(){ alert(i) }; } }; </script> </html>
以上代碼執行,無論按哪一個按鈕彈出都是3
因爲var
聲明變量只在函數做用域中擴散到全局
在for或者if快級做用域中聲明的變量會在局部或全局生效
當for
循環執行完畢,i
這個變量暴露到全局了,等於3
因此for
循環中執行的事件綁定,是將點擊事件回調函數執行。當點擊按鈕時候,會出發綁定回調函數,此時當前做用域中,i
等於3,因此不管點擊哪一個按鈕彈出都是3
之前咱們是經過閉包解決這個問題
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <input type="button" value="按鈕1"> <input type="button" value="按鈕2"> <input type="button" value="按鈕3"> </body> <script type="text/javascript"> window.onload=function(){ var aBtn=document.getElementsByTagName('input'); for (var i = 0; i < aBtn.length; i++) { ! function(i) { aBtn[i].onclick=function(){ alert(i) }; }(i); } console.log(i) }; </script> </html>
在每一層循環的時候,用一個匿名函數並且是當即執行的匿名函數給他包裝起來,而後將每一次遍歷的1.2.3分別的值去傳到這個匿名函數裏,而後匿名函數接到這個參數i再放到點擊事件中去引用i當咱們每次點擊事件輸出的值i就會取每個閉包環境下的i。因此這樣就能達到效果。
使用let
來實現
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <input type="button" value="按鈕1"> <input type="button" value="按鈕2"> <input type="button" value="按鈕3"> </body> <script type="text/javascript"> window.onload=function(){ var aBtn=document.getElementsByTagName('input'); for (let i = 0; i < aBtn.length; i++) { aBtn[i].onclick=function(){ alert(i) }; } console.log(i) }; </script> </html>
for
循環自己就是一個語法塊,自身就是一個塊
因爲var
只把函數做爲做用域
因此以上須要經過當即執行函數來包一層,來實現效果。
而let
自己是支持塊級做用域的,因此電腦按鈕執行回掉函數,打印i,是當前塊級做用域下的i
這個i
在非for
塊做用域下是未定義的。
箭頭函數在寫法上對es5作了一些修整,代碼看起來更顯得簡潔
// 定義一個箭頭函數 let a = (arg)=>{ // 這裏=>符號就至關於function關鍵字 return arg+=1 } // 也能夠簡寫爲 let a = arg => arg+=1
箭頭函數的做用跟之前接觸的函數沒什麼本質的區別,更多的是一種寫法上的變化。
function show() { } 同等於 let show =()=>{ }
function () { } 同等於 ()=>{ }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> </body> <script type="text/javascript"> window.onload=function(){ alert('123') }; </script> </html> 同等於 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> </body> <script type="text/javascript"> window.onload=()=>{ alert('123') }; </script> </html>
箭頭函數也對this的指向作了修整 es6以前的函數的this指向調用函數時所在的對象,而箭頭函數的this指向函數定義時所在的對象
//普通函數 var obj = { say: function () { setTimeout(function() { console.log(this) }); } } obj.say();//Window object
// 箭頭函數 var obj = { say: function () { setTimeout(() => { console.log(this) }); }, test:123 } obj.say(); // obj
參數擴展
ES6 引入 rest 參數(形式爲...變量名),用於獲取函數的多餘參數,這樣就不須要使用arguments對象了。rest 參數搭配的變量是一個數組,該變量將多餘的參數放入數組中。
function show (a,b,...args){ console.log(a);//1 console.log(b);//2 console.log(args);//[3,4,5,6] } show(1,2,3,4,5,6);
下面是一個 rest 參數代替arguments變量的例子。
// arguments變量的寫法 function sortNumbers() { return Array.prototype.slice.call(arguments).sort(); } // rest參數的寫法 const sortNumbers = (...numbers) => numbers.sort();
展開後的效果,跟直接把數組內容寫在這同樣
let arr=[1,2,3]; console.log(1,2,3); console.log(...arr); //1,2,3同等於...arr
function show(a,b,c){ console.log(a) console.log(b) console.log(c) } let arr=[1,2,3]; show(1,2,3); show(...arr); //1,2,3同等於...arr
let arr1=[1,2,3]; let arr2=[4,5,6]; let arr=[...arr1,...arr2]; let arrS=[1,2,3,4,5,6]; //...arr1,寫法,至關於將數組內容掏出來內容
默認參數
function show(a,b=5,c=6){ //我但願b,默認是5 不傳的時候 //我但願c,默認是6 不傳的時候 console.log(a,b,c);//1,2,6 } show(1,2);
容許按照必定模式,從數組和對象中提取值,對變量進行賦值,這被稱爲解構。好比:
var [a,b] = [1,2] // a=1 b=2
let [a, b] = [1, 2] // 左右都是數組,能夠解構賦值 let {a, b} = {a:1, b:2} // 左右都是對象,能夠解構賦值 let [obj, arr] = [{a:1}, [1, 2]] // 左右都是對象,能夠解構賦值 let [a, b] = {a:1, b:2} // err 左右結構不同,不能夠解構賦值 let {a,b} = {1, 2} // err 右邊不是一個合法的數據,不能解構賦值 let [a, b]; [a, b] = [1, 2] // err 聲明與賦值分開,不能解構賦值
數組擴展了4個方法:map、reduce、filter、forEach
經過指定函數處理數組的每一個元素,並返回處理後的數組。
一個對一個
[12,58,99,86,45,91] [不及格,不及格,及格,及格,不及格,及格] //將數組映射成另外一個數組
[45,57,135,28] //將用戶id映射成對象 [ {name:'huang',role:1}, {name:'huang1',role:2}, {name:'huang2',role:3}, {name:'huang4',role:1} ]
map例子
let arr=[12,5,8]; //我想要將數組內容乘與2的結果 let result=arr.map(function(item){ console.log(item); //須要將你要的內容返回出來 return item*2; }); console.log(arr);//[12, 5, 8] console.log(result);//[24, 10, 16]
簡寫1
let arr=[12,5,8]; //我想要將數組內容乘與2的結果 let result=arr.map(item=>{ console.log(item); //須要將你要的內容返回出來 return item*2; }); console.log(arr);//[12, 5, 8] console.log(result);//[24, 10, 16]
簡寫2
let arr=[12,5,8]; //我想要將數組內容乘與2的結果 let result=arr.map(item=>item*2); console.log(arr);//[12, 5, 8] console.log(result);//[24, 10, 16]
let arr=[12,58,99,86,45,91] let result=arr.map(item=>item>=60?'及格':'不及格'); console.log(arr);//[12, 58, 99, 86, 45, 91] console.log(result);//["不及格", "不及格", "及格", "及格", "不及格", "及格"]
將數組元素計算爲一個值(從左到右)。
一堆出一個
算個總數
let arr=[12,58,99,86,45,91] /** * [description] * @param {[type]} (total,currentValue,index,arr [ * 初始值, 或者計算結束後的返回值。 * 當前元素 * 當前元素的索引 * 當前元素所屬的數組對象。 * ] * @return {[type]} [返回計算結果] */ let result=arr.reduce((total,currentValue,index,arr)=>{ return total+currentValue; }); console.log(result)//391
算個平均數
let arr=[12,58,99,86,45,91] /** * [description] * @param {[type]} (total,currentValue,index,arr [ * 初始值, 或者計算結束後的返回值。 * 當前元素 * 當前元素的索引 * 當前元素所屬的數組對象。 * ] * @return {[type]} [返回計算結果] */ let result=arr.reduce((total,currentValue,index,arr)=>{ if(index!=arr.length-1){ //若是不是最後一次 return total+currentValue; //求和 }else{ //最後一次 return (total+currentValue)/arr.length; //求和再除於長度個數 } }); console.log(result)//65.16666666666667
檢測數值元素,並返回符合條件全部元素的數組。
定義和用法
filter() 方法建立一個新的數組,新數組中的元素是經過檢查指定數組中符合條件的全部元素。
注意: filter() 不會對空數組進行檢測。
注意: filter() 不會改變原始數組。
需求,能被3整除的留下,不能的去除
let arr=[12,58,99,86,45,91] //需求,能被3整除的留下,不能的去除 /** * [description] * @param {[type]} (currentValue,index,arr [ * 當前元素的值 * 當前元素的索引值 * 當前元素屬於的數組對象 * ] * @return {[type]} [返回數組,包含了符合條件的全部元素。若是沒有符合條件的元素則返回空數組。] */ let result=arr.filter((currentValue,index,arr)=>{ if(currentValue%3==0){ return true; }else{ return false; } }); console.log(result)//[12, 99, 45]
簡寫
let arr=[12,58,99,86,45,91] //需求,能被3整除的留下,不能的去除 /** * [description] * @param {[type]} (currentValue,index,arr [ * 當前元素的值 * 當前元素的索引值 * 當前元素屬於的數組對象 * ] * @return {[type]} [返回數組,包含了符合條件的全部元素。若是沒有符合條件的元素則返回空數組。] */ let result=arr.filter((currentValue,index,arr)=>{ return currentValue%3==0; }); console.log(result)//[12, 99, 45]
再次簡寫
let arr=[12,58,99,86,45,91] let result=arr.filter(currentValue=>currentValue%3==0); console.log(result)//[12, 99, 45]
數組每一個元素都執行一次回調函數。
forEach() 方法用於調用數組的每一個元素,並將元素傳遞給回調函數。
注意: forEach() 對於空數組是不會執行回調函數的。
多了兩個新方法
startsWith應用
let str='http://blog.huanghanlian.com' if(str.startsWith('http://')){ console.log('普通網址'); }else if(str.startsWith('https://')){ console.log('加密網址'); }else if(str.startsWith('git://')){ console.log('git網址'); }else if(str.startsWith('svn://')){ console.log('svn網址'); }else{ console.log('其餘') }
let str='http://blog.huanghanlian.com/sitemap.xml' if(str.endsWith('.xml')){ console.log('網站地圖'); }else if(str.endsWith('.jpg')){ console.log('圖片'); }else if(str.endsWith('.txt')){ console.log('文本文件'); }else{ console.log('其餘') } //網站地圖
字符串模板
模板字符串有兩個能力
日常寫字符串有兩種寫法,
let str="abc"; let str2='efg';
一種是單引號,一種是雙引號。在js裏都能用。沒什麼區別
如今出來一種新的字符串
let str=`abc`;
這種符號叫反單引號
簡單使用例子
let a=12; let str=`a${a}bc`; console.log(str);//a12bc
反單引號中的美圓符號帶上花括號他的做用就是把變量直接塞進字符串裏面去。
例子
之前字符串拼接
let title='我是標題'; let content='我是內容'; let str='<div class="wrap">\ <h1>'+title+'</h1>\ <p>'+content+'</p>\ </div>'; document.body.innerHTML=str; console.log(str);
有了字符串模板後的寫法
let title='我是標題'; let content='我是內容'; let str='<div class="wrap">\ <h1>'+title+'</h1>\ <p>'+content+'</p>\ </div>'; let str2=`<div class="wrap"> <h1>${title}</h1> <p>${content}</p> </div>`; document.body.innerHTML=str2; console.log(str2);
面向對象
es5面向對象
function User(name,pass){ this.name=name; this.pass=pass; } //給這個類加原型方法 /** * [showName 獲取用戶名] * @return {[type]} [返回用戶名] */ User.prototype.showName=function(){ console.log(this.name); return this.name; }; /** * [showPass 獲取用戶密碼] * @return {[type]} [返回用戶密碼] */ User.prototype.showPass=function(){ console.log(this.pass); return this.pass; }; var ul=new User('黃繼鵬','abc'); //調用類方法 ul.showName();//黃繼鵬
這樣寫的缺點
ES6 提供了更接近傳統語言的寫法,引入了 Class(類)這個概念,做爲對象的模板。經過class關鍵字,能夠定義類。 先看如何定義一個class類:
class User { constructor(name) { // 構造器,至關於es5中的構造函數 this.name = name // 實例屬性 } showName(){ // 定義類的方法,不能使用function關鍵字,不能使用逗號分隔 console.log(this.name) } } var foo = new User('黃繼鵬') foo.showName();//黃繼鵬
其實class的基本類型就是函數(typeof User = "function"),既然是函數,那麼就會有prototype屬性。
類的全部方法都是定義在prototype上
class User { constructor() { // ... } toString() { // ... } toValue() { // ... } } User.toValue() // err User.toValue is not a function User.prototype.toValue() // 能夠調用toValue方法 // 等同於 User.prototype = { constructor() {}, toString() {}, toValue() {}, };
//定義類 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } var point = new Point(2, 3); point.toString() // (2, 3) point.hasOwnProperty('x') // true point.hasOwnProperty('y') // true point.hasOwnProperty('toString') // false point.proto.hasOwnProperty('toString') // true
若是在類中定義一個方法的前面加上static關鍵字,就表示定義一個靜態方法,靜態方法不會被實例繼承,但會被子類繼承,因此不能經過實例使用靜態方法,而是經過類直接調用
class User { constructor(name){ this.name = name } static show(){ console.log('123') } } class VipUser extends User{} VipUser.show() // 123 User.show() // 123 var foo = new User('foo') foo.show() // foo.show is not a function
class User{} User.name = 'foo' // 爲class定義一個靜態屬性 class VipUser extends User{} console.log(VipUser.name) // foo var foo = new User() console.log(foo.name) // undefined
es6是不支持私有屬性和私有方法,可是平常需求可能會用到私有屬性和私有方法,因此目前有一些提案,不過只是提案,還沒有支持。
類的繼承
ES5寫法
function User(name,pass){ this.name=name; this.pass=pass; } User.prototype.showName=function(){ console.log(this.name); }; /** * [showPass 獲取用戶密碼] * @return {[type]} [返回用戶密碼] */ User.prototype.showPass=function(){ console.log(this.pass); }; //繼承user類 function aUser(name, pass, type) { User.call(this, name, pass); this.type = type; }; aUser.prototype.showType = function() { console.log(this.type); }; var ul=new User('黃繼鵬','abc'); ul.showName()//黃繼鵬 var ull=new aUser('繼小鵬','ccc','男'); ul.showName();//繼小鵬 ull.showType();//男 //aUser繼承類User類,而且有本身的方法
class經過extends關鍵字實現繼承:
class User { constructor(name){ this.name = name } show(){...} } class VipUser extends User{ constructor(vipName){ // 子類的構造器 super(vipName) // 調用父類的constructor。至關於User.prototype.constructor.call(this,vipName) } showVip(){...} } var v = new VipUser('foo') // 建立實例 v instanceof VipUser // v是子類VipUser的實例 v instanceof User // v仍是父類User的實例
super能夠當作函數使用,也能夠當作對象使用。
當作函數使用
super做爲函數調用時,表明父類的構造函數,就是在子類的構造器中執行父類的constructor函數以獲取父類的this對象,由於子類沒有本身的this對象,因此ES6規定子類必須在constructor中執行一次super函數。super()函數只能在子類的constructor中執行,不能在其餘地方執行。
雖然super表明父類的構造器,可是super()在執行時內部的this指向子類,因此super()就至關於User.prototype.constructor.call(this)。
當作對象使用
super能夠做爲對象調用父類的屬性和方法,在子類的普通方法中,指向父類的原型對象(即User.prototype);在子類的靜態方法中,指向父類。
class User { constructor(){ this.x = 'hello' } show() { return 2; } } class VipUser extends User { constructor() { super(); console.log(super.show()); // 2 此時super指向User.prototype,至關於User.prototype.show() console.log(super.x) // undefined 沒法訪問實例屬性 } } let vip = new VipUser(); console.log(vip.x);//hello
因爲super對象在普通函數中使用super指向User.prototype,因此super只能訪問父類的原型上的方法,無法訪問父類的實例屬性和實例方法。
ES6規定若是在子類中使用super對象調用父類的方法時,方法內部的this指向子類
class User { constructor() { this.x = 1 } show() { return this.x; } } class VipUser extends User { constructor() { super(); this.x = 2 console.log(super.show()) // 2 此時show()方法內部的this指向子類,因此輸出2,而不是1 } } let vip = new VipUser();
上述代碼中雖然super.show()調用的是User.prototype.show(),可是因爲經過super對象調用父類方法時,方法內部的this指向子類,因此super.show()至關於 super.show().call(this),也就是User.prototype.show().call(this)
在子類的靜態方法中super對象指向父類,而不是父類的原型(User.prototype)。
class User { constructor() { this.x = 1 } static fn() { console.log('父類靜態方法') } } class VipUser extends User { constructor() { super(); this.x = 2 } static childFn() { super.fn() // 至關於User.fn() } } VipUser.childFn()
在es5中每個對象都有proto屬性,指向對應的構造函數的prototype屬性。Class 做爲構造函數的語法糖,同時有prototype屬性和proto屬性,所以同時存在兩條繼承鏈。
class User { } class VipUser extends User { } VipUser.proto === User // true VipUser.prototype.proto === User.prototype // true
子類實例的proto屬性指向子類的原型(子類的prototype),子類實例的proto屬性的proto屬性指向父類的原型(父類的prototype)
class User { } class VipUser extends User { } var vip = new VipUser() console.log(vip.proto === VipUser.prototype) // true console.log(vip.proto.proto === User.prototype) // true
面向對象應用---react
react介紹:
在react裏一個組件就是一個class,
jsx==babel==browser.js
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script> <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script> <script type="text/babel"> let oDiv=document.getElementById('div1'); ReactDOM.render( <span>123</span>, oDiv ); </script> </head> <body> <div id="div1"></div> </body> </html>
ReactDOM.render( <span>123</span>, oDiv );
這種語法爲何會支持呢?
這個就是jsx和普通js最大的差異。
你能夠認爲jsx是普通js的擴展版本
既然是擴展版本,那確定會多出一些功能來。
若是不寫引號,不是字符串同時長得像html,他就是能夠要建立一個標籤
切換搭配重點
react是一個基於組件
組件與class形式存在
我想寫一個組件,做爲一個組件是否是應該有一些基本的功能,好比我能被渲染,我有一些狀態,我有生命週期,換句話說我如今不是從零開始寫一個class,我須要不少基礎的類的集成。
class Test extends React.Component{ }
類繼承最大的意義在於一切不用從零開始
一個類須要有構造函數constructor
.
做爲繼承類,在構造函數中須要繼承父級的屬性
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script> <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script> <script type="text/babel"> class Test extends React.Component{ //構造函數接到任何參數,都給繼承父級類 constructor(...args){ super(...args); } render(){ return <div>hello world</div> } } let oDiv=document.getElementById('div1'); ReactDOM.render( <Test>123</Test>, oDiv ); </script> </head> <body> <div id="div1"></div> </body> </html>
組件套組件方法例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script> <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script> <script type="text/babel"> class Item extends React.Component{ //構造函數接到任何參數,都給繼承父級類 constructor(...args){ super(...args); } render(){ return <div>{this.props.str}</div> } } class List extends React.Component{ //構造函數接到任何參數,都給繼承父級類 constructor(...args){ super(...args); } render(){ /*//寫法1 let aItem=[]; for(let i=0;i<this.props.arr.length;i++){ aItem.push(<Item key={i} str={this.props.arr[i]}></Item>); }*/ /*// 寫法2 let aItem=this.props.arr.map((str,index)=>{ return <Item key={index} str={str}></Item> }) return <div> {aItem} </div>*/ // 寫法3 return <div> { this.props.arr.map((str,index)=>{ return <Item key={index} str={str}></Item> }) } </div> } } let oDiv=document.getElementById('div1'); ReactDOM.render( <List arr={['abc','efg','hij']}></List>, oDiv ); </script> </head> <body> <div id="div1"></div> </body> </html>
Promise的中文含義是承諾
瞭解Promise以前。先來了解下同步異步
異步:操做之間沒啥管系,同時進行多個操做
同步:同時只能作一件事
同步異步的優缺點
異步:代碼更復雜
同步:代碼簡單
一個頁面可能會有多個請求
好比淘寶網頁,banner區域,側邊欄,導航欄,右側欄,信息商品等
都是由鍍鉻接口異步請求組成
這就回形成代碼邏輯複雜
按照以往前端ajax請求寫法。一個請求成功後繼續請求嵌套。邏輯會變得異常費勁
異步
$.ajax({ type: 'post', url: '/api/banner', success:function(result){ console.log('成功'); $.ajax({ type: 'post', url: '/api/1', success:function(result){ console.log('成功'); $.ajax({ type: 'post', url: '/api/banner', success:function(result){ console.log('成功'); $.ajax({ type: 'post', url: '/api/banner', success:function(result){ console.log('成功') }, error:function(error){ console.log('失敗') }, }) }, error:function(error){ console.log('失敗') }, }) }, error:function(error){ console.log('失敗') }, }) }, error:function(error){ console.log('失敗') }, })
同步
let banner_data=ajax_async('/banner'); let banner_data1=ajax_async('/banner1'); let banner_data2=ajax_async('/banner2'); let banner_data3=ajax_async('/banner3'); let banner_data4=ajax_async('/banner4');
你會發現異步處理性能好,用戶體驗好,但實際代碼複雜
要是同步方式頁面用戶體驗很差
這個時候幻想一下,我能不能像同步方式來寫代碼。也像異步同樣請求數據。
Promise就能作到這個工做
Promise--消除異步操做
Promise如何使用
須要使用promise的時候,你須要new一個promise對象。
這個對象接收一個參數,是一個函數。
將異步的代碼寫在函數裏
這個函數兩個參數resolve
決心reject
拒絕
//封裝Promise ajax let p=new Promise(function(resolve,reject){ //異步代碼塊 //resolve--成功了 //reject--失敗了 $.ajax({ type: 'post', dataType:'json', url: '/api/banner', success:function(result){ resolve(result); }, error:function(error){ reject(error); }, }) }); //使用Promise ajax封裝 //當Promise調用有結果了就會調用then //then有兩個參數,都是函數,第一個是resolve,第二個是reject p.then((result)=>{ console.log(result); },(error)=>{ console.log(error); })
function createPromise(url){ return new Promise(function(resolve,reject){ //異步代碼塊 //resolve--成功了 //reject--失敗了 $.ajax({ type: 'post', dataType:'json', url, success:function(result){ resolve(result); }, error:function(error){ reject(error); }, }) }); } createPromise('./aa') .then((res)=>{ console.log(res) },(err)=>{ console.log(err) })
function createPromise(url){ return new Promise(function(resolve,reject){ //異步代碼塊 //resolve--成功了 //reject--失敗了 $.ajax({ type: 'post', dataType:'json', url, success:function(result){ resolve(result); }, error:function(error){ reject(error); }, }) }); } Promise.all([ createPromise('./aa'), createPromise('./bb') ]) .then((res)=>{ let [arr1,arr2]=res },(err)=>{ console.log(err) })
generator的做用
generator-生成器
生成器是程序裏面的一個概念,能夠依靠它生成一堆東西
Generator能夠理解爲生成器,和普通函數沒多大區別,普通函數是隻要開始執行,就一直執行到底,而Generator函數是中間能夠停,搭配使用next函數繼續執行。
生動的比喻
普通函數比如坐飛機,飛機起飛,不到目的地中途是不會降落的
Generator比如於出租車。能夠隨叫隨停。停了再走,走了再停
function * fn(){ alert('a') yield alert('b') } var f = fn() f.next() // a f.next() // b
直接調用Generator函數,是什麼都不執行的,調用第一個next()纔開始執行,一直執行到第一個yield中止,第二次調用next(),從第一個yield執行到第二個yield中止,依次類推
如今疑惑,在真實場景中,我爲何要讓一個函數停呢?
剛纔舉個了出租車的例子,說白了,你爲何要讓出租車司機停車,確定是你有事情,你要去忙,或者要求拿什麼東西,或者見什麼朋友。
等你事情辦完了,還再回來。
因此Generator特別適合一個場景。
好比說你要請求數據。請求數據不是瞬間就能回來的,這個時候就須要暫停,來等他結果過來。再繼續執行下面的操做。
/** * 普通函數在執行過程當中須要請求獲得結果再執行對應代碼,就會出現代碼嵌套再嵌套 */ function 函數(){ 代碼... ajax({ 代碼... }) } /** * Generator函數可讓代碼在那一步暫時暫停 拿到數據後再繼續往下走 */ function *函數(){ 代碼... yiels ajax(xxx) 代碼... }
Generator是怎麼作到走走停停的?
其實本質是用Generator函數生成了一堆小函數
比方說fn函數
function * fn(){ alert('a') yield alert('b') } var f = fn() f.next() // a f.next() // b
其實他在背後生成了兩個小函數
function fn_1(){ alert('a') } function fn_2(){ alert('b') }
固然這個過程咱們是看不見的
至關於把一個大函數切分紅了兩個小函數
第一次next的時候他走的是fn_1
第二次next的時候走的是fn_2
yield和next
yield表明暫時暫停執行,next表明繼續執行。
yield和next能夠傳參數,也能夠有返回值
function *show(){ console.log('a') let a=yield; console.log('b') console.log(a) } let gen=show(); gen.next(12) gen.next(5) //a //b //5
第一次執行next的時候執行黃色框代碼
第二次執行紅色框的代碼
傳參的時候經過yield來傳參的時候,第一個next是無效的,
若是想給第一個過程傳參須要使用傳統方法,在使用函數時傳參
function *show(num1,num2){ console.log(`${num1},${num2}`) console.log('a') let a=yield; console.log('b') console.log(a) } let gen=show(11,12); gen.next(12);//無法給yield傳參 gen.next(5) //11,12 //a //b //5
function *show(){ console.log('a') let a=yield 12; console.log('b') } let gen=show(11,12); let res1=gen.next(); console.log(res1) let res2=gen.next() console.log(res2) //a //{value: 12, done: false} //b //{value: undefined, done: true}
value是yield 返回的參數
done代碼函數是否走完
爲何第二次執行完value是空
由於第二次next是執行的最後一道程序,最後一道程序就沒有yield 了,若是想返回東西須要使用return
function *show(){ console.log('a') let a=yield 12; console.log('b') return 111; } let gen=show(11,12); let res1=gen.next(); console.log(res1) let res2=gen.next() console.log(res2) //a //{value: 12, done: false} //b //{value: 111, done: true}
yield 究竟是個啥
這種Generator函數適用多個異步請求之間有邏輯分析的狀況,好比有一個需求,先請求用戶數據,根據用戶數據的類型判斷用戶是普通用戶仍是VIP用戶,而後再根據判斷結果請求普通商品數據或者VIP商品數據
// 藉助runner腳本,runner腳本規定Generator函數執行完一個next以後自動執行下一個next runner(function() * (){ let userData = yield $.ajax(...) // 請求用戶數據 if(userData.type === 'vip') { let goods = yield $.ajax(...) // 請求vip商品數據 } else { let goods = yield $.ajax(...) // 請求普通商品數據 } })
第一次yield ajax實際上是Promise對象,將Promise對象yield 出去。
yield 給了runner對象
將數據請求完成給data1
這個函數暫停了
使用Generator函數使得代碼看起來更像同步代碼,其實使用Promise一樣能夠實現這種效果,只不過得須要在then()函數中嵌套請求。
異步請求的幾種方式
$.ajax({ type: 'post', url: '/api/banner', success:function(result){ console.log('成功'); $.ajax({ type: 'post', url: '/api/1', success:function(result){ console.log('成功'); $.ajax({ type: 'post', url: '/api/banner', success:function(result){ console.log('成功'); $.ajax({ type: 'post', url: '/api/banner', success:function(result){ console.log('成功') }, error:function(error){ console.log('失敗') }, }) }, error:function(error){ console.log('失敗') }, }) }, error:function(error){ console.log('失敗') }, }) }, error:function(error){ console.log('失敗') }, })
function createPromise(url){ return new Promise(function(resolve,reject){ //異步代碼塊 //resolve--成功了 //reject--失敗了 $.ajax({ type: 'post', dataType:'json', url, success:function(result){ resolve(result); }, error:function(error){ reject(error); }, }) }); } Promise.all([ createPromise('./aa'), createPromise('./bb') ]) .then((res)=>{ let [arr1,arr2]=res },(err)=>{ console.log(err) })
runner(function() * (){ let userData = yield $.ajax(...) // 請求用戶數據 if(userData.type === 'vip') { let goods = yield $.ajax(...) // 請求vip商品數據 } else { let goods = yield $.ajax(...) // 請求普通商品數據 } })
Promise和Generator相比,Generator並無特別的省事
Promise也有它不適用的地方。我若是是寫死要請求接口。那麼Promise和Generator確實沒太大區別,
Generator他的優勢在於適合參雜一些邏輯
比方說在請求一個接口拿到用戶信息,根據信息判斷他該去請求哪些不一樣的接口
感受比普通嵌套還麻煩
帶邏輯-Generator
// 藉助runner腳本,runner腳本規定Generator函數執行完一個next以後自動執行下一個next runner(function() * (){ let userData = yield $.ajax(...) // 請求用戶數據 if(userData.type === 'vip') { let goods = yield $.ajax(...) // 請求vip商品數據 } else { let goods = yield $.ajax(...) // 請求普通商品數據 } })
Promise適合一次請求一堆場景
Generator適合邏輯性請求處理
KOA是nodejs的框架
async其實就是對Generator的封裝,只不過async能夠自動執行next()。
async function read () { let data1= await new Promise(resolve => { resolve('100') }) let data2 = await 200 return 300 }
async默認返回一個Promise,若是return不是一個Promise對象,就會被轉爲當即resolve的Promise,能夠在then函數中獲取返回值。
async必須等到裏面全部的await執行完,async纔開始return,返回的Promise狀態才改變。除非遇到return和錯誤。
async function fn () { await 100 await 200 return 300 } fn().then(res => { console.log9(res) // 300 })
await也是默認返回Promise對象,若是await後面不是一個Promise對象,就會轉爲當即resolve的Promise
若是一個await後面的Promise若是爲reject,那麼整個async都會中斷執行,後面的awiat都不會執行,而且拋出錯誤,能夠在async的catch中捕獲錯誤
async function f() { await Promise.reject('error'); await Promise.resolve('hello world'); // 不會執行 } f().then(res =>{ }).catch(err=>{ console.log(err) // error })
若是但願一個await失敗,後面的繼續執行,可使用try...catch或者在await後面的Promise跟一個catch方法:
// try...catch async function f() { try { await Promise.reject('出錯了'); } catch(e) { } return await Promise.resolve('hello world'); } f() .then(v => console.log(v)) // hello world // catch async function f() { await Promise.reject('出錯了') .catch(e => console.log(e)); // 出錯了 return await Promise.resolve('hello world'); } f() .then(v => console.log(v)) // hello world