promise知識點

Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強大署照。它由社區(qū)最早提出和實現(xiàn)吗浩,ES6 將其寫進了語言標準懂扼,統(tǒng)一了用法,原生提供了Promise對象赶熟。

promise的特點:

  1. 對象的狀態(tài)不會受到外界所影響陷嘴,Promise對象代表一次異步操作灾挨,只會有三種狀態(tài)Pending(進行中),Fulfilled(已成功)地技,Rejected(失斉彝痢)。只有異步操作的結(jié)果可以決定Promise對象的狀態(tài)狡相,任何其他操作都無法改變這個狀態(tài)尽棕。
  2. Promise對象的狀態(tài)變化只會有兩種可能:(1)Pending變成Fulfilled(2)Pending變成Rejected彬伦。只要這兩種情況任意一個發(fā)生单绑,Promise的狀態(tài)就會固定,不會再發(fā)生變化了歉提。
  3. 如果狀態(tài)已經(jīng)發(fā)生的改變苔巨,再給給期添加回調(diào)函數(shù)废离,依然可以得到結(jié)果蜻韭。這與event不同,事件一旦錯過就沒有了诀豁。
  4. 缺點: a. Promise一旦創(chuàng)建就會立即執(zhí)行,無法中途取消活翩。b. 如果不設置回調(diào)函數(shù),Promise內(nèi)部發(fā)生的錯誤沮焕,不會反應到外部峦树。c. Pending狀態(tài)時旦事,無法得知當前進展到哪一狀態(tài)姐浮。

基本用法

  1. 創(chuàng)建一個Promise實例;
var promise = new Promise(function (resolve, reject) {
  if(/*成功*/) {
    resolve(value);
  } else {
    reject(error);
  }
})

Promise實例創(chuàng)建完畢,我們需要通過then方法分別指定ResolvedRejected狀態(tài)的回調(diào)函數(shù)肾扰。

promise.then(function () {
  // success code
}, function () {
  // failure code
})

then 方法可以接受兩個參數(shù)集晚,第一個參數(shù)對應成功的回調(diào)函數(shù)区匣,第二個對應的是失敗的回調(diào)函數(shù)沉颂,并且第二個參數(shù)的可選的铸屉,不一定需要傳遞。

  1. Promise新建以后就會立即執(zhí)行顷啼。
var promise = new Promise(function (resolve, reject) {
  resolve();
  console.log('promise')
})
promise.then(function () {
  console.log('resolved')
})
console.log('hi');
// 執(zhí)行結(jié)果:
// promise
// hi
// resolved

then 方法指定的回調(diào)函數(shù)钙蒙,將在當前腳本所同步任務執(zhí)行完成才會執(zhí)行间驮,所以resolved最后輸出竞帽。

Promise.prototype.then()

Promise實例具有then方法,也就是then方法是定義在Promise的原型對象上的匙奴,他的作用就是添加狀態(tài)改變時的回調(diào)函數(shù)妄荔。
注意:then方法返回的是一新的Promise實例(不是原來的那個Promise實例),因此可以鏈式調(diào)用啦租。

var getJSON = function (url) {
  var promise = new Promise(function (resolve, reject) {
    var client = new XMLHttpRequest();
    client.open('GET', url);
    client.onreadystatechange = handler;
    client.responseType = 'json';
    client.setRequestHeader('Accept', 'application/json');
    client.send();
    function handler() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
  })
  return promise;
}
getJSON('/posts.json').then(function (json) {
  return json.post;
}).then(function(post){
  //post 是上一個回調(diào)函數(shù)傳過來的
})

上面的代碼使用then方法刷钢,依次指定了兩個回調(diào)函數(shù),第一個回調(diào)函數(shù)完成以后伴澄,會將返回結(jié)果作為參數(shù)非凌,傳入第二個回調(diào)函數(shù)荆针。

getJSON('/post/1.json').then(function(post){
  return getJSON('/post/2.json')
}).then(function(post){
    console.log('resloved')
}, function(err){
    console.log('rejected')
})

上面的代碼航背,第一個then方法返回了Promise對象玖媚,這時,第二個then指定的回調(diào)函數(shù)勺像,要等到這個Promise對象狀態(tài)改變時才會觸發(fā)吟宦。

Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, fn)的別名殃姓,用于指定發(fā)生錯誤的回調(diào)函數(shù)。

getJSON('/post.json').then(function(){
  // code
}).catch(function(err){
  console.log(err)
})

getJSON返回一個Promise對象锋叨,如果是成功狀態(tài)就會走then里面設置的回調(diào)函數(shù),如果失敗了就會走catch設置的回調(diào)函數(shù)叫倍,如果then指定的回調(diào)函數(shù)里面出錯了豺瘤,也會被catch所捕獲坐求,調(diào)用catch設置的回調(diào)函數(shù)處理桥嗤。

p.then(function(){
  //code
}).catch(function(){
  //code
})
// 等同于
p.then(function(){
  // code
}).then(null, function(){
  //code
})
// 第一種寫法
var promise = new Promise(function(resolve, reject){
  try {
    throw new Error('test')
  } catch (e) {
    reject(e)
  }
})
promise.catch(function(err){
  console.log(err)
})
// 第二種寫法
var promise = new Promise(function(resolve, reject){
  reject(new Error('test'))
})
promise.catch(function(error){
  console.log(error)
})

上面兩種寫法是等價的,可以看出reject方法作用荒吏,等同于拋出錯誤绰更。

var promise = new Promise(function(resolve, reject){
  resolve('ok');
  throw new Error('test')
})
promise.then(function(value){
  console.log(value)
}).catch(function(err){
  console.log(err)
})
// ok

Promiseresolve語句后面再拋出錯誤儡湾,是不會被catch捕獲的盒粮,等于沒有拋出奠滑。

getJSON('/post/1.json').then(function(post){
  return getJSON('/post/2.json')
}).then(function(json2){
  // code
}).catch(function(e){
  console.log(e)
  // 捕獲前面三個promise對象的錯誤
})

Promise對象的錯誤具有“冒泡”的性質(zhì)宋税,會向后面一直傳遞杰赛,直到被捕獲為止。

//不推薦的寫法
promise.then(function(data){
  //success
}, function(err){
  // failure
})
// 推薦寫法
promise.then(function(data){
  //success
}).catch(function(err){
  console.log(err)
})

第二種方法優(yōu)于第一種寫法根时,理由是第二種可以捕獲前面then 里面的錯誤蛤迎,語法頁更貼近(try/catch)替裆。

var someAsyncThing = function() {
  return new Promise(function() {
    resolve(x + 2)
  })
}
someAsyncThing().then(function() {
  console.log('..')
})

上面的代碼中,someAsynicThing函數(shù)產(chǎn)生的Promise對象會報錯宜咒,但是沒有指定catch方法這個錯誤是不會被捕獲的故黑,也不會被傳遞到外層的场晶。

var promise = new Promise(function(resolve, reject) {
  resolve('ok');
  setTimeout(function(){
    throw new Error('test')
  }, 0)
})
promise.then(function(value) {
  console.log(value);
}).catch(function(err) {
  console.log(err);
})
// ok
// VM672:4 Uncaught Error: test

Promise指定在下輪的事件循環(huán)里面在拋出一個錯誤峰搪,到那個時候Promise的運行已經(jīng)結(jié)束凯旭,所以這個錯誤會在函數(shù)體外拋出罐呼,冒泡到最外層成為未捕獲的錯誤嫉柴。

var someAsyncthing = function() {
  return new Promise(function(resolve, reject){
    // 將報錯,x沒有申明
    resolve(x + 2)
  })
}
someAsyncthing().catch(function(err){
  console.log('err:', err)
  return '123'
}).then(function(str){
  console.log('ok', str)
})
// err: ReferenceError: x is not defined
//    at <anonymous>:4:13
//    at Promise (<anonymous>)
//    at someAsyncthing (<anonymous>:2:10)
//    at <anonymous>:7:1
//ok 123

catch方法返回的還是一個Promise對象,因此后面還是可以接著調(diào)用then方法匙握。

var someAsyncthing = function() {
  return new Promise(function(resolve, reject){
    resolve(2)
  })
}
someAsyncthing().catch(function(err){
  console.log('err:', err)
  return '123'
}).then(function(str){
  console.log('ok', str)
  // x 未定義會報錯
  return x;
})
//ok 2
//ReferenceError: x is not defined
//    at <anonymous>:12:3
//    at <anonymous>

上面代碼圈纺,Promise里面沒有錯誤,就會跳過catch的方法灯谣,直接執(zhí)行下面的then方法蛔琅,then里面在出錯就與前面的catch無關了揍愁,錯誤也不會被捕獲莽囤。

var someAsyncthing = function() {
  return new Promise(function(resolve, reject){
    resolve(2)
  })
}
someAsyncthing().catch(function(err){
  console.log('err:', err)
// x 未定義
  return x
}).then(function(str){
  console.log('ok', str)
})
// ok 2

catch里面出了錯誤朽缎,因為后面沒有catch了话肖,導致這個錯誤不會被捕獲葡幸,也不會被傳遞到最外層蔚叨。

總結(jié): 為了盡可能多的捕獲到錯誤床蜘,應該將catch寫在所有then的最后面,這樣不僅可以捕獲到promise的錯誤蔑水,也能捕獲到前面then里面錯誤.

Promise.all()

Promise.all方法用于將多個Promise實例邢锯,包裝成一個新的Promise實例。

var p = Promise.all([p1, p2, p3])

Promise.all接受一個數(shù)組作為參數(shù)搀别,p1丹擎,p2p3都是Promise實例歇父,如果不是,就會先調(diào)用Promise.resolve方法榜苫,將其轉(zhuǎn)為Promise實例护戳。(Promise.all的參數(shù)可以不一定是數(shù)組,但一定是要實現(xiàn)Iterator接口的单刁,且返回的每個成員都是Promise實例)
p的狀態(tài)是由p1,p2,p3決定的:

  1. 只有p1,p2,p3的狀態(tài)都變成fulfilled灸异,p的狀態(tài)就變成fulfilled府适,此時p1,p2,p3的返回組成一個數(shù)組,傳遞給p的回調(diào)函數(shù)肺樟。
  2. 只有p1,p2,p3中有一個的狀態(tài)都變成rejected檐春,p的狀態(tài)就變成rejected,此時第一個變成rejected的實例的返回值么伯,傳遞給p的回調(diào)函數(shù)疟暖。
var a = function(){
    var t = ~~(Math.random()*1000)
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log(t)
            resolve(t)
        },t)
    })
}
var p1 = a(); var p2 = a();
Promise.all([p1,p2]).then(([p1,p2])=>{console.log(123, p1,p2)})
// 562 
// 879 
// 123 879 562 

上面p1p2是兩個異步的操作,只有等到兩個結(jié)果都返回了才會觸發(fā)Promise.allthen的方法

// 由于是隨機數(shù)要多試幾次
var a = function(){
    var t = ~~(Math.random()*1000)
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log(t)
            if(t < 500){
                reject(new Error('<500'))
            }
            resolve(t)
        },t)
    })
}
// 如果出錯田柔,p1將不是a函數(shù)返回的Promise俐巴,而是catch返回的promise
var p1 = a().catch((err) => {console.log('err:', err)});
var p2 = a().catch((err) => {console.log('err:', err)});
Promise.all([p1,p2]).then(([p1,p2])=>{console.log(123, p1,p2)}).catch((err) => {console.log('all',err)})
//7 a 里面的輸出
//err: Error: <500 p1 的catch的輸出
//    at <anonymous>:7:12
// 687 a 里面的輸出
//123 undefined 687 Promise.all的then輸出

上面p1p2都有可能會Rejected,一旦Rejected就會被他們的catch所捕獲硬爆,然后就會返回一個全新的Promise對象不再是原來的那個欣舵,全新的實例執(zhí)行完catch以后也會變成Resolved, 導致Promise.all方法里面的兩個實例都是Resolved缀磕,則會走Promise.allthen方法缘圈,而不會是catch方法。

Promise.race()

Promise.race方法同樣是將多個Promise的實例袜蚕,包裝成一個新的Promise實例糟把。

var p = Promise.race([p1,p2,p3])

上面代碼,只要p1,p2,p3之中有一個實例率先改變了狀態(tài)牲剃,p的狀態(tài)就會跟著改變遣疯,那個率先改變的Promise實例的返回值,就傳遞給p的回調(diào)函數(shù)凿傅。
他處理參數(shù)的方法的原則和Promise.all是一樣的缠犀。

var a = function(){
    var t = ~~(Math.random()*1000)
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log(t)
            resolve(t)
        },t)
    })
}
var p1 = a()
var p2 = a()
Promise.race([p1,p2]).then(t =>{console.log(123, t)})
// 87 a 函數(shù)輸出
// 123 87 Promise.race的then輸出
// 591 a 函數(shù)輸出

Promise.resolve()

有時需要將現(xiàn)有的對象轉(zhuǎn)化成Promise對象,Promise.resolve方法就起到這個作用狭归。

var  jqPromise = Promise.resolve($.ajax('/1.json'))

上面的代碼將jQuery生成的deferred對象夭坪,轉(zhuǎn)成一個新的Promise對象。

Promise.resolve('foo')
// 等價于
new Promise(function(resolve, reject){
  resolve('foo')
})

Promise.resolve方法的參數(shù)分為四種情況
a. 參數(shù)是一個Promise實例
如果參數(shù)是一個Promise實例过椎,那么Promise.resolve將不做任何修改室梅,原封不動的返回這個實例。
b. 參數(shù)是一個thenable對象
thenable對象是指具有then方法的對象疚宇,例如:

 // thenable 對象
 let thenable = {
   then: function(resolve, reject){
     resolve(42)
   }
 }
 let p1 = Promise.resolve(thenable);
 p1.then(function(value){
   console.log(value) // 42
 })

thenable對象的then方法執(zhí)行以后亡鼠,對象p1的狀態(tài)就會變成resolved,從而立即執(zhí)行最后的那個then方法指定的函數(shù)敷待,輸出42
c. 參數(shù)不是具有then方法的對象间涵,或根本就不是對象
如果參數(shù)是一個原始值,或者是一個不具有then方法的對象榜揖,則Promise.resolve方法返回一個新的Promise對象勾哩,狀態(tài)為Resolved.

var p = Promise.resolve('hello')
p.then(function(s){
  console.log(s)
})
// hello

上面的代碼生成一個新的Promise對象的實例p抗蠢,由于字符串hello不屬于異步操作(判斷方法字符串不具有then方法),返回的Promise實例的狀態(tài)從一生成就是Resolved思劳,所以回調(diào)函數(shù)立即執(zhí)行迅矛。Promise.resolve方法的參數(shù),會同時傳回給回調(diào)函數(shù)潜叛。
d. 不帶任何參數(shù)
Promise.resolve方法允許調(diào)用時不帶任何參數(shù)秽褒,直接返回一個Resolved狀態(tài)的Promise對象。
注意: 立即resolvePromise對象威兜,實在本輪事件循環(huán)的結(jié)束時销斟,而不是在下一輪事件循環(huán)的開始時

setTimeout(function(){
    console.log('setTimeout')
}, 0)
Promise.resolve().then(function(){
    console.log('Promise.resolve')
})
console.log('console.log()')
// console.log() 順序執(zhí)行
// Promise.resolve 本輪事件循環(huán)的尾部
// setTimeout 下輪事件循環(huán)的開始

setTimeout(fn, 0)實在下一輪事件循環(huán)的開始的時候執(zhí)行的,Promise.resolve()實在本輪事件循環(huán)的結(jié)束時執(zhí)行椒舵,console.log()立即執(zhí)行蚂踊,所以最先輸出。

Promise.reject()

Promise.resolve方法類似逮栅,生成相對的狀態(tài)為RejectedPromise實例悴势。

var p = Promise.reject('出錯了');
// 等同于
var p = new Promise((resolve, reject) => reject('出錯了'))

p.then(null, function (s) {
  console.log(s)
});
// 出錯了

生成的p的狀態(tài)為Rejected的窗宇,回調(diào)函數(shù)立馬執(zhí)行措伐。
注意: Promise.reject()方法的參數(shù),會原封不動的作為reject的理由军俊,比那成后續(xù)方法的參數(shù)侥加,這一點與Promise.resolve方法不一致

const thenable = {
  then(resolve, reject) {
    reject('出錯了');
  }
};

Promise.reject(thenable)
.catch(e => {
  console.log(e === thenable)
})
// true

兩個有用的附加方法

ES6的Promise API提供的方法不是很多,有些有用的方法可以自己部署粪躬。下面介紹如何部署兩個不在ES6之中担败、但很有用的方法。

Done()

