前提條件: | 基本的電腦知識,對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)夫考慮為他的家庭做一周的食物計劃,他或許就需要執(zhí)行一段循環(huán):
一段循環(huán)通常需要一個活多個條件:
在偽代碼中,這將類似于以下內(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)民花了一個小時收集兩份食物,環(huán)路再次運行。
在這一點上,你可能會理解循環(huán)的高層概念,但你可能認為"OK,好,但是這如何幫助我編寫更好的JavaScript代碼? 正如我們之前所說,循環(huán)是一次又一次地做同樣的事情,這對于快速完成重復(fù)任務(wù)非常有用。
通常,代碼將在循環(huán)的每個連續(xù)迭代中略有不同,這意味著您可以完成類似但略有不同的任務(wù)的整個加載 - 如果您有很多不同的計算要做, 做每一個不同的,不一樣的一遍又一遍!
讓我們看一個例子來完美地說明為什么循環(huán)是這樣好的東西。 假設(shè)我們要在 < canvas>
元素上繪制100個隨機圓圈(按 更新按鈕一次運行該示例以查看不同的隨機集):
<!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上看到完整的源代碼,看到在單獨的窗口中運行的示例),但讓我們看看實際畫出100個圓圈的代碼部分:
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)該得到基本的想法 - 我們使用一個循環(huán)來運行這個代碼的100次迭代,每個迭代在頁面上的隨機位置繪制一個圓。 無論我們繪制100個圓,1000或10,000,所需的代碼量都是相同的。 只有一個數(shù)字必須更改。
如果我們沒有在這里使用循環(huán),我們必須為每個要繪制的圓重復(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)。 您最常使用的第一個是 a> loop - 這有以下語法:
for (initializer; exit-condition; final-expression) { // code to run }
這里我們有:
for
, followed by some parentheses.讓我們看一個真實的例子,所以我們可以想象這些做得更清楚。
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>
注意:您可以找到 class ="external"> GitHub上的示例代碼(也 class ="external">看到它正在運行)。
這顯示了一個循環(huán),用于遍歷數(shù)組中的項目,并對其中的每一個執(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 ,因為計算機從0開始計數(shù),而不是1 - 我們在
/ code>,并向上到 i = 4
(最后一個數(shù)組項的索引)。 cats.length
返回5,因為數(shù)組中有5個項目,但我們不想到 i = 5
,因為會返回 未定義(沒有索引為5的數(shù)組項)。 因此,我們想要比
cats.length
( i )少1,而不是
code> i )。 cats.length
注意:退出條件的常見錯誤是使用"等于"而不是說"小于或等于"。 如果我們想要運行循環(huán)直到i = 5,退出條件將需要是i 等于5在第一次循環(huán)迭代,所以它會立即停止。
我們剩下的一個小問題是最后的輸出句子不是很好:
我的貓叫Bill,Jeff,Pete,Biggles,Jasmin,
理想情況下,我們想改變最后循環(huán)迭代的連接,以便我們在句子末尾沒有逗號。 嗯,沒有問題 - 我們可以很高興地在我們的for循環(huán)中插入一個條件來處理這個特殊情況:
for (var i = 0; i < cats.length; i++) { if (i === cats.length - 1) { info += 'and ' + cats[i] + '.'; } else { info += cats[i] + ', '; } }
重要:使用for - 與所有循環(huán)一樣,您必須確保初始化程序被迭代,以便最終達到退出條件。 如果沒有,循環(huán)將永遠繼續(xù),瀏覽器將強制它停止,否則會崩潰。 這稱為無限循環(huán)。
如果要在所有迭代完成之前退出循環(huán),可以使用 > break 語句。 當(dāng)我們查看切換語句時,在switch語句中遇到一個情況時,我們在前一篇文章中已經(jīng)滿足了這一點 匹配輸入表達式,break語句立即退出switch語句并移動到它之后的代碼。
它與循環(huán)一樣 - break
語句將立即退出循環(huán),并使瀏覽器移動到其后的任何代碼。
說我們想要通過一系列聯(lián)系人和電話號碼進行搜索,并只返回我們想要找到的號碼? 首先是一些簡單的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)在對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)的下一次迭代。 讓我們看看另一個例子,它接受一個數(shù)字作為輸入,并且只返回整數(shù)的正方形數(shù)字(整數(shù))。
HTML基本上與上一個示例相同 - 一個簡單的文本輸入和一個用于輸出的段落。 JavaScript大部分是相同的,雖然循環(huá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)類型。 實際上有很多其他的,雖然你現(xiàn)在不需要了解所有這些,但值得看看一些其他人的結(jié)構(gòu),以便你可以以一種稍微不同的方式在工作中識別相同的功能。
首先,讓我們看看 while 循環(huán)。 這個循環(huán)的語法如下:
initializer while (exit-condition) { // code to run final-expression }
除了在循環(huán)之前設(shè)置初始化器變量,并且final-expression包含在運行代碼之后的循環(huán)內(nèi),而不是括號內(nèi)包含這兩個項目之外,它的工作方式與for循環(huán)非常相似。 退出條件包含在括號內(nèi),其前面有 while
關(guān)鍵字,而不是的。
同樣的三個項目仍然存在,它們?nèi)匀灰耘c它們在for循環(huán)中相同的順序定義 - 這是有意義的,因為您仍然必須定義初始化器,然后才能檢查它是否已達到退出條件 ; 最終條件然后在循環(huán)中的代碼已經(jīng)運行(迭代已經(jīng)完成)之后運行,這將僅在仍然未達到退出條件時發(fā)生。
讓我們再看看我們的cats列表示例,但重寫為使用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ù)期相同 - 請查看 while.html"class ="external">在GitHub上運行(也可以查看 /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)鍵字直接位于包含要運行的代碼和final-expression的花括號之前。
這里的區(qū)別是退出條件在所有其他之后,包裹在括號中,并且在 while
關(guān)鍵字之前。 在 do ... while
循環(huán)中,花括號中的代碼總是運行一次,然后再進行檢查,看看是否應(yīng)該再次執(zhí)行(在while和for中,檢查首先, 所以代碼可能永遠不會被執(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ù)期相同 - 請查看 /do-while.html"class ="external">在GitHub上運行(還可以查看 building-blocks / loops / do-while.html"class ="external">完整的源代碼)。
重要:使用while和do ... while - 與所有循環(huán)一樣,您必須確保初始化器被迭代,以便最終達到退出條件。 如果沒有,循環(huán)將永遠繼續(xù),瀏覽器將強制它停止,否則會崩潰。 這稱為無限循環(huán)。
在本練習(xí)中,我們希望您打印出一個簡單的啟動倒計時到輸出框,從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?如果出錯,您可以隨時使用"重置"按鈕重置示例。 如果你真的卡住,按"顯示解決方案"看到一個解決方案。
<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í)中,我們希望您獲取存儲在數(shù)組中的名稱列表,并將其放入guest虛擬機列表中。 但它不是那么容易 - 我們不想讓菲爾和洛拉因為他們貪婪和粗魯,總是吃所有的食物! 我們有兩個名單,一個是客人承認,一個是客人拒絕。
具體來說,我們希望您:
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
.額外的獎金問題 - 成功完成上述任務(wù)后,您將留下兩個名稱列表,用逗號分隔,但它們將不整潔 - 每個結(jié)尾處都有一個逗號。 你能找出如何編寫在每種情況下切分最后一個逗號的行,并添加一個完整的句點到底? 有關(guān)幫助,請查看有用的字符串方法文章。
如果出錯,您可以隨時使用"重置"按鈕重置示例。 如果你真的卡住,按"顯示解決方案"看到一個解決方案。
<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);
對于基本使用,
, while
和 do ... while
循環(huán)在很大程度上是可互換的。 他們都可以用來解決相同的問題,你使用哪一個很大程度上取決于你的個人喜好 - 哪一個你發(fā)現(xiàn)最容易記住或最直觀。 讓我們再看看一下。
第一個 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
,至少從開始,因為它可能是最容易記住一切 - 初始化,退出條件和最終表達式都必須整齊地放在括號中,所以它 很容易看到他們在哪里,并檢查,你不是錯過他們。
注意:還有其他循環(huán)類型/功能,這在高級/特殊情況下有用,超出了本文的范圍。 如果您想進一步了解循環(huán)學(xué)習(xí),請參閱我們的高級循環(huán)和迭代指南 a>。
本文向您展示了基本概念,以及在JavaScript中循環(huán)代碼時可用的不同選項。 你現(xiàn)在應(yīng)該明確為什么循環(huán)是一個處理重復(fù)代碼的好機制,并且在你自己的例子中使用它們。
如果您有任何不明白的地方,請隨時閱讀本文,或與我們聯(lián)系以尋求幫助。
為循環(huán)編寫JavaScript的最佳方式是什么? - 一些高級循環(huán)最佳做法
更多建議: