摘要:迭代器和生成器將迭代的概念直接帶入核心語言,并提供一種機(jī)制來自定義循環(huán)的行為。本文主要會介紹中新增的迭代器和生成器。屬性本身是函數(shù),是當(dāng)前數(shù)據(jù)結(jié)構(gòu)默認(rèn)的迭代器生成函數(shù)。
本文是 重溫基礎(chǔ) 系列文章的第十三篇。
今日感受:每次自我年終總結(jié),都會有各種情緒和收獲。
系列目錄:
【復(fù)習(xí)資料】ES6/ES7/ES8/ES9資料整理(個(gè)人整理)
【重溫基礎(chǔ)】1.語法和數(shù)據(jù)類型
【重溫基礎(chǔ)】2.流程控制和錯(cuò)誤處理
【重溫基礎(chǔ)】3.循環(huán)和迭代
【重溫基礎(chǔ)】4.函數(shù)
【重溫基礎(chǔ)】5.表達(dá)式和運(yùn)算符
【重溫基礎(chǔ)】6.數(shù)字
【重溫基礎(chǔ)】7.時(shí)間對象
【重溫基礎(chǔ)】8.字符串
【重溫基礎(chǔ)】9.正則表達(dá)式
【重溫基礎(chǔ)】10.數(shù)組
【重溫基礎(chǔ)】11.Map和Set對象
【重溫基礎(chǔ)】12.使用對象
本章節(jié)復(fù)習(xí)的是JS中的迭代器和生成器,常常用來處理集合。
前置知識:
JavaScrip已經(jīng)提供多個(gè)迭代集合的方法,從簡單的for循環(huán)到map()和filter()。
迭代器和生成器將迭代的概念直接帶入核心語言,并提供一種機(jī)制來自定義for...of循環(huán)的行為。
本文會將知識點(diǎn)分為兩大部分,簡單介紹和詳細(xì)介紹:
簡單介紹,適合基礎(chǔ)入門會使用的目標(biāo);
詳細(xì)介紹,會更加深入的做介紹,適合理解原理;
當(dāng)我們使用循環(huán)語句迭代數(shù)據(jù)時(shí),需初始化一個(gè)變量來記錄每一次迭代在數(shù)據(jù)集合中的位置:
let a = ["aaa","bbb","ccc"]; for (let i = 0; i< a.length; i++){ console.log(a[i]); }
這邊的i就是我們用來記錄迭代位置的變量,但是在ES6開始,JavaScrip引入了迭代器這個(gè)特性,并且新的數(shù)組方法和新的集合類型(如Set集合與Map集合)都依賴迭代器的實(shí)現(xiàn),這個(gè)新特性對于高效的數(shù)據(jù)處理而言是不可或缺的,在語言的其他特性中也都有迭代器的身影:新的for-of循環(huán)、展開運(yùn)算符(...),甚至連異步編程都可以使用迭代器。
本文主要會介紹ES6中新增的迭代器(Iterator)和生成器(Generator)。
2. 迭代器(簡單介紹)迭代器是一種特殊對象,它具有一些專門為迭代過程設(shè)計(jì)的專有接口,所有的迭代器對象都有一個(gè)next()方法,每次調(diào)用都會返回一個(gè)結(jié)果對象。
這個(gè)結(jié)果對象,有兩個(gè)屬性:
value: 表示下一個(gè)將要返回的值。
done: 一個(gè)布爾值,若沒有更多可返回的數(shù)據(jù)時(shí),值為true,否則false。
如果最后一個(gè)值返回后,再調(diào)用next(),則返回的對象的done值為true,而value值如果沒有值的話,返回的為undefined。
ES5實(shí)現(xiàn)一個(gè)迭代器:
function myIterator(list){ var i = 0; return { next: function(){ var done = i >= list.length; var value = !done ? list[i++] : undefined; return { done : done, value : value } } } } var iterator = myIterator([1,2,3]); iterator.next(); // "{done: false, value: 1}" iterator.next(); // "{done: false, value: 2}" iterator.next(); // "{done: false, value: 3}" iterator.next(); // "{done: true, value: undefined}" // 以后的調(diào)用都一樣 iterator.next(); // "{done: true, value: undefined}"
從上面代碼可以看出,ES5的實(shí)現(xiàn)還是比較麻煩,而ES6新增的生成器,可以使得創(chuàng)建迭代器對象的過程更加簡單。
3. 生成器(簡單介紹)生成器是一種返回迭代器的函數(shù),通過function關(guān)鍵字后的星號(*)來表示,函數(shù)中會用到新的關(guān)鍵字yield。星號可以緊挨著function關(guān)鍵字,也可以在中間添加一個(gè)空格。
function *myIterator(){ yield 1; yield 2; yield 3; } let iterator = myIterator(); iterator.next(); // "{done: false, value: 1}" iterator.next(); // "{done: false, value: 2}" iterator.next(); // "{done: false, value: 3}" iterator.next(); // "{done: true, value: undefined}" // 以后的調(diào)用都一樣 iterator.next(); // "{done: true, value: undefined}"
生成器函數(shù)最有趣的部分是,每當(dāng)執(zhí)行完一條yield語句后函數(shù)就會自動停止執(zhí)行,比如上面代碼,當(dāng)yield 1;執(zhí)行完后,便不會執(zhí)行任何語句,而是等到再調(diào)用迭代器的next()方法才會執(zhí)行下一個(gè)語句,即yield 2;.
使用yield關(guān)鍵字可以返回任何值和表達(dá)式,因?yàn)榭梢酝ㄟ^生成器函數(shù)批量給迭代器添加元素:
function *myIterator(list){ for(let i = 0; i< list.length ; i ++){ yield list[i]; } } var iterator = myIterator([1,2,3]); iterator.next(); // "{done: false, value: 1}" iterator.next(); // "{done: false, value: 2}" iterator.next(); // "{done: false, value: 3}" iterator.next(); // "{done: true, value: undefined}" // 以后的調(diào)用都一樣 iterator.next(); // "{done: true, value: undefined}"
生成器的適用返回很廣,可以將它用于所有支持函數(shù)使用的地方。
4. 迭代器(詳細(xì)介紹) 4.1 Iterator迭代器概念Iterator是一種接口,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問機(jī)制。任何數(shù)據(jù)結(jié)構(gòu)只要部署 Iterator 接口,就可以完成迭代操作(即依次處理該數(shù)據(jù)結(jié)構(gòu)的所有成員)。
Iterator三個(gè)作用:
為各種數(shù)據(jù)結(jié)構(gòu),提供一個(gè)統(tǒng)一的、簡便的訪問接口;
使得數(shù)據(jù)結(jié)構(gòu)的成員能夠按某種次序排列;
Iterator 接口主要供ES6新增的for...of消費(fèi);
4.2 Iterator迭代過程創(chuàng)建一個(gè)指針對象,指向當(dāng)前數(shù)據(jù)結(jié)構(gòu)的起始位置。也就是說,迭代器對象本質(zhì)上,就是一個(gè)指針對象。
第一次調(diào)用指針對象的next方法,可以將指針指向數(shù)據(jù)結(jié)構(gòu)的第一個(gè)成員。
第二次調(diào)用指針對象的next方法,指針就指向數(shù)據(jù)結(jié)構(gòu)的第二個(gè)成員。
不斷調(diào)用指針對象的next方法,直到它指向數(shù)據(jù)結(jié)構(gòu)的結(jié)束位置。
每一次調(diào)用next方法,都會返回?cái)?shù)據(jù)結(jié)構(gòu)的當(dāng)前成員的信息。具體來說,就是返回一個(gè)包含value和done兩個(gè)屬性的對象。
value屬性是當(dāng)前成員的值;
done屬性是一個(gè)布爾值,表示迭代是否結(jié)束;
模擬next方法返回值:
let f = function (arr){ var nextIndex = 0; return { next:function(){ return nextIndex < arr.length ? {value: arr[nextIndex++], done: false}: {value: undefined, done: true} } } } let a = f(["a", "b"]); a.next(); // { value: "a", done: false } a.next(); // { value: "b", done: false } a.next(); // { value: undefined, done: true }4.3 默認(rèn)Iterator接口
若數(shù)據(jù)可迭代,即一種數(shù)據(jù)部署了Iterator接口。
ES6中默認(rèn)的Iterator接口部署在數(shù)據(jù)結(jié)構(gòu)的Symbol.iterator屬性,即如果一個(gè)數(shù)據(jù)結(jié)構(gòu)具有Symbol.iterator屬性,就可以認(rèn)為是可迭代。
Symbol.iterator屬性本身是函數(shù),是當(dāng)前數(shù)據(jù)結(jié)構(gòu)默認(rèn)的迭代器生成函數(shù)。執(zhí)行這個(gè)函數(shù),就會返回一個(gè)迭代器。至于屬性名Symbol.iterator,它是一個(gè)表達(dá)式,返回Symbol對象的iterator屬性,這是一個(gè)預(yù)定義好的、類型為 Symbol 的特殊值,所以要放在方括號內(nèi)(參見《Symbol》一章)。
原生具有Iterator接口的數(shù)據(jù)結(jié)構(gòu)有:
Array
Map
Set
String
TypedArray
函數(shù)的 arguments 對象
NodeList 對象
4.4 Iterator使用場景(1)解構(gòu)賦值
對數(shù)組和 Set 結(jié)構(gòu)進(jìn)行解構(gòu)賦值時(shí),會默認(rèn)調(diào)用Symbol.iterator方法。
let a = new Set().add("a").add("b").add("c"); let [x, y] = a; // x = "a" y = "b" let [a1, ...a2] = a; // a1 = "a" a2 = ["b","c"]
(2)擴(kuò)展運(yùn)算符
擴(kuò)展運(yùn)算符(...)也會調(diào)用默認(rèn)的 Iterator 接口。
let a = "hello"; [...a]; // ["h","e","l","l","o"] let a = ["b", "c"]; ["a", ...a, "d"]; // ["a", "b", "c", "d"]
(2)yield*
yield*后面跟的是一個(gè)可迭代的結(jié)構(gòu),它會調(diào)用該結(jié)構(gòu)的迭代器接口。
let a = function*(){ yield 1; yield* [2,3,4]; yield 5; } let b = a(); b.next() // { value: 1, done: false } b.next() // { value: 2, done: false } b.next() // { value: 3, done: false } b.next() // { value: 4, done: false } b.next() // { value: 5, done: false } b.next() // { value: undefined, done: true }
(4)其他場合
由于數(shù)組的迭代會調(diào)用迭代器接口,所以任何接受數(shù)組作為參數(shù)的場合,其實(shí)都調(diào)用了迭代器接口。下面是一些例子。
for...of
Array.from()
Map(), Set(), WeakMap(), WeakSet()(比如new Map([["a",1],["b",2]]))
Promise.all()
Promise.race()
4.5 for...of循環(huán)只要數(shù)據(jù)結(jié)構(gòu)部署了Symbol.iterator屬性,即具有 iterator 接口,可以用for...of循環(huán)迭代它的成員。也就是說,for...of循環(huán)內(nèi)部調(diào)用的是數(shù)據(jù)結(jié)構(gòu)的Symbol.iterato方法。
使用場景:
for...of可以使用在數(shù)組,Set和Map結(jié)構(gòu),類數(shù)組對象,Genetator對象和字符串。
數(shù)組
for...of循環(huán)可以代替數(shù)組實(shí)例的forEach方法。
let a = ["a", "b", "c"]; for (let k of a){console.log(k)}; // a b c a.forEach((ele, index)=>{ console.log(ele); // a b c console.log(index); // 0 1 2 })
與for...in對比,for...in只能獲取對象鍵名,不能直接獲取鍵值,而for...of允許直接獲取鍵值。
let a = ["a", "b", "c"]; for (let k of a){console.log(k)}; // a b c for (let k in a){console.log(k)}; // 0 1 2
Set和Map
可以使用數(shù)組作為變量,如for (let [k,v] of b){...}。
let a = new Set(["a", "b", "c"]); for (let k of a){console.log(k)}; // a b c let b = new Map(); b.set("name","leo"); b.set("age", 18); b.set("aaa","bbb"); for (let [k,v] of b){console.log(k + ":" + v)}; // name:leo // age:18 // aaa:bbb
類數(shù)組對象
// 字符串 let a = "hello"; for (let k of a ){console.log(k)}; // h e l l o // DOM NodeList對象 let b = document.querySelectorAll("p"); for (let k of b ){ k.classList.add("test"); } // arguments對象 function f(){ for (let k of arguments){ console.log(k); } } f("a","b"); // a b
對象
普通對象不能直接使用for...of會報(bào)錯(cuò),要部署Iterator才能使用。
let a = {a:"aa",b:"bb",c:"cc"}; for (let k in a){console.log(k)}; // a b c for (let k of a){console>log(k)}; // TypeError4.6 跳出for...of
使用break來實(shí)現(xiàn)。
for (let k of a){ if(k>100) break; console.log(k); }5. 生成器(詳細(xì)介紹) 5.1 基本概念
Generator生成器函數(shù)是一種異步編程解決方案。
原理:
執(zhí)行Genenrator函數(shù)會返回一個(gè)遍歷器對象,依次遍歷Generator函數(shù)內(nèi)部的每一個(gè)狀態(tài)。
Generator函數(shù)是一個(gè)普通函數(shù),有以下兩個(gè)特征:
function關(guān)鍵字與函數(shù)名之間有個(gè)星號;
函數(shù)體內(nèi)使用yield表達(dá)式,定義不同狀態(tài);
通過調(diào)用next方法,將指針移向下一個(gè)狀態(tài),直到遇到下一個(gè)yield表達(dá)式(或return語句)為止。簡單理解,Generator函數(shù)分段執(zhí)行,yield表達(dá)式是暫停執(zhí)行的標(biāo)記,而next恢復(fù)執(zhí)行。
function * f (){ yield "hi"; yield "leo"; return "ending"; } let a = f(); a.next(); // {value: "hi", done : false} a.next(); // {value: "leo", done : false} a.next(); // {value: "ending", done : true} a.next(); // {value: undefined, done : false}5.2 yield表達(dá)式
yield表達(dá)式是暫停標(biāo)志,遍歷器對象的next方法的運(yùn)行邏輯如下:
遇到yield就暫停執(zhí)行,將這個(gè)yield后的表達(dá)式的值,作為返回對象的value屬性值。
下次調(diào)用next往下執(zhí)行,直到遇到下一個(gè)yield。
直到函數(shù)結(jié)束或者return為止,并返回return語句后面表達(dá)式的值,作為返回對象的value屬性值。
如果該函數(shù)沒有return語句,則返回對象的value為undefined 。
注意:
yield只能用在Generator函數(shù)里使用,其他地方使用會報(bào)錯(cuò)。
// 錯(cuò)誤1 (function(){ yiled 1; // SyntaxError: Unexpected number })() // 錯(cuò)誤2 forEach參數(shù)是個(gè)普通函數(shù) let a = [1, [[2, 3], 4], [5, 6]]; let f = function * (i){ i.forEach(function(m){ if(typeof m !== "number"){ yield * f (m); }else{ yield m; } }) } for (let k of f(a)){ console.log(k) }
yield表達(dá)式如果用于另一個(gè)表達(dá)式之中,必須放在圓括號內(nèi)。
function * a (){ console.log("a" + yield); // SyntaxErro console.log("a" + yield 123); // SyntaxErro console.log("a" + (yield)); // ok console.log("a" + (yield 123)); // ok }
yield表達(dá)式用做函數(shù)參數(shù)或放在表達(dá)式右邊,可以不加括號。
function * a (){ f(yield "a", yield "b"); // ok lei i = yield; // ok }5.3 next方法
yield本身沒有返回值,或者是總返回undefined,next方法可帶一個(gè)參數(shù),作為上一個(gè)yield表達(dá)式的返回值。
function * f (){ for (let k = 0; true; k++){ let a = yield k; if(a){k = -1}; } } let g =f(); g.next(); // {value: 0, done: false} g.next(); // {value: 1, done: false} g.next(true); // {value: 0, done: false}
這一特點(diǎn),可以讓Generator函數(shù)開始執(zhí)行之后,可以從外部向內(nèi)部注入不同值,從而調(diào)整函數(shù)行為。
function * f(x){ let y = 2 * (yield (x+1)); let z = yield (y/3); return (x + y + z); } let a = f(5); a.next(); // {value : 6 ,done : false} a.next(); // {value : NaN ,done : false} a.next(); // {value : NaN ,done : true} // NaN因?yàn)閥eild返回的是對象 和數(shù)字計(jì)算會NaN let b = f(5); b.next(); // {value : 6 ,done : false} b.next(12); // {value : 8 ,done : false} b.next(13); // {value : 42 ,done : false} // x 5 y 24 z 135.4 for...of循環(huán)
for...of循環(huán)會自動遍歷,不用調(diào)用next方法,需要注意的是,for...of遇到next返回值的done屬性為true就會終止,return返回的不包括在for...of循環(huán)中。
function * f(){ yield 1; yield 2; yield 3; yield 4; return 5; } for (let k of f()){ console.log(k); } // 1 2 3 4 沒有 55.5 Generator.prototype.throw()
throw方法用來向函數(shù)外拋出錯(cuò)誤,并且在Generator函數(shù)體內(nèi)捕獲。
let f = function * (){ try { yield } catch (e) { console.log("內(nèi)部捕獲", e) } } let a = f(); a.next(); try{ a.throw("a"); a.throw("b"); }catch(e){ console.log("外部捕獲",e); } // 內(nèi)部捕獲 a // 外部捕獲 b5.6 Generator.prototype.return()
return方法用來返回給定的值,并結(jié)束遍歷Generator函數(shù),如果return方法沒有參數(shù),則返回值的value屬性為undefined。
function * f(){ yield 1; yield 2; yield 3; } let g = f(); g.next(); // {value : 1, done : false} g.return("leo"); // {value : "leo", done " true} g.next(); // {value : undefined, done : true}5.7 next()/throw()/return()共同點(diǎn)
相同點(diǎn)就是都是用來恢復(fù)Generator函數(shù)的執(zhí)行,并且使用不同語句替換yield表達(dá)式。
next()將yield表達(dá)式替換成一個(gè)值。
let f = function * (x,y){ let r = yield x + y; return r; } let g = f(1, 2); g.next(); // {value : 3, done : false} g.next(1); // {value : 1, done : true} // 相當(dāng)于把 let r = yield x + y; // 替換成 let r = 1;
throw()將yield表達(dá)式替換成一個(gè)throw語句。
g.throw(new Error("報(bào)錯(cuò)")); // Uncaught Error:報(bào)錯(cuò) // 相當(dāng)于將 let r = yield x + y // 替換成 let r = throw(new Error("報(bào)錯(cuò)"));
next()將yield表達(dá)式替換成一個(gè)return語句。
g.return(2); // {value: 2, done: true} // 相當(dāng)于將 let r = yield x + y // 替換成 let r = return 2;5.8 yield* 表達(dá)式
用于在一個(gè)Generator中執(zhí)行另一個(gè)Generator函數(shù),如果沒有使用yield*會沒有效果。
function * a(){ yield 1; yield 2; } function * b(){ yield 3; yield * a(); yield 4; } // 等同于 function * b(){ yield 3; yield 1; yield 2; yield 4; } for(let k of b()){console.log(k)} // 3 // 1 // 2 // 45.9 應(yīng)用場景
控制流管理
解決回調(diào)地獄:
// 使用前 f1(function(v1){ f2(function(v2){ f3(function(v3){ // ... more and more }) }) }) // 使用Promise Promise.resolve(f1) .then(f2) .then(f3) .then(function(v4){ // ... },function (err){ // ... }).done(); // 使用Generator function * f (v1){ try{ let v2 = yield f1(v1); let v3 = yield f1(v2); let v4 = yield f1(v3); // ... }catch(err){ // console.log(err) } } function g (task){ let obj = task.next(task.value); // 如果Generator函數(shù)未結(jié)束,就繼續(xù)調(diào)用 if(!obj.done){ task.value = obj.value; g(task); } } g( f(initValue) );
異步編程的使用
在真實(shí)的異步任務(wù)封裝的情況:
let fetch = require("node-fetch"); function * f(){ let url = "http://www.baidu.com"; let res = yield fetch(url); console.log(res.bio); } // 執(zhí)行該函數(shù) let g = f(); let result = g.next(); // 由于fetch返回的是Promise對象,所以用then result.value.then(function(data){ return data.json(); }).then(function(data){ g.next(data); })參考資料
1.MDN 迭代器和生成器
2.ES6中的迭代器(Iterator)和生成器(Generator)
本部分內(nèi)容到這結(jié)束
Author | 王平安 |
---|---|
[email protected] | |
博 客 | www.pingan8787.com |
微 信 | pingan8787 |
每日文章推薦 | https://github.com/pingan8787... |
JS小冊 | js.pingan8787.com |
歡迎關(guān)注微信公眾號【前端自習(xí)課】每天早晨,與您一起學(xué)習(xí)一篇優(yōu)秀的前端技術(shù)博文 .
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/100747.html
摘要:本文是重溫基礎(chǔ)系列文章的第十四篇。元,是指程序本身。有理解不到位,還請指點(diǎn),具體詳細(xì)的介紹,可以查看維基百科元編程。攔截,返回一個(gè)布爾值。 本文是 重溫基礎(chǔ) 系列文章的第十四篇。 這是第一個(gè)基礎(chǔ)系列的最后一篇,后面會開始復(fù)習(xí)一些中級的知識了,歡迎持續(xù)關(guān)注呀! 接下來會統(tǒng)一整理到我的【Cute-JavaScript】的JavaScript基礎(chǔ)系列中。 今日感受:獨(dú)樂樂不如眾樂樂...
摘要:構(gòu)造函數(shù)通常首字母大寫,用于區(qū)分普通函數(shù)。這種關(guān)系常被稱為原型鏈,它解釋了為何一個(gè)對象會擁有定義在其他對象中的屬性和方法。中所有的對象,都有一個(gè)屬性,指向?qū)嵗龑ο蟮臉?gòu)造函數(shù)原型由于是個(gè)非標(biāo)準(zhǔn)屬性,因此只有和兩個(gè)瀏覽器支持,標(biāo)準(zhǔn)方法是。 從這篇文章開始,復(fù)習(xí) MDN 中級教程 的內(nèi)容了,在初級教程中,我和大家分享了一些比較簡單基礎(chǔ)的知識點(diǎn),并放在我的 【Cute-JavaScript】系...
摘要:本文是重溫基礎(chǔ)系列文章的第十篇。返回一個(gè)由回調(diào)函數(shù)的返回值組成的新數(shù)組。返回一個(gè)數(shù)組迭代器對象,該迭代器會包含所有數(shù)組元素的鍵值對?;卣{(diào)函數(shù)接收三個(gè)參數(shù),當(dāng)前值當(dāng)前位置和原數(shù)組。 本文是 重溫基礎(chǔ) 系列文章的第十篇。 今日感受:平安夜,多棒。 系列目錄: 【復(fù)習(xí)資料】ES6/ES7/ES8/ES9資料整理(個(gè)人整理) 【重溫基礎(chǔ)】1.語法和數(shù)據(jù)類型 【重溫基礎(chǔ)】2.流程控制和錯(cuò)誤...
摘要:本文是重溫基礎(chǔ)系列文章的第三篇,今天想起鬼腳七的一句話人不一定自由,但思想一定是自由的。系列目錄復(fù)習(xí)資料資料整理個(gè)人整理重溫基礎(chǔ)語法和數(shù)據(jù)類型重溫基礎(chǔ)流程控制和錯(cuò)誤處理本章節(jié)復(fù)習(xí)的是中的循環(huán)語句,讓我們能更快速且簡單的完成一些需求。 本文是 重溫基礎(chǔ) 系列文章的第三篇,今天想起鬼腳七的一句話:人不一定自由,但思想一定是自由的。思想沒有對和錯(cuò),也沒有高和低,只有不同。了解一個(gè)人可以去了解...
摘要:來說說迭代器和生成器,還有可迭代對象和生成器表達(dá)式。有點(diǎn)繞是不是,其實(shí),一般只要知道可迭代對象以及它是如何實(shí)現(xiàn)的就行了,中常常用生成器來代替迭代器,可以說,生成器就是迭代器。 來說說迭代器和生成器,還有可迭代對象和生成器表達(dá)式。 之前簡單的提到過,一個(gè)對象是可迭代的可以理解為能夠使用for循環(huán)。這樣說其實(shí)不太準(zhǔn)確,某個(gè)對象可迭代是因?yàn)樗鼉?nèi)部實(shí)現(xiàn)了$__iter__$這個(gè)特殊方法。比如在...
閱讀 2822·2023-04-25 22:51
閱讀 2067·2021-10-11 10:58
閱讀 3319·2019-08-30 10:49
閱讀 1884·2019-08-29 17:09
閱讀 3143·2019-08-29 10:55
閱讀 853·2019-08-26 10:34
閱讀 3499·2019-08-23 17:54
閱讀 990·2019-08-23 16:06