前提條件: | 基本的電腦知識(shí),對(duì)HTML與CSS有基本的了解,及已閱讀: JavaScript first steps(JS的入門). |
---|---|
目標(biāo): | 學(xué)習(xí)如何在JS里面使用循環(huán)語句. |
循環(huán),循環(huán),循環(huán). 就與這些:popular breakfast cereals, roller coasters and musical production一樣,類似存在于編程中.編程中的循環(huán)也是一直重復(fù)著去做一件事 - 此處循環(huán)便是編程中的術(shù)語.
讓我們來想一下下圖,這位農(nóng)夫考慮為他的家庭做一周的食物計(jì)劃,他或許就需要執(zhí)行一段循環(huán):
一段循環(huán)通常需要一個(gè)活多個(gè)條件:
在偽代碼中,這將類似于以下內(nèi)容:
loop(food = 0; foodNeeded = 10) { if (food = foodNeeded) { exit loop; // We have enough food; let's go home } else { food += 2; // Spend an hour collecting 2 more food // loop will then run again } }
因此,所需的食物量設(shè)置為10,并且農(nóng)民目前具有的量設(shè)置為0.在循環(huán)的每次迭代中,我們檢查農(nóng)民的食物量是否等于他需要的量。 如果是這樣,我們可以退出循環(huán)。 如果不是,農(nóng)民花了一個(gè)小時(shí)收集兩份食物,環(huán)路再次運(yùn)行。
在這一點(diǎn)上,你可能會(huì)理解循環(huán)的高層概念,但你可能認(rèn)為"OK,好,但是這如何幫助我編寫更好的JavaScript代碼? 正如我們之前所說,循環(huán)是一次又一次地做同樣的事情,這對(duì)于快速完成重復(fù)任務(wù)非常有用。
通常,代碼將在循環(huán)的每個(gè)連續(xù)迭代中略有不同,這意味著您可以完成類似但略有不同的任務(wù)的整個(gè)加載 - 如果您有很多不同的計(jì)算要做, 做每一個(gè)不同的,不一樣的一遍又一遍!
讓我們看一個(gè)例子來完美地說明為什么循環(huán)是這樣好的東西。 假設(shè)我們要在 < canvas>
元素上繪制100個(gè)隨機(jī)圓圈(按 更新按鈕一次運(yùn)行該示例以查看不同的隨機(jī)集):
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Random canvas circles</title> <style> html { width: 100%; height: inherit; background: #ddd; } canvas { display: block; } body { margin: 0; } button { position: absolute; top: 5px; left: 5px; } </style> </head> <body> <button>Update</button> <canvas></canvas> <script> var btn = document.querySelector('button'); var canvas = document.querySelector('canvas'); var ctx = canvas.getContext('2d'); var WIDTH = document.documentElement.clientWidth; var HEIGHT = document.documentElement.clientHeight; canvas.width = WIDTH; canvas.height = HEIGHT; function random(number) { return Math.floor(Math.random()*number); } function draw() { ctx.clearRect(0,0,WIDTH,HEIGHT); for (var i = 0; i < 100; i++) { ctx.beginPath(); ctx.fillStyle = 'rgba(255,0,0,0.5)'; ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI); ctx.fill(); } } btn.addEventListener('click',draw); </script> </body> </html>
你現(xiàn)在不必理解所有的代碼(你可以在GitHub上看到完整的源代碼,看到在單獨(dú)的窗口中運(yùn)行的示例),但讓我們看看實(shí)際畫出100個(gè)圓圈的代碼部分:
for (var i = 0; i < 100; i++) { ctx.beginPath(); ctx.fillStyle = 'rgba(255,0,0,0.5)'; ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI); ctx.fill(); }
你應(yīng)該得到基本的想法 - 我們使用一個(gè)循環(huán)來運(yùn)行這個(gè)代碼的100次迭代,每個(gè)迭代在頁面上的隨機(jī)位置繪制一個(gè)圓。 無論我們繪制100個(gè)圓,1000或10,000,所需的代碼量都是相同的。 只有一個(gè)數(shù)字必須更改。
如果我們沒有在這里使用循環(huán),我們必須為每個(gè)要繪制的圓重復(fù)以下代碼:
ctx.beginPath(); ctx.fillStyle = 'rgba(255,0,0,0.5)'; ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI); ctx.fill();
這將非常無聊,很難很快維持。 循環(huán)真的是最好的。
讓我們開始探索一些特定的循環(huán)結(jié)構(gòu)。 您最常使用的第一個(gè)是 a> loop - 這有以下語法:
for (initializer; exit-condition; final-expression) { // code to run }
這里我們有:
for
, followed by some parentheses.讓我們看一個(gè)真實(shí)的例子,所以我們可以想象這些做得更清楚。
var cats = ['Bill', 'Jeff', 'Pete', 'Biggles', 'Jasmin']; var info = 'My cats are called '; var para = document.querySelector('p'); for (var i = 0; i < cats.length; i++) { info += cats[i] + ', '; } para.textContent = info;
這給了我們以下輸出:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Basic for loop example</title> <style> </style> </head> <body> <p></p> <script> var cats = ['Bill', 'Jeff', 'Pete', 'Biggles', 'Jasmin']; var info = 'My cats are called '; var para = document.querySelector('p'); for (var i = 0; i < cats.length; i++) { info += cats[i] + ', '; } para.textContent = info; </script> </body> </html>
這顯示了一個(gè)循環(huán),用于遍歷數(shù)組中的項(xiàng)目,并對(duì)其中的每一個(gè)執(zhí)行一些操作 - 這是JavaScript中的一種常見模式。 這里:
i
, starts at 0
(var i = 0
).i < cats.length
is still true, the loop will still run.cats[i]
is cats[whatever i is at the time]
) along with a comma and a space, onto the end of the info
variable. So: i = 0
, so cats[0] + ', '
will be concatenated onto info ("Bill, ").i = 1
, so cats[1] + ', '
will be concatenated onto info ("Jeff, ")i
(i++
), then the process will start again.i
becomes equal to cats.length
, the loop will stop, and the browser will move on to the next bit of code below the loop.注意:我們已設(shè)置退出條件 i<
cats.length 而不是 i ,因?yàn)橛?jì)算機(jī)從0開始計(jì)數(shù),而不是1 - 我們?cè)?code>
/ code>,并向上到 i = 4
(最后一個(gè)數(shù)組項(xiàng)的索引)。 cats.length
返回5,因?yàn)閿?shù)組中有5個(gè)項(xiàng)目,但我們不想到 i = 5
,因?yàn)闀?huì)返回 未定義(沒有索引為5的數(shù)組項(xiàng))。 因此,我們想要比
cats.length
( i )少1,而不是
code> i )。 cats.length
注意:退出條件的常見錯(cuò)誤是使用"等于"而不是說"小于或等于"。 如果我們想要運(yùn)行循環(huán)直到i = 5,退出條件將需要是i 等于5在第一次循環(huán)迭代,所以它會(huì)立即停止。
我們剩下的一個(gè)小問題是最后的輸出句子不是很好:
我的貓叫Bill,Jeff,Pete,Biggles,Jasmin,
理想情況下,我們想改變最后循環(huán)迭代的連接,以便我們?cè)诰渥幽┪矝]有逗號(hào)。 嗯,沒有問題 - 我們可以很高興地在我們的for循環(huán)中插入一個(gè)條件來處理這個(gè)特殊情況:
for (var i = 0; i < cats.length; i++) { if (i === cats.length - 1) { info += 'and ' + cats[i] + '.'; } else { info += cats[i] + ', '; } }
重要:使用for - 與所有循環(huán)一樣,您必須確保初始化程序被迭代,以便最終達(dá)到退出條件。 如果沒有,循環(huán)將永遠(yuǎn)繼續(xù),瀏覽器將強(qiáng)制它停止,否則會(huì)崩潰。 這稱為無限循環(huán)。
如果要在所有迭代完成之前退出循環(huán),可以使用 > break 語句。 當(dāng)我們查看切換語句時(shí),在switch語句中遇到一個(gè)情況時(shí),我們?cè)谇耙黄恼轮幸呀?jīng)滿足了這一點(diǎn) 匹配輸入表達(dá)式,break語句立即退出switch語句并移動(dòng)到它之后的代碼。
它與循環(huán)一樣 - break
語句將立即退出循環(huán),并使瀏覽器移動(dòng)到其后的任何代碼。
說我們想要通過一系列聯(lián)系人和電話號(hào)碼進(jìn)行搜索,并只返回我們想要找到的號(hào)碼? 首先是一些簡單的HTML - < input>
允許我們輸入名稱 以搜索要提交搜索的 < button>
元素, a href ="/ zh-CN / docs / Web / HTML / Element / p"> < p>
元素,
<label for="search">Search by contact name: </label> <input id="search" type="text"> <button>Search</button> <p></p>
現(xiàn)在對(duì)JavaScript:
var contacts = ['Chris:2232322', 'Sarah:3453456', 'Bill:7654322', 'Mary:9998769', 'Dianne:9384975']; var para = document.querySelector('p'); var input = document.querySelector('input'); var btn = document.querySelector('button'); btn.addEventListener('click', function() { var searchName = input.value; input.value = ''; input.focus(); for (var i = 0; i < contacts.length; i++) { var splitContact = contacts[i].split(':'); if (splitContact[0] === searchName) { para.textContent = splitContact[0] + '\'s number is ' + splitContact[1] + '.'; break; } else { para.textContent = 'Contact not found.'; } } });
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Simple contact search example</title> <style> </style> </head> <body> <label for="search">Search by contact name: </label> <input id="search" type="text"> <button>Search</button> <p></p> <script> var contacts = ['Chris:2232322', 'Sarah:3453456', 'Bill:7654322', 'Mary:9998769', 'Dianne:9384975']; var para = document.querySelector('p'); var input = document.querySelector('input'); var btn = document.querySelector('button'); btn.addEventListener('click', function() { var searchName = input.value; input.value = ''; input.focus(); for (var i = 0; i < contacts.length; i++) { var splitContact = contacts[i].split(':'); if (splitContact[0] === searchName) { para.textContent = splitContact[0] + '\'s number is ' + splitContact[1] + '.'; break; } else { para.textContent = 'Contact not found.'; } } }); </script> </body> </html>
btn
), so that when it is pressed, some code is run to perform the search and return the results.searchName
, before then emptying the text input and focusing it again, ready for the next search.0
, run the loop until the counter is no longer less than contacts.length
, and increment i
by 1 after each iteration of the loop.contacts[i]
) at the colon character, and store the resulting two values in an array called splitContact
.splitContact[0]
(the contact's name) is equal to the inputted searchName
. If it is, we enter a string into the paragraph to report what the contact's number is, and use break
to end the loop.繼續(xù)語句的工作方式與 break / code>,但是不是完全打破循環(huán),而是跳過循環(huán)的下一次迭代。 讓我們看看另一個(gè)例子,它接受一個(gè)數(shù)字作為輸入,并且只返回整數(shù)的正方形數(shù)字(整數(shù))。
HTML基本上與上一個(gè)示例相同 - 一個(gè)簡單的文本輸入和一個(gè)用于輸出的段落。 JavaScript大部分是相同的,雖然循環(huán)本身有點(diǎn)不同:
var num = input.value; for (var i = 1; i <= num; i++) { var sqRoot = Math.sqrt(i); if (Math.floor(sqRoot) !== sqRoot) { continue; } para.textContent += i + ' '; }
這里是輸出:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Integer squares generator</title> <style> </style> </head> <body> <label for="number">Enter number: </label> <input id="number" type="text"> <button>Generate integer squares</button> <p>Output: </p> <script> var para = document.querySelector('p'); var input = document.querySelector('input'); var btn = document.querySelector('button'); btn.addEventListener('click', function() { para.textContent = 'Output: '; var num = input.value; input.value = ''; input.focus(); for (var i = 1; i <= num; i++) { var sqRoot = Math.sqrt(i); if (Math.floor(sqRoot) !== sqRoot) { continue; } para.textContent += i + ' '; } }); </script> </body> </html>
num
). The for
loop is given a counter starting at 1 (as we are not interested in 0 in this case), an exit condition that says the loop will stop when the counter becomes bigger than the input num
, and an iterator that adds 1 to the counter each time.!==
), it means that the square root is not an integer, so we are not interested in it. In such a case, we use the continue
statement to skip on to the next loop iteration without recording the number anywhere.continue
statement is not executed; instead, we concatenate the current i
value plus a space on to the end of the paragraph content. for
不是JavaScript中唯一可用的循環(huán)類型。 實(shí)際上有很多其他的,雖然你現(xiàn)在不需要了解所有這些,但值得看看一些其他人的結(jié)構(gòu),以便你可以以一種稍微不同的方式在工作中識(shí)別相同的功能。
首先,讓我們看看 while 循環(huán)。 這個(gè)循環(huán)的語法如下:
initializer while (exit-condition) { // code to run final-expression }
除了在循環(huán)之前設(shè)置初始化器變量,并且final-expression包含在運(yùn)行代碼之后的循環(huán)內(nèi),而不是括號(hào)內(nèi)包含這兩個(gè)項(xiàng)目之外,它的工作方式與for循環(huán)非常相似。 退出條件包含在括號(hào)內(nèi),其前面有 while
關(guān)鍵字,而不是的。
同樣的三個(gè)項(xiàng)目仍然存在,它們?nèi)匀灰耘c它們?cè)趂or循環(huán)中相同的順序定義 - 這是有意義的,因?yàn)槟匀槐仨毝x初始化器,然后才能檢查它是否已達(dá)到退出條件 ; 最終條件然后在循環(huán)中的代碼已經(jīng)運(yùn)行(迭代已經(jīng)完成)之后運(yùn)行,這將僅在仍然未達(dá)到退出條件時(shí)發(fā)生。
讓我們?cè)倏纯次覀兊腸ats列表示例,但重寫為使用while循環(huán):
var i = 0; while (i < cats.length) { if (i === cats.length - 1) { info += 'and ' + cats[i] + '.'; } else { info += cats[i] + ', '; } i++; }
注意:此效果仍然與預(yù)期相同 - 請(qǐng)查看 while.html"class ="external">在GitHub上運(yùn)行(也可以查看 /loops/while.html"class ="external">完整的源代碼)。
do ... while 循環(huán)非常相似 ,但提供了while結(jié)構(gòu)的變化:
initializer do { // code to run final-expression } while (exit-condition)
在這種情況下,初始化器再次來到循環(huán)開始之前。 do
關(guān)鍵字直接位于包含要運(yùn)行的代碼和final-expression的花括號(hào)之前。
這里的區(qū)別是退出條件在所有其他之后,包裹在括號(hào)中,并且在 while
關(guān)鍵字之前。 在 do ... while
循環(huán)中,花括號(hào)中的代碼總是運(yùn)行一次,然后再進(jìn)行檢查,看看是否應(yīng)該再次執(zhí)行(在while和for中,檢查首先, 所以代碼可能永遠(yuǎn)不會(huì)被執(zhí)行)。
讓我們重寫我們的cat列表示例以使用 do ... while
loop:
var i = 0; do { if (i === cats.length - 1) { info += 'and ' + cats[i] + '.'; } else { info += cats[i] + ', '; } i++; } while (i < cats.length);
注意:同樣,此工作方式與預(yù)期相同 - 請(qǐng)查看 /do-while.html"class ="external">在GitHub上運(yùn)行(還可以查看 building-blocks / loops / do-while.html"class ="external">完整的源代碼)。
重要:使用while和do ... while - 與所有循環(huán)一樣,您必須確保初始化器被迭代,以便最終達(dá)到退出條件。 如果沒有,循環(huán)將永遠(yuǎn)繼續(xù),瀏覽器將強(qiáng)制它停止,否則會(huì)崩潰。 這稱為無限循環(huán)。
在本練習(xí)中,我們希望您打印出一個(gè)簡單的啟動(dòng)倒計(jì)時(shí)到輸出框,從10下降到Blast off。 具體來說,我們希望您:
var i = 10;
.<div>
, which we've selected using var output = document.querySelector('.output');
. In comments, we've provided you with three code lines that need to be used somewhere inside the loop: var para = document.createElement('p');
— creates a new paragraph.output.appendChild(para);
— appends the paragraph to the output <div>
.para.textContent =
— makes the text inside the paragraph equal to whatever you put on the right hand side, after the equals sign.para.textContent =
lines): i++
— how do you iterate downwards?如果出錯(cuò),您可以隨時(shí)使用"重置"按鈕重置示例。 如果你真的卡住,按"顯示解決方案"看到一個(gè)解決方案。
<div class="output" style="height: 410px;overflow: auto;"> </div> <textarea id="code" class="playable-code" style="height: 300px;"> var output = document.querySelector('.output'); output.innerHTML = ''; // var i = 10; // var para = document.createElement('p'); // para.textContent = ; // output.appendChild(para); </textarea> <div class="playable-buttons"> <input id="reset" type="button" value="Reset"> <input id="solution" type="button" value="Show solution"> </div>
var textarea = document.getElementById('code'); var reset = document.getElementById('reset'); var solution = document.getElementById('solution'); var code = textarea.value; function updateCode() { eval(textarea.value); } reset.addEventListener('click', function() { textarea.value = code; updateCode(); }); solution.addEventListener('click', function() { textarea.value = jsSolution; updateCode(); }); var jsSolution = 'var output = document.querySelector(\'.output\');\noutput.innerHTML = \'\';\n\nvar i = 10;\n\nwhile(i >= 0) {\n var para = document.createElement(\'p\');\n if(i === 10) {\n para.textContent = \'Countdown \' + i;\n } else if(i === 0) {\n ?para.textContent = \'Blast off!\';\n } else {\n para.textContent = i;\n }\n\n output.appendChild(para);\n\n i--;\n}'; textarea.addEventListener('input', updateCode); window.addEventListener('load', updateCode);
在本練習(xí)中,我們希望您獲取存儲(chǔ)在數(shù)組中的名稱列表,并將其放入guest虛擬機(jī)列表中。 但它不是那么容易 - 我們不想讓菲爾和洛拉因?yàn)樗麄冐澙泛痛拄?,總是吃所有的食物?我們有兩個(gè)名單,一個(gè)是客人承認(rèn),一個(gè)是客人拒絕。
具體來說,我們希望您:
people
array. You'll need to start with an initializer of? var i = 0;
, but what exit condition do you need?refused
paragraph's textContent
, followed by a comma and a space.admitted
paragraph's textContent
, followed by a comma and a space.我們已經(jīng)為您提供:
var i = 0;
— Your initializer.refused.textContent +=
— the beginnings of a line that will concatenate something on to the end of refused.textContent
.admitted.textContent +=
— the beginnings of a line that will concatenate something on to the end of admitted.textContent
.額外的獎(jiǎng)金問題 - 成功完成上述任務(wù)后,您將留下兩個(gè)名稱列表,用逗號(hào)分隔,但它們將不整潔 - 每個(gè)結(jié)尾處都有一個(gè)逗號(hào)。 你能找出如何編寫在每種情況下切分最后一個(gè)逗號(hào)的行,并添加一個(gè)完整的句點(diǎn)到底? 有關(guān)幫助,請(qǐng)查看有用的字符串方法文章。
如果出錯(cuò),您可以隨時(shí)使用"重置"按鈕重置示例。 如果你真的卡住,按"顯示解決方案"看到一個(gè)解決方案。
<div class="output" style="height: 100px;overflow: auto;"> <p class="admitted">Admit: </p> ? <p class="refused">Refuse: </p> </div> <textarea id="code" class="playable-code" style="height: 400px;"> var people = ['Chris', 'Anne', 'Colin', 'Terri', 'Phil', 'Lola', 'Sam', 'Kay', 'Bruce']; var admitted = document.querySelector('.admitted'); var refused = document.querySelector('.refused'); admitted.textContent = 'Admit: '; refused.textContent = 'Refuse: ' // var i = 0; // refused.textContent += ; // admitted.textContent += ; </textarea> <div class="playable-buttons"> <input id="reset" type="button" value="Reset"> <input id="solution" type="button" value="Show solution"> </div>
var textarea = document.getElementById('code'); var reset = document.getElementById('reset'); var solution = document.getElementById('solution'); var code = textarea.value; function updateCode() { eval(textarea.value); } reset.addEventListener('click', function() { textarea.value = code; updateCode(); }); solution.addEventListener('click', function() { textarea.value = jsSolution; updateCode(); }); var jsSolution = 'var people = [\'Chris\', \'Anne\', \'Colin\', \'Terri\', \'Phil\', \'Lola\', \'Sam\', \'Kay\', \'Bruce\'];\n\nvar admitted = document.querySelector(\'.admitted\');\nvar refused = document.querySelector(\'.refused\');\n\nvar i = 0;\n\ndo {\n if(people[i] === \'Phil\' || people[i] === \'Lola\') {\n refused.textContent += people[i] + \', \';\n } else {\n admitted.textContent += people[i] + \', \';\n }\n i++;\n} while(i < people.length);\n\nrefused.textContent = refused.textContent.slice(0,refused.textContent.length-2) + \'.\';\nadmitted.textContent = admitted.textContent.slice(0,admitted.textContent.length-2) + \'.\';'; textarea.addEventListener('input', updateCode); window.addEventListener('load', updateCode);
對(duì)于基本使用,
, while
和 do ... while
循環(huán)在很大程度上是可互換的。 他們都可以用來解決相同的問題,你使用哪一個(gè)很大程度上取決于你的個(gè)人喜好 - 哪一個(gè)你發(fā)現(xiàn)最容易記住或最直觀。 讓我們?cè)倏纯匆幌隆?/p>
第一個(gè) for
:
for (initializer; exit-condition; final-expression) { // code to run }
while
:
initializer while (exit-condition) { // code to run final-expression }
最后 do ... while
:
initializer do { // code to run final-expression } while (exit-condition)
我們推薦 for
,至少從開始,因?yàn)樗赡苁亲钊菀子涀∫磺?- 初始化,退出條件和最終表達(dá)式都必須整齊地放在括號(hào)中,所以它 很容易看到他們?cè)谀睦?,并檢查,你不是錯(cuò)過他們。
注意:還有其他循環(huán)類型/功能,這在高級(jí)/特殊情況下有用,超出了本文的范圍。 如果您想進(jìn)一步了解循環(huán)學(xué)習(xí),請(qǐng)參閱我們的高級(jí)循環(huán)和迭代指南 a>。
本文向您展示了基本概念,以及在JavaScript中循環(huán)代碼時(shí)可用的不同選項(xiàng)。 你現(xiàn)在應(yīng)該明確為什么循環(huán)是一個(gè)處理重復(fù)代碼的好機(jī)制,并且在你自己的例子中使用它們。
如果您有任何不明白的地方,請(qǐng)隨時(shí)閱讀本文,或與我們聯(lián)系以尋求幫助。
為循環(huán)編寫JavaScript的最佳方式是什么? - 一些高級(jí)循環(huán)最佳做法
更多建議: