弿¥è¿ä»£å 许æä»¬å¯¹æééè¿å¼æ¥è¯·æ±èå¾å°çæ°æ®è¿è¡è¿ä»£ãä¾å¦ï¼æä»¬éè¿ç½ç»å段ï¼chunk-by-chunkï¼ä¸è½½æ°æ®æ¶ã弿¥çæå¨ï¼generatorï¼ä½¿è¿ä¸æ¥éª¤æ´å æ¹ä¾¿ã
é¦å ï¼è®©æä»¬æ¥çä¸ä¸ªç®åç示ä¾ä»¥ææ¡è¯æ³ï¼ç¶ååçä¸ä¸ªå®é ç¨ä¾ã
å顾å¯è¿ä»£å¯¹è±¡
让æä»¬å顾ä¸ä¸å¯è¿ä»£å¯¹è±¡çç¸å ³å 容ã
å设æä»¬æä¸ä¸ªå¯¹è±¡ï¼ä¾å¦ä¸é¢ç rangeï¼
let range = {
from: 1,
to: 5
};
æä»¬æ³å¯¹å®ä½¿ç¨ for..of 循ç¯ï¼ä¾å¦ for(value of range)ï¼æ¥è·åä» 1 å° 5 çå¼ã
æ¢å¥è¯è¯´ï¼æä»¬æ³å对象 range æ·»å è¿ä»£è½åã
è¿å¯ä»¥éè¿ä½¿ç¨ä¸ä¸ªå为 Symbol.iterator çç¹æ®æ¹æ³æ¥å®ç°ï¼
- å½å¾ªç¯å¼å§æ¶ï¼è¯¥æ¹æ³è¢«
for..ofç»æè°ç¨ï¼å¹¶ä¸å®åºè¯¥è¿åä¸ä¸ªå¸¦ænextæ¹æ³ç对象ã - å¯¹äºæ¯æ¬¡è¿ä»£ï¼é½ä¼ä¸ºä¸ä¸ä¸ªå¼è°ç¨
next()æ¹æ³ã next()æ¹æ³åºè¯¥ä»¥{done: true/false, value:<loop value>}çæ ¼å¼è¿åä¸ä¸ªå¼ï¼å ¶ä¸done:true表示循ç¯ç»æã
è¿æ¯å¯è¿ä»£ç range çä¸ä¸ªå®ç°ï¼
let range = {
from: 1,
to: 5,
[Symbol.iterator]() { // å¨ for..of 循ç¯å¼å§æ¶è¢«è°ç¨ä¸æ¬¡
return {
current: this.from,
last: this.to,
next() { // æ¯æ¬¡è¿ä»£æ¶é½ä¼è¢«è°ç¨ï¼æ¥è·åä¸ä¸ä¸ªå¼
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
}
};
for(let value of range) {
alert(value); // 1ï¼ç¶å 2ï¼ç¶å 3ï¼ç¶å 4ï¼ç¶å 5
}
妿æä»»ä½ä¸æ¸ æ¥çï¼ä½ å¯ä»¥é 读 Iterable objectï¼å¯è¿ä»£å¯¹è±¡ï¼ ä¸ç« ï¼å ¶ä¸è¯¦ç»è®²è§£äºå ³äºå¸¸è§è¿ä»£å¨ï¼iteratorï¼çææå 容ã
弿¥å¯è¿ä»£å¯¹è±¡
å½å¼æ¯ä»¥å¼æ¥çå½¢å¼åºç°æ¶ï¼ä¾å¦å¨ setTimeout æè
å¦ä¸ç§å»¶è¿ä¹åï¼å°±éè¦å¼æ¥è¿ä»£ã
æå¸¸è§çåºæ¯æ¯ï¼å¯¹è±¡éè¦åéä¸ä¸ªç½ç»è¯·æ±ä»¥ä¼ éä¸ä¸ä¸ªå¼ï¼ç¨åæä»¬å°çå°ä¸ä¸ªå®ççå®ç¤ºä¾ã
è¦ä½¿å¯¹è±¡å¼æ¥è¿ä»£ï¼
- 使ç¨
Symbol.asyncIteratorå代Symbol.iteratorã next()æ¹æ³åºè¯¥è¿åä¸ä¸ªpromiseï¼å¸¦æä¸ä¸ä¸ªå¼ï¼å¹¶ä¸ç¶æä¸ºfulfilledï¼ã- å
³é®å
asyncå¯ä»¥å®ç°è¿ä¸ç¹ï¼æä»¬å¯ä»¥ç®åå°ä½¿ç¨async next()ã
- å
³é®å
- æä»¬åºè¯¥ä½¿ç¨
for await (let item of iterable)å¾ªç¯æ¥è¿ä»£è¿æ ·ç对象ã- 注æå
³é®å
awaitã
- 注æå
³é®å
ä½ä¸ºå¼å§ç示ä¾ï¼è®©æä»¬å建ä¸ä¸ªå¯è¿ä»£ç range 对象ï¼ä¸åé¢çé£ä¸ªç±»ä¼¼ï¼ä¸è¿ç°å¨å®å°å¼æ¥å°æ¯ç§è¿åä¸ä¸ªå¼ã
æä»¬éè¦åçå°±æ¯å¯¹ä¸é¢ä»£ç ä¸çé¨å代ç è¿è¡æ¿æ¢ï¼
let range = {
from: 1,
to: 5,
[Symbol.asyncIterator]() { // (1)
return {
current: this.from,
last: this.to,
async next() { // (2)
// 注æï¼æä»¬å¯ä»¥å¨ async next å
é¨ä½¿ç¨ "await"
await new Promise(resolve => setTimeout(resolve, 1000)); // (3)
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
}
};
(async () => {
for await (let value of range) { // (4)
alert(value); // 1,2,3,4,5
}
})()
æ£å¦æä»¬æçå°çï¼å ¶ç»æä¸å¸¸è§ç iterator 类似:
- 为äºä½¿ä¸ä¸ªå¯¹è±¡å¯ä»¥å¼æ¥è¿ä»£ï¼å®å¿
é¡»å
·ææ¹æ³
Symbol.asyncIterator(1)ã - è¿ä¸ªæ¹æ³å¿
é¡»è¿åä¸ä¸ªå¸¦æ
next()æ¹æ³ç对象ï¼next()æ¹æ³ä¼è¿åä¸ä¸ª promise(2)ã - è¿ä¸ª
next()æ¹æ³å¯ä»¥ä¸æ¯asyncçï¼å®å¯ä»¥æ¯ä¸ä¸ªè¿å弿¯ä¸ä¸ªpromiseç常è§çæ¹æ³ï¼ä½æ¯ä½¿ç¨asyncå ³é®åå¯ä»¥å 许æä»¬å¨æ¹æ³å é¨ä½¿ç¨awaitï¼æä»¥ä¼æ´å æ¹ä¾¿ãè¿éæä»¬åªæ¯ç¨äºå»¶è¿ 1 ç§çæä½(3)ã - æä»¬ä½¿ç¨
for await(let value of range)(4)æ¥è¿è¡è¿ä»£ï¼ä¹å°±æ¯å¨forå颿·»åawaitãå®ä¼è°ç¨ä¸æ¬¡range[Symbol.asyncIterator]()æ¹æ³ä¸æ¬¡ï¼ç¶åè°ç¨å®çnext()æ¹æ³è·åå¼ã
è¿æ¯ä¸ä¸ªå¯¹æ¯ Iterator å弿¥ iterator ä¹é´å·®å¼çè¡¨æ ¼ï¼
| Iterator | 弿¥ iterator | |
|---|---|---|
| æä¾ iterator çå¯¹è±¡æ¹æ³ | Symbol.iterator |
Symbol.asyncIterator |
next() è¿åç弿¯ |
ä»»æå¼ | Promise |
| è¦è¿è¡å¾ªç¯ï¼ä½¿ç¨ | for..of |
for await..of |
... æ æ³å¼æ¥å·¥ä½éè¦å¸¸è§ç忥 iterator çåè½ï¼æ æ³ä¸å¼æ¥ iterator ä¸èµ·ä½¿ç¨ã
ä¾å¦ï¼spread è¯æ³æ æ³å·¥ä½ï¼
alert( [...range] ); // Error, no Symbol.iterator
è¿å¾æ£å¸¸ï¼å ä¸ºå®æææ¾å° Symbol.iteratorï¼è䏿¯ Symbol.asyncIteratorã
for..of çæ
åµåè¿ä¸ªä¸æ ·ï¼æ²¡æ await å
³é®åæ¶ï¼åæææ¾å°çæ¯ Symbol.iteratorã
å顾 generator
ç°å¨ï¼è®©æä»¬å顾ä¸ä¸ generatorï¼å®ä½¿æä»¬è½å¤ååºæ´ççè¿ä»£ä»£ç ãå¨å¤§å¤æ°æ¶åï¼å½æä»¬æ³è¦å建ä¸ä¸ªå¯è¿ä»£å¯¹è±¡æ¶ï¼æä»¬ä¼ä½¿ç¨ generatorã
ç®åèµ·è§ï¼è¿éçç¥äºä¸äºè§£éï¼å³ generator æ¯âçæï¼yieldï¼å¼ç彿°âãå ³äºæ¤ç详ç»è¯´æè¯·è§ generator ä¸ç« ã
Generator æ¯æ æ function*ï¼æ³¨ææå·ï¼ç彿°ï¼å®ä½¿ç¨ yield æ¥çæå¼ï¼å¹¶ä¸æä»¬å¯ä»¥ä½¿ç¨ for..of å¾ªç¯æ¥éåå®ä»¬ã
ä¸é¢è¿ä¾åçæäºä» start å° end çä¸ç³»åå¼ï¼
function* generateSequence(start, end) {
for (let i = start; i <= end; i++) {
yield i;
}
}
for(let value of generateSequence(1, 5)) {
alert(value); // 1ï¼ç¶å 2ï¼ç¶å 3ï¼ç¶å 4ï¼ç¶å 5
}
æ£å¦æä»¬æç¥éçï¼è¦ä½¿ä¸ä¸ªå¯¹è±¡å¯è¿ä»£ï¼æä»¬éè¦ç»å®æ·»å Symbol.iteratorã
let range = {
from: 1,
to: 5,
[Symbol.iterator]() {
return <带æ next æ¹æ³ç对象ï¼ä»¥ä½¿å¯¹è±¡ range å¯è¿ä»£>
}
}
å¯¹äº Symbol.iterator æ¥è¯´ï¼ä¸ä¸ªé常çåæ³æ¯è¿åä¸ä¸ª generatorï¼è¿æ ·å¯ä»¥ä½¿ä»£ç æ´çï¼å¦ä¸æç¤ºï¼
let range = {
from: 1,
to: 5,
*[Symbol.iterator]() { // [Symbol.iterator]: function*() çä¸ç§ç®å
for(let value = this.from; value <= this.to; value++) {
yield value;
}
}
};
for(let value of range) {
alert(value); // 1ï¼ç¶å 2ï¼ç¶å 3ï¼ç¶å 4ï¼ç¶å 5
}
å¦æä½ æ³äºè§£æ´å¤è¯¦ç»å 容ï¼è¯·é 读 generator ä¸ç« ã
å¨å¸¸è§ç generator ä¸ï¼æä»¬æ æ³ä½¿ç¨ awaitãææçå¼é½å¿
é¡»æç
§ for..of æé çè¦æ±åæ¥å°åºç°ã
妿æä»¬æ³è¦å¼æ¥å°çæå¼è¯¥æä¹åï¼ä¾å¦ï¼å¯¹äºæ¥èªç½ç»è¯·æ±çå¼ã
让æä»¬ååå°å¼æ¥ generatorï¼æ¥ä½¿è¿ä¸ªéæ±æä¸ºå¯è½ã
弿¥ generator (finally)
对äºå¤§å¤æ°çå®é åºç¨ç¨åºï¼å½æä»¬æ³å建ä¸ä¸ªå¼æ¥çæä¸ç³»åå¼ç对象æ¶ï¼æä»¬é½å¯ä»¥ä½¿ç¨å¼æ¥ generatorã
è¯æ³å¾ç®åï¼å¨ function* åé¢å ä¸ asyncãè¿å³å¯ä½¿ generator åä¸ºå¼æ¥çã
ç¶åä½¿ç¨ for await (...) æ¥éåå®ï¼åè¿æ ·ï¼
async function* generateSequence(start, end) {
for (let i = start; i <= end; i++) {
// åï¼å¯ä»¥ä½¿ç¨ await äºï¼
await new Promise(resolve => setTimeout(resolve, 1000));
yield i;
}
}
(async () => {
let generator = generateSequence(1, 5);
for await (let value of generator) {
alert(value); // 1ï¼ç¶å 2ï¼ç¶å 3ï¼ç¶å 4ï¼ç¶å 5ï¼å¨æ¯ä¸ª alert ä¹é´æå»¶è¿ï¼
}
})();
å ä¸ºæ¤ generator æ¯å¼æ¥çï¼æä»¥æä»¬å¯ä»¥å¨å
¶å
é¨ä½¿ç¨ awaitï¼ä¾èµäº promiseï¼æ§è¡ç½ç»è¯·æ±çä»»å¡ã
å¦æä½ è¿è®°å¾æä»¬å¨åé¢ç« èä¸æè®²çå ³äº generator çç»èç¥è¯ï¼é£ä½ åºè¯¥ç¥éï¼ä»ææ¯ä¸è®²ï¼å¼æ¥ generator å常è§ç generator å¨å 鍿¯æåºå«çã
对äºå¼æ¥ generatorï¼generator.next() æ¹æ³æ¯å¼æ¥çï¼å®è¿å promiseã
å¨ä¸ä¸ªå¸¸è§ç generator ä¸ï¼æä»¬ä½¿ç¨ result = generator.next() æ¥è·å¾å¼ãä½å¨ä¸ä¸ªå¼æ¥ generator ä¸ï¼æä»¬åºè¯¥æ·»å await å
³é®åï¼åè¿æ ·ï¼
result = await generator.next(); // result = {value: ..., done: true/false}
è¿å°±æ¯ä¸ºä»ä¹å¼æ¥ generator å¯ä»¥ä¸ for await...of ä¸èµ·å·¥ä½ã
弿¥çå¯è¿ä»£å¯¹è±¡ range
常è§ç generator å¯ç¨ä½ Symbol.iterator 以使è¿ä»£ä»£ç æ´çã
ä¸ä¹ç±»ä¼¼ï¼å¼æ¥ generator å¯ç¨ä½ Symbol.asyncIterator æ¥å®ç°å¼æ¥è¿ä»£ã
ä¾å¦ï¼æä»¬å¯ä»¥éè¿å°åæ¥ç Symbol.iterator æ¿æ¢ä¸ºå¼æ¥ç Symbol.asyncIteratorï¼æ¥ä½¿å¯¹è±¡ range 弿¥å°çæå¼ï¼æ¯ç§çæä¸ä¸ªï¼
let range = {
from: 1,
to: 5,
// è¿ä¸è¡çä»·äº [Symbol.asyncIterator]: async function*() {
async *[Symbol.asyncIterator]() {
for(let value = this.from; value <= this.to; value++) {
// å¨ value ä¹é´æåä¸ä¼å¿ï¼çå¾
ä¸äºä¸è¥¿
await new Promise(resolve => setTimeout(resolve, 1000));
yield value;
}
}
};
(async () => {
for await (let value of range) {
alert(value); // 1ï¼ç¶å 2ï¼ç¶å 3ï¼ç¶å 4ï¼ç¶å 5
}
})();
ç°å¨ï¼value ä¹é´çå»¶è¿ä¸º 1 ç§ã
仿æ¯ä¸è®²ï¼æä»¬å¯ä»¥æ Symbol.iterator å Symbol.asyncIterator 齿·»å å°å¯¹è±¡ä¸ï¼å æ¤å®æ¢å¯ä»¥æ¯åæ¥çï¼for..ofï¼ä¹å¯ä»¥æ¯å¼æ¥çï¼for await..ofï¼å¯è¿ä»£å¯¹è±¡ã
使¯å®é ä¸ï¼è¿å°æ¯ä¸ä»¶å¾å¥æªçäºæ ã
å®é çä¾åï¼åé¡µçæ°æ®
å°ç®å为æ¢ï¼æä»¬å·²ç»äºè§£äºä¸äºåºæ¬ç¤ºä¾ï¼ä»¥å æ·±çè§£ãç°å¨ï¼æä»¬æ¥çä¸ä¸ªå®é çç¨ä¾ã
ç®åï¼æå¾å¤å¨çº¿æå¡é½æ¯åéçåé¡µçæ°æ®ï¼paginated dataï¼ãä¾å¦ï¼å½æä»¬éè¦ä¸ä¸ªç¨æ·å表æ¶ï¼ä¸ä¸ªè¯·æ±åªè¿åä¸ä¸ªé¢è®¾æ°éçç¨æ·ï¼ä¾å¦ 100 ä¸ªç¨æ·ï¼ââ âä¸é¡µâï¼å¹¶æä¾äºæåä¸ä¸é¡µç URLã
è¿ç§æ¨¡å¼é常常è§ãä¸ä» å¯ç¨äºè·åç¨æ·å表ï¼è¿ç§æ¨¡å¼è¿å¯ä»¥ç¨äºä»»æä¸è¥¿ã
ä¾å¦ï¼GitHub å 许使ç¨ç¸åçå页æäº¤ï¼paginated fashionï¼çæ¹å¼æ¾å commitï¼
- æä»¬åºè¯¥ä»¥
https://api.github.com/repos/<repo>/commitsæ ¼å¼å建è¿è¡fetchçç½ç»è¯·æ±ã - å®è¿åä¸ä¸ªå
å« 30 æ¡ commit ç JSONï¼å¹¶å¨è¿åç
Linkheader 䏿ä¾äºæåä¸ä¸é¡µç龿¥ã - ç¶åæä»¬å¯ä»¥å°è¯¥é¾æ¥ç¨äºä¸ä¸ä¸ªè¯·æ±ï¼ä»¥è·åæ´å¤ commitï¼ä»¥æ¤ç±»æ¨ã
å¯¹äºæä»¬ç代ç ï¼æä»¬å¸ææä¸ç§æ´ç®åçè·å commit çæ¹å¼ã
让æä»¬å建ä¸ä¸ªå½æ° fetchCommits(repo)ï¼ç¨æ¥å¨ä»»ä½æä»¬æéè¦çæ¶åååºè¯·æ±ï¼æ¥ä¸ºæä»¬è·å commitãå¹¶ä¸ï¼è¯¥å½æ°è½å¤å
³æ³¨å°ææå页å
容ãå¯¹äºæä»¬æ¥è¯´ï¼å®å°æ¯ä¸ä¸ªç®åç for await..of 弿¥è¿ä»£ã
å æ¤ï¼å ¶ç¨æ³å°å¦ä¸æç¤ºï¼
for await (let commit of fetchCommits("username/repository")) {
// å¤ç commit
}
éè¿å¼æ¥ generatorï¼æä»¬å¯ä»¥è½»æ¾å®ç°ä¸é¢ææè¿°ç彿°ï¼å¦ä¸æç¤ºï¼
async function* fetchCommits(repo) {
let url = `https://api.github.com/repos/${repo}/commits`;
while (url) {
const response = await fetch(url, { // (1)
headers: {'User-Agent': 'Our script'}, // github éè¦ä»»æç user-agent header
});
const body = await response.json(); // (2) ååºçæ¯ JSONï¼array of commitsï¼
// (3) åå¾ä¸ä¸é¡µç URL å¨ header ä¸ï¼æåå®
let nextPage = response.headers.get('Link').match(/<(.*?)>; rel="next"/);
nextPage = nextPage?.[1];
url = nextPage;
for(let commit of body) { // (4) ä¸ä¸ªæ¥ä¸ä¸ªå° yield commitï¼ç´å°æåä¸é¡µ
yield commit;
}
}
}
å ³äºå ¶å·¥ä½åççè¿ä¸æ¥è§£éï¼
-
æä»¬ä½¿ç¨æµè§å¨ç fetch æ¹æ³æ¥ä¸è½½ commitã
- åå§ URL æ¯
https://api.github.com/repos/<repo>/commitsï¼å¹¶ä¸ä¸ä¸é¡µç URL å°å¨ååºçLinkheader ä¸ã fetchæ¹æ³å 许æä»¬æä¾ææåå ¶ä» headerï¼å¦æéè¦ ââ è¿é GitHub éè¦çæ¯User-Agentã
- åå§ URL æ¯
-
commit 被以 JSON çæ ¼å¼è¿åã
-
æä»¬åºè¯¥ä»ååºï¼responseï¼ç
Linkheader ä¸è·ååå¾ä¸ä¸é¡µç URLã宿ä¸ä¸ªç¹æ®çæ ¼å¼ï¼æä»¥æä»¬å¯¹å®ä½¿ç¨æ£å表达å¼ï¼æä»¬å°å¨ æ£åè¡¨è¾¾å¼ ä¸ç« ä¸å¦ä¹ å®ï¼ã- åå¾ä¸ä¸é¡µç URL çèµ·æ¥å¯è½å°±åè¿æ ·
https://api.github.com/repositories/93253246/commits?page=2ãè¿æ¯ç± GitHub èªå·±çæçã
- åå¾ä¸ä¸é¡µç URL çèµ·æ¥å¯è½å°±åè¿æ ·
-
ç¶åï¼æä»¬å°æ¥æ¶å°çææ commit ä¸ä¸ªä¸ä¸ªå° yield åºæ¥ï¼å½ææ commit é½ yield 宿æ¶ï¼å°è§¦åä¸ä¸ä¸ª
while(url)è¿ä»£ï¼å¹¶ååºä¸ä¸ä¸ªè¯·æ±ã
è¿æ¯ä¸ä¸ªä½¿ç¨ç¤ºä¾ï¼å¨æ§å¶å°ä¸æ¾ç¤º commit çä½è ï¼
(async () => {
let count = 0;
for await (const commit of fetchCommits('javascript-tutorial/en.javascript.info')) {
console.log(commit.author.login);
if (++count == 100) { // 让æä»¬å¨è·åäº 100 个 commit æ¶åæ¢
break;
}
}
})();
// 注æï¼å¦æä½ å¨å¤é¨æ²ç®±ä¸è¿è¡å®ï¼ä½ éè¦æä¸é¢ç fetchCommits 彿°ç²è´´å°è¿å¿ã
è¿å°±æ¯æä»¬æ³è¦çã
ä»å¤é¨çä¸å°å页请æ±ï¼paginated requestsï¼çå 鍿ºå¶ã对æä»¬æ¥è¯´ï¼å®åªæ¯ä¸ä¸ªè¿å commit ç弿¥ generatorã
æ»ç»
常è§ç iterator å generator å¯ä»¥å¾å¥½å°å¤çé£äºä¸éè¦è±è´¹æ¶é´æ¥çæçæ°æ®ã
彿们ææå¼æ¥å°ï¼æå»¶è¿å°è·åæ°æ®æ¶ï¼å¯ä»¥ä½¿ç¨å®ä»¬ç弿¥çæ¬ï¼å¹¶ä¸ä½¿ç¨ for await..of æ¿ä»£ for..ofã
弿¥ iterator ä¸å¸¸è§ iterator å¨è¯æ³ä¸çåºå«ï¼
| Iterable | 弿¥ Iterable | |
|---|---|---|
| æä¾ iterator çå¯¹è±¡æ¹æ³ | Symbol.iterator |
Symbol.asyncIterator |
next() è¿åç弿¯ |
{value:â¦, done: true/false} |
resolve æ {value:â¦, done: true/false} ç Promise |
弿¥ generator ä¸å¸¸è§ generator å¨è¯æ³ä¸çåºå«ï¼
| Generator | 弿¥ generator | |
|---|---|---|
| å£°ææ¹å¼ | function* |
async function* |
next() è¿åç弿¯ |
{value:â¦, done: true/false} |
resolve æ {value:â¦, done: true/false} ç Promise |
å¨ Web å¼åä¸ï¼æä»¬ç»å¸¸ä¼éå°æ°æ®æµï¼å®ä»¬å段æµå¨ï¼flows chunk-by-chunkï¼ãä¾å¦ï¼ä¸è½½æä¸ä¼ 大æä»¶ã
æä»¬å¯ä»¥ä½¿ç¨å¼æ¥ generator æ¥å¤çæ¤ç±»æ°æ®ãå¼å¾æ³¨æçæ¯ï¼å¨ä¸äºç¯å¢ï¼ä¾å¦æµè§å¨ç¯å¢ä¸ï¼è¿æå¦ä¸ä¸ªè¢«ç§°ä¸º Streams ç APIï¼å®æä¾äºç¹æ®çæ¥å£æ¥å¤çæ¤ç±»æ°æ®æµï¼è½¬æ¢æ°æ®å¹¶å°æ°æ®ä»ä¸ä¸ªæ°æ®æµä¼ éå°å¦ä¸ä¸ªæ°æ®æµï¼ä¾å¦ï¼ä»ä¸ä¸ªå°æ¹ä¸è½½å¹¶ç«å³åéå°å ¶ä»å°æ¹ï¼ã
è¯è®º
<code>æ ç¾æå ¥åªæå 个è¯ç代ç ï¼æå ¥å¤è¡ä»£ç å¯ä»¥ä½¿ç¨<pre>æ ç¾ï¼å¯¹äºè¶ è¿ 10 è¡ç代ç ï¼å»ºè®®ä½ ä½¿ç¨æ²ç®±ï¼plnkrï¼JSBinï¼codepenâ¦ï¼