摘要:在年正式發布了,簡稱,又稱為。再次簡寫循環迭代數組每個元素都執行一次回調函數。方法用于調用數組的每個元素,并將元素傳遞給回調函數。注意對于空數組是不會執行回調函數的。
轉載請注明出處
原文連接 http://blog.huanghanlian.com/article/5c7aa6c7bf3acc0864870f9d
es6 是什么首先弄明白ECMA和js的關系。ECMA是標準,Javascript是ECMA的實現。因為js也是一種語言,但凡語言都有一套標準,而ECMA就是javascript的標準。 在2015年正式發布了ECMAscript6.0,簡稱ES6,又稱為ECMAscript2015。
歷史
ECMAScript和Javascript
ECMA是標準,JS是實現
類似于HTML5是標準,IE10,Chrome,FF都是實現
換句話說,將來也能有其他XXXScript來實現ECMA
ECMAScript簡稱ECMA或ES
目前版本
低級瀏覽器主要支持ES 3.1
高級瀏覽器正在從ES 5過度ES 6
歷史版本
時間 | 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 因為吵得太厲害,所以ES3.1代號為Harmony(和諧) |
|
2009.12 | ES 5.0 | ES5正式發布 同時公布了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預計發布正式版 JavaScript.next開始指向ES 7.0 |
es5兼容性
http://kangax.github.io/compat-table/es5/
es6兼容性
http://kangax.github.io/compat-table/es6/
ES6(ES2015)-- IE10+,Chrome,FireFox,移動端,NodeJS。這些環境基本上都是認得,都能兼容
但是有需求兼容ie怎么辦
有兩種辦法
比方說在移動端或者是混合開發當中,多去用用ES6,在老的版本中不用。惹不起咋躲得起。編譯,轉換
在線轉換
簡單來說就是寫好了ES6了然后引用一個js庫進來。我什么也不用做了。它替我去做了各種各樣的事情
缺點,用戶每次打開頁面都要重新轉換一遍,性能體驗不是很好。
提前編譯
ES6的到底有什么樣的東西?
變量(對原有的變量做了修改)
函數(對原有的函數也做了修改)
數組(對數組做了一些改進)
字符串(改進)
面向對象
Promise(串行化的異步請求方式)
yield && generator(generator是專門把同步操作拆成異步操作,generator是對Promise的一個封裝)
模塊化
變量-let和const回顧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
不能重復聲明,let是變量,可以修改,塊級作用域
塊級作用域
可修改let變量的值
const
不可重復聲明,const常量,不能修改,塊級作用域
塊級作用域
不可修改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也不例外。
塊級作用域有什么
Document
以上代碼執行,不管按哪個按鈕彈出都是3
由于var聲明變量只在函數作用域中擴散到全局
在for或者if快級作用域中聲明的變量會在局部或全局生效
當for循環執行完畢,i這個變量暴露到全局了,等于3
所以for循環中執行的事件綁定,是將點擊事件回調函數執行。當點擊按鈕時候,會出發綁定回調函數,此時當前作用域中,i等于3,所以無論點擊哪個按鈕彈出都是3
以前我們是通過閉包解決這個問題
Document
在每一層循環的時候,用一個匿名函數而且是立即執行的匿名函數給他包裝起來,然后將每一次遍歷的1.2.3分別的值去傳到這個匿名函數里,然后匿名函數接到這個參數i再放到點擊事件中去引用i當我們每次點擊事件輸出的值i就會取每一個閉包環境下的i。所以這樣就能達到效果。
使用let來實現
Document
for循環本身就是一個語法塊,自身就是一個塊
由于var只把函數作為作用域
所以以上需要通過立即執行函數來包一層,來實現效果。
而let本身是支持塊級作用域的,所以電腦按鈕執行回掉函數,打印i,是當前塊級作用域下的i
這個i在非for塊作用域下是未定義的。
函數-箭頭函數箭頭函數在寫法上對es5做了一些修整,代碼看起來更顯得簡潔
如果只有一個參數,圓括號"()"可以省略
函數體如果只有一句return語句,花括號也可以省略
// 定義一個箭頭函數 let a = (arg)=>{ // 這里=>符號就相當于function關鍵字 return arg+=1 } // 也可以簡寫為 let a = arg => arg+=1
箭頭函數的作用跟以前接觸的函數沒什么本質的區別,更多的是一種寫法上的變化。
function show() { } 同等于 let show =()=>{ }
function () { } 同等于 ()=>{ }
Document 同等于Document
箭頭函數也對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
map 映射
通過指定函數處理數組的每個元素,并返回處理后的數組。
一個對一個
[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);//["不及格", "不及格", "及格", "及格", "不及格", "及格"]
reduce 匯總
將數組元素計算為一個值(從左到右)。
一堆出一個
算個總數
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() 不會對空數組進行檢測。
注意: 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() 方法用于調用數組的每個元素,并將元素傳遞給回調函數。
注意: forEach() 對于空數組是不會執行回調函數的。
字符串多了兩個新方法
字符串模板
多了兩個新方法
startsWith() 表示參數字符串是否在原字符串的頭部,返回布爾值
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("其他") }
endsWith() 表示參數字符串是否在原字符串的尾部,返回布爾值
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("其他") } //網站地圖
includes() 表示是否在原字符串找到了參數字符串,返回布爾值
字符串模板
模板字符串有兩個能力
能直接把變量塞到字符串中去。
可以折行
平常寫字符串有兩種寫法,
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=""; document.body.innerHTML=str; console.log(str);"+title+"
"+content+"
有了字符串模板后的寫法
let title="我是標題"; let content="我是內容"; let str="面向對象-基礎"; let str2=`"+title+"
"+content+"
`; document.body.innerHTML=str2; console.log(str2);${title}
${content}
面向對象
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();//黃繼鵬(1)constructor
es6中class類專用的構造器,相當于之前定義的構造函數,每個類都必須有constructor,如果沒有則自動添加一個空的constructor構造器。
創建實例的時候自動執行constructor函數
constructor中的this指向實例,并且默認返回this(實例)
(2)class類的prototype其實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() {}, };(3)類的實例
類的實例只能通過new來創建
除了靜態方法,定義在類上的所有的方法都會被實例繼承
除非定義在類的this對象上才是實例屬性,否則都是定義在類的原型(prototype)上
//定義類 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(4)靜態方法
如果在類中定義一個方法的前面加上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(5)靜態屬性
class的靜態屬性指的是 Class 本身的屬性,目前只能通過Class.propName定義靜態屬性
靜態屬性可以被子類繼承,不會被實例繼承
class User{} User.name = "foo" // 為class定義一個靜態屬性 class VipUser extends User{} console.log(VipUser.name) // foo var foo = new User() console.log(foo.name) // undefined(6)私有屬性和私有方法
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的實例(1)super
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()(2)類的prototype和proto屬性
在es5中每一個對象都有proto屬性,指向對應的構造函數的prototype屬性。Class 作為構造函數的語法糖,同時有prototype屬性和proto屬性,因此同時存在兩條繼承鏈。
子類的proto屬性,表示構造函數的繼承,總是指向父類。
子類prototype屬性的proto屬性,表示方法的繼承,總是指向父類的prototype屬性。
class User { } class VipUser extends User { } VipUser.proto === User // true VipUser.prototype.proto === User.prototype // true(3)實例的proto屬性
子類實例的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
jsx==babel==browser.js
ReactDOM.render(
123,
oDiv
);
這種語法為什么會支持呢?
這個就是jsx和普通js最大的差別。
你可以認為jsx是普通js的擴展版本
既然是擴展版本,那肯定會多出一些功能來。
如果不寫引號,不是字符串同時長得像html,他就是可以要創建一個標簽
切換搭配重點
react是一個基于組件
組件與class形式存在
我想寫一個組件,作為一個組件是不是應該有一些基本的功能,比如我能被渲染,我有一些狀態,我有生命周期,換句話說我現在不是從零開始寫一個class,我需要很多基礎的類的集成。
class Test extends React.Component{ }
類繼承最大的意義在于一切不用從零開始
一個類需要有構造函數constructor.
作為繼承類,在構造函數中需要繼承父級的屬性
組件套組件方法例子
Promise
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可以理解為生成器,和普通函數沒多大區別,普通函數是只要開始執行,就一直執行到底,而Generator函數是中間可以停,搭配使用next函數繼續執行。
生動的比喻
普通函數好比坐飛機,飛機起飛,不到目的地中途是不會降落的
Generator好比于出租車??梢噪S叫隨停。停了再走,走了再停
(1)定義一個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
generator-yieldyield和next
yield代表暫時暫停執行,next代表繼續執行。
yield和next可以傳參數,也可以有返回值
yield可以傳參
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
yield返回
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-實例:runner這種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("失敗") }, })
Promise寫法
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寫法
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 awaitasync其實就是對Generator的封裝,只不過async可以自動執行next()。
async function read () { let data1= await new Promise(resolve => { resolve("100") }) let data2 = await 200 return 300 }async 返回值
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
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
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/109179.html
摘要:在之前,中沒有常量聲明方式,有的僅僅是一種命名上的約定。這樣就可以最大程度上規避變量的意外變化來使程序發生異常的可能性。為了避免作用域的污染,在中需要使用一個立即執行函數來確保變量的作用域范圍。 在 ES6 之前,JS 中沒有常量聲明方式,有的僅僅是一種命名上的約定。 var PI = 3.14; PI = 4; console.log(PI); // 4 我們用大寫變量名來標識這是一...
摘要:在年正式發布了,簡稱,又稱為。再次簡寫循環迭代數組每個元素都執行一次回調函數。方法用于調用數組的每個元素,并將元素傳遞給回調函數。注意對于空數組是不會執行回調函數的。 轉載請注明出處 原文連接 http://blog.huanghanlian.com/article/5c7aa6c7bf3acc0864870f9d es6 是什么 首先弄明白ECMA和js的關系。ECMA是標準,Jav...
摘要:以下,請求兩個,當兩個異步請求返還結果后,再請求第三個此處為調用后的結果的數組對于來說,只要參數數組有一個元素變為決定態,便返回新的。 showImg(https://segmentfault.com/img/remote/1460000015444020); Promise 札記 研究 Promise 的動機大體有以下幾點: 對其 api 的不熟悉以及對實現機制的好奇; 很多庫(比...
閱讀 3920·2021-11-17 09:33
閱讀 3282·2021-10-08 10:05
閱讀 3110·2021-09-22 15:36
閱讀 1139·2021-09-06 15:02
閱讀 2771·2019-08-29 12:45
閱讀 1589·2019-08-26 13:40
閱讀 3399·2019-08-26 13:37
閱讀 419·2019-08-26 13:37