Promise對象的回調(diào)鏈媒鼓,無論是以then方法或者catch方法結(jié)尾鸭巴,要是最后一個方法拋出錯誤牧挣,都有可能無法捕捉到(因為Promise內(nèi)部的錯誤不會冒泡到全局)。因此我們可以提供一個done方法狈网,總是處于回調(diào)鏈的尾端,保證拋出任何可能出現(xiàn)的錯誤笨腥。

asyncFunc()
  .then()
  .then()
  .catch()
  .done()

他的實現(xiàn)頁很簡單

Promise.prototype.done = function (onFulfilled, onRejected) {
  this.then(onFulfilled, onRejected)
    .catch(function (reason) {
        // 拋出全局錯誤
        setTimeout(() => {throw reason}, 0)
    })
}

done方法的使用拓哺,可以像then方法那樣用,提供FulfilledRejected狀態(tài)的回調(diào)函數(shù)脖母,也可以不提供任何參數(shù)士鸥。但不管怎樣,done都會捕捉到任何可能出現(xiàn)的錯誤谆级,并向全局拋出烤礁。

finally()

finally方法不管狀態(tài)是成功還是失敗讼积,都會執(zhí)行的的操作,他與done的最大的區(qū)別脚仔,是他接受一個回調(diào)函數(shù)作為參數(shù)币砂,該函數(shù)不管怎樣都必須執(zhí)行。

server.listen(0)
  .then(function () {
    // run test
  })
  .finally(server.stop);

實現(xiàn)

Promise.prototype.finally = function (callback) {
  let P = this.cunstructor;
  return this.then(function(value){
    P.resolve(callback()).then(function(){
      return value;
    })
  }, function(reason){
    P.reject(callback()).then(function(){
      throw reason;
    })
  })
}

上面代碼中玻侥,不管前面的Promisefulfilled還是rejected决摧,都會執(zhí)行回調(diào)函數(shù)callback

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凑兰,一起剝皮案震驚了整個濱河市掌桩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌姑食,老刑警劉巖波岛,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異音半,居然都是意外死亡则拷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門曹鸠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來煌茬,“玉大人,你說我怎么就攤上這事彻桃√成疲” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵邻眷,是天一觀的道長眠屎。 經(jīng)常有香客問我,道長肆饶,這世上最難降的妖魔是什么改衩? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮驯镊,結(jié)果婚禮上葫督,老公的妹妹穿的比我還像新娘。我一直安慰自己阿宅,他們只是感情好候衍,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著洒放,像睡著了一般蛉鹿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上往湿,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天妖异,我揣著相機與錄音惋戏,去河邊找鬼。 笑死他膳,一個胖子當著我的面吹牛响逢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播棕孙,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼舔亭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蟀俊?” 一聲冷哼從身側(cè)響起钦铺,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肢预,沒想到半個月后矛洞,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡烫映,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年沼本,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锭沟。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡抽兆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出冈钦,到底是詐尸還是另有隱情郊丛,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布瞧筛,位于F島的核電站,受9級特大地震影響导盅,放射性物質(zhì)發(fā)生泄漏较幌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一白翻、第九天 我趴在偏房一處隱蔽的房頂上張望乍炉。 院中可真熱鬧,春花似錦滤馍、人聲如沸岛琼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽槐瑞。三九已至,卻和暖如春阁苞,著一層夾襖步出監(jiān)牢的瞬間困檩,已是汗流浹背祠挫。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留悼沿,地道東北人等舔。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像糟趾,于是被迫代替她去往敵國和親慌植。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 00、前言Promise 是異步編程的一種解決方案魔慷,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強大只锭。它由社區(qū)...
    夜幕小草閱讀 2,133評論 0 12
  • Promiese 簡單說就是一個容器,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果院尔,語法上說蜻展,Pr...
    雨飛飛雨閱讀 3,358評論 0 19
  • Promise的含義: ??Promise是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和...
    呼呼哥閱讀 2,170評論 0 16
  • 本文適用的讀者 本文寫給有一定Promise使用經(jīng)驗的人邀摆,如果你還沒有使用過Promise纵顾,這篇文章可能不適合你,...
    HZ充電大喵閱讀 7,310評論 6 19
  • 分為兩類 技術(shù)類前端Javascript權(quán)威指南完成一半html5秘籍完成圖解http完成javascipt高級程...
    驕傲的葡萄干閱讀 235評論 3 0