編寫程序時(shí),我們經(jīng)常需要重復(fù)執(zhí)行某些操作,這時(shí)候循環(huán)結(jié)構(gòu)就顯得非常有用。JavaScript 提供了多種循環(huán)結(jié)構(gòu),以適應(yīng)不同的編程場景。以下是 JavaScript 中常見的循環(huán)結(jié)構(gòu):
for 循環(huán)是最常見的循環(huán)結(jié)構(gòu)之一,它允許我們指定循環(huán)開始前的初始化代碼、循環(huán)繼續(xù)的條件以及每次循環(huán)結(jié)束時(shí)要執(zhí)行的代碼。
for (初始化表達(dá)式; 循環(huán)條件; 循環(huán)后的操作表達(dá)式) {
// 循環(huán)體代碼
}
for (var i=1; i <=10; i++) {
console.log(i);
}
while 循環(huán)在給定條件為真時(shí)將不斷循環(huán)執(zhí)行代碼塊。與 for 循環(huán)不同,while 循環(huán)只有循環(huán)條件,沒有初始化和迭代表達(dá)式。
while (條件) {
// 循環(huán)體代碼
}
var i=1;
while (i <=10) {
console.log(i);
i++;
}
do...while 循環(huán)和 while 循環(huán)類似,但它至少會(huì)執(zhí)行一次循環(huán)體,無論條件是否為真。
do {
// 循環(huán)體代碼
} while (條件);
var i=1;
do {
console.log(i);
i++;
} while (i <=10);
for...in 循環(huán)用于遍歷對(duì)象的屬性。
for (var key in 對(duì)象) {
// 使用 key 訪問對(duì)象屬性
}
var person={
name: "張三",
age: 30,
job: "軟件工程師"
};
for (var key in person) {
console.log(key + ": " + person[key]);
}
for...of 循環(huán)用于遍歷可迭代對(duì)象(如數(shù)組、字符串等)的元素。
for (var item of 可迭代對(duì)象) {
// 使用 item 訪問元素
}
var fruits=["蘋果", "香蕉", "橘子"];
for (var fruit of fruits) {
console.log(fruit);
}
JavaScript 的循環(huán)結(jié)構(gòu)為我們提供了強(qiáng)大的工具來處理重復(fù)任務(wù)。for 循環(huán)適合于當(dāng)我們知道循環(huán)次數(shù)時(shí)使用;while 和 do...while 循環(huán)適合于循環(huán)次數(shù)未知,但是循環(huán)條件明確的情況;for...in 和 for...of 循環(huán)則讓對(duì)象和數(shù)組的遍歷變得更加簡潔。掌握這些循環(huán)結(jié)構(gòu)有助于我們編寫更加高效和可讀性更強(qiáng)的代碼。
件語句的代碼可以被想象成是一條條分支的路徑,而循環(huán)語句的代碼則是程序路徑的一個(gè)回路,可以讓一部分代碼重復(fù)執(zhí)行。JavaScript中的循環(huán)語句有for語句和while語句。
for語句的語法如下:
1 for(初始值;布爾值;計(jì)數(shù)器){
2 //語句塊
3 }
在for語句中,如果布爾值是true,就會(huì)一直執(zhí)行語句塊中的內(nèi)容,為了防止死循環(huán),需要有一個(gè)計(jì)數(shù)器,當(dāng)數(shù)值達(dá)到指定值,布爾值就會(huì)變成false,循環(huán)便停止了,下面的示例代碼使用for循環(huán)輸出0~9九個(gè)數(shù)字示例代碼如下:
1 for(var i=0;i<10;i++){
2 // i的初始值是0
3 // 判斷i是否小于10,如果小于10則執(zhí)行花括號(hào)中的代碼
4 // 每次執(zhí)行完花括號(hào)中的代碼后,i的值加1
5 console.log(i);
6 }
通過上面的例子我們進(jìn)一步理解了for語句的用法,下面我們來做一個(gè)聯(lián)系,利用for循環(huán)語句輸出100以內(nèi)所有正整數(shù)的加和示例代碼如下:
1 var sum=0; //sum用來存儲(chǔ)循環(huán)過程中正整數(shù)的加和
2 for(var i=1;i<=100;i++){
3 sum +=i;
4 }
5 console.log(sum); //這時(shí)候輸出的就應(yīng)該是5050
while語句語法如下所示:
1 while(bool){
2 //bool為true,循環(huán)執(zhí)行
3 }
當(dāng)bool為true的時(shí)候,花括號(hào)中的內(nèi)容會(huì)循環(huán)執(zhí)行。為了防止死循環(huán),需要在循環(huán)的過程實(shí)現(xiàn)類似for計(jì)數(shù)器的功能,讓循環(huán)在有限的次數(shù)內(nèi)定制,下面我們使用while語句輸出0~9是個(gè)數(shù)字示例代碼如下:
1 var n=0;
2 while(n<10){
3 console.log(n);
4 n++;
5 }
在每次循環(huán)的過程中都會(huì)讓n的值加1,這樣當(dāng)n的值等于10,循環(huán)便停止,下面我來使用while語句輸出100以內(nèi)所有正整數(shù)的加和示例代碼如下:
1 var n=0;
2 var sum=0;
3 while(n<=100){
4 sum +=n;
5 n++;
6 }
7 console.log(sum);
continue可以結(jié)束本次循環(huán),直接進(jìn)入到下一次循環(huán),例如我們用for循環(huán)語句來實(shí)現(xiàn)輸出0 ~ 5,7 ~ 9九個(gè)數(shù)字(跳過6)示例代碼如下:
1 for(var i=0;i<10;i++){
2 if(i===6){
3 continue;
4 }
5 console.log(i);
6 }
上面的代碼通過判斷,實(shí)現(xiàn)當(dāng)i的值為6的時(shí)候,跳過本次循環(huán),直接接入下一次循環(huán)。下面我們使用continue來實(shí)現(xiàn)計(jì)算100以內(nèi)所有不能被7整除的正整數(shù)加和示例代碼如下:
1 var sum=0;
2 for(var i=0;i<=100;i++){
3 if(i%7===0){
4 continue;
5 }
6 sum +=i;
7 }
8 console.log(sum);
在學(xué)switch語句中,我們已經(jīng)接觸到了break,它可以讓分支語句在結(jié)束一個(gè)case之后,跳出switch語句,break同樣可以用在循環(huán)語句當(dāng)中,當(dāng)代碼執(zhí)行到break時(shí),直接結(jié)束循環(huán)示例代碼如下:
1 for(var i=0;i<10;i++){
2 if(i===6){
3 break;
4 }
5 console.log(i);
6 }
如上面的代碼所示,當(dāng)控制帶輸出5之后,循環(huán)結(jié)束。
【融職教育】在工作中學(xué)習(xí),在學(xué)習(xí)中工作
wift 使用自動(dòng)引用計(jì)數(shù)(ARC)這一機(jī)制來跟蹤和管理應(yīng)用程序的內(nèi)存
通常情況下我們不需要去手動(dòng)釋放內(nèi)存,因?yàn)?ARC 會(huì)在類的實(shí)例不再被使用時(shí),自動(dòng)釋放其占用的內(nèi)存。
但在有些時(shí)候我們還是需要在代碼中實(shí)現(xiàn)內(nèi)存管理。
ARC 功能
當(dāng)每次使用 init() 方法創(chuàng)建一個(gè)類的新的實(shí)例的時(shí)候,ARC 會(huì)分配一大塊內(nèi)存用來儲(chǔ)存實(shí)例的信息。
內(nèi)存中會(huì)包含實(shí)例的類型信息,以及這個(gè)實(shí)例所有相關(guān)屬性的值。
當(dāng)實(shí)例不再被使用時(shí),ARC 釋放實(shí)例所占用的內(nèi)存,并讓釋放的內(nèi)存能挪作他用。
為了確保使用中的實(shí)例不會(huì)被銷毀,ARC 會(huì)跟蹤和計(jì)算每一個(gè)實(shí)例正在被多少屬性,常量和變量所引用。
實(shí)例賦值給屬性、常量或變量,它們都會(huì)創(chuàng)建此實(shí)例的強(qiáng)引用,只要強(qiáng)引用還在,實(shí)例是不允許被銷毀的。
ARC 實(shí)例
class Person { let name: String init(name: String) { self.name=name print("\(name) 開始初始化") } deinit { print("\(name) 被析構(gòu)") }}// 值會(huì)被自動(dòng)初始化為nil,目前還不會(huì)引用到Person類的實(shí)例var reference1: Person?var reference2: Person?var reference3: Person?// 創(chuàng)建Person類的新實(shí)例reference1=Person(name: "Runoob")//賦值給其他兩個(gè)變量,該實(shí)例又會(huì)多出兩個(gè)強(qiáng)引用reference2=reference1 reference3=reference1//斷開第一個(gè)強(qiáng)引用reference1=nil//斷開第二個(gè)強(qiáng)引用reference2=nil//斷開第三個(gè)強(qiáng)引用,并調(diào)用析構(gòu)函數(shù)reference3=nil
以上程序執(zhí)行輸出結(jié)果為:
Runoob 開始初始化Runoob 被析構(gòu)
類實(shí)例之間的循環(huán)強(qiáng)引用
在上面的例子中,ARC 會(huì)跟蹤你所新創(chuàng)建的 Person 實(shí)例的引用數(shù)量,并且會(huì)在 Person 實(shí)例不再被需要時(shí)銷毀它。
然而,我們可能會(huì)寫出這樣的代碼,一個(gè)類永遠(yuǎn)不會(huì)有0個(gè)強(qiáng)引用。這種情況發(fā)生在兩個(gè)類實(shí)例互相保持對(duì)方的強(qiáng)引用,并讓對(duì)方不被銷毀。這就是所謂的循環(huán)強(qiáng)引用。
實(shí)例
下面展示了一個(gè)不經(jīng)意產(chǎn)生循環(huán)強(qiáng)引用的例子。例子定義了兩個(gè)類:Person和Apartment,用來建模公寓和它其中的居民:
class Person { let name: String init(name: String) { self.name=name } var apartment: Apartment? deinit { print("\(name) 被析構(gòu)") }}class Apartment { let number: Int init(number: Int) { self.number=number } var tenant: Person? deinit { print("Apartment #\(number) 被析構(gòu)") }}// 兩個(gè)變量都被初始化為nilvar runoob: Person?var number73: Apartment?// 賦值runoob=Person(name: "Runoob")number73=Apartment(number: 73)// 意感嘆號(hào)是用來展開和訪問可選變量 runoob 和 number73 中的實(shí)例// 循環(huán)強(qiáng)引用被創(chuàng)建runoob!.apartment=number73 number73!.tenant=runoob// 斷開 runoob 和 number73 變量所持有的強(qiáng)引用時(shí),引用計(jì)數(shù)并不會(huì)降為 0,實(shí)例也不會(huì)被 ARC 銷毀// 注意,當(dāng)你把這兩個(gè)變量設(shè)為nil時(shí),沒有任何一個(gè)析構(gòu)函數(shù)被調(diào)用。// 強(qiáng)引用循環(huán)阻止了Person和Apartment類實(shí)例的銷毀,并在你的應(yīng)用程序中造成了內(nèi)存泄漏runoob=nilnumber73=nil
解決實(shí)例之間的循環(huán)強(qiáng)引用
Swift 提供了兩種辦法用來解決你在使用類的屬性時(shí)所遇到的循環(huán)強(qiáng)引用問題:
弱引用
無主引用
弱引用和無主引用允許循環(huán)引用中的一個(gè)實(shí)例引用另外一個(gè)實(shí)例而不保持強(qiáng)引用。這樣實(shí)例能夠互相引用而不產(chǎn)生循環(huán)強(qiáng)引用。
對(duì)于生命周期中會(huì)變?yōu)閚il的實(shí)例使用弱引用。相反的,對(duì)于初始化賦值后再也不會(huì)被賦值為nil的實(shí)例,使用無主引用。
弱引用實(shí)例
class Module { let name: String init(name: String) { self.name=name } var sub: SubModule? deinit { print("\(name) 主模塊") }}class SubModule { let number: Int init(number: Int) { self.number=number } weak var topic: Module? deinit { print("子模塊 topic 數(shù)為 \(number)") }}var toc: Module?var list: SubModule?toc=Module(name: "ARC")list=SubModule(number: 4)toc!.sub=list list!.topic=toc toc=nillist=nil
以上程序執(zhí)行輸出結(jié)果為:
ARC 主模塊子模塊 topic 數(shù)為 4
無主引用實(shí)例
class Student { let name: String var section: Marks? init(name: String) { self.name=name } deinit { print("\(name)") }}class Marks { let marks: Int unowned let stname: Student init(marks: Int, stname: Student) { self.marks=marks self.stname=stname } deinit { print("學(xué)生的分?jǐn)?shù)為 \(marks)") }}var module: Student?module=Student(name: "ARC")module!.section=Marks(marks: 98, stname: module!)module=nil
以上程序執(zhí)行輸出結(jié)果為:
ARC學(xué)生的分?jǐn)?shù)為 98
閉包引起的循環(huán)強(qiáng)引用
循環(huán)強(qiáng)引用還會(huì)發(fā)生在當(dāng)你將一個(gè)閉包賦值給類實(shí)例的某個(gè)屬性,并且這個(gè)閉包體中又使用了實(shí)例。這個(gè)閉包體中可能訪問了實(shí)例的某個(gè)屬性,例如self.someProperty,或者閉包中調(diào)用了實(shí)例的某個(gè)方法,例如self.someMethod。這兩種情況都導(dǎo)致了閉包 "捕獲" self,從而產(chǎn)生了循環(huán)強(qiáng)引用。
實(shí)例
下面的例子為你展示了當(dāng)一個(gè)閉包引用了self后是如何產(chǎn)生一個(gè)循環(huán)強(qiáng)引用的。例子中定義了一個(gè)叫HTMLElement的類,用一種簡單的模型表示 HTML 中的一個(gè)單獨(dú)的元素:
class HTMLElement { let name: String let text: String? lazy var asHTML: () -> String={ if let text=self.text { return "<\(self.name)>\(text)</\(self.name)>" } else { return "<\(self.name) />" } } init(name: String, text: String?=nil) { self.name=name self.text=text } deinit { print("\(name) is being deinitialized") } }// 創(chuàng)建實(shí)例并打印信息var paragraph: HTMLElement?=HTMLElement(name: "p", text: "hello, world")print(paragraph!.asHTML())
HTMLElement 類產(chǎn)生了類實(shí)例和 asHTML 默認(rèn)值的閉包之間的循環(huán)強(qiáng)引用。
實(shí)例的 asHTML 屬性持有閉包的強(qiáng)引用。但是,閉包在其閉包體內(nèi)使用了self(引用了self.name和self.text),因此閉包捕獲了self,這意味著閉包又反過來持有了HTMLElement實(shí)例的強(qiáng)引用。這樣兩個(gè)對(duì)象就產(chǎn)生了循環(huán)強(qiáng)引用。
解決閉包引起的循環(huán)強(qiáng)引用:在定義閉包時(shí)同時(shí)定義捕獲列表作為閉包的一部分,通過這種方式可以解決閉包和類實(shí)例之間的循環(huán)強(qiáng)引用。
弱引用和無主引用
當(dāng)閉包和捕獲的實(shí)例總是互相引用時(shí)并且總是同時(shí)銷毀時(shí),將閉包內(nèi)的捕獲定義為無主引用。
相反的,當(dāng)捕獲引用有時(shí)可能會(huì)是nil時(shí),將閉包內(nèi)的捕獲定義為弱引用。
如果捕獲的引用絕對(duì)不會(huì)置為nil,應(yīng)該用無主引用,而不是弱引用。
實(shí)例
前面的HTMLElement例子中,無主引用是正確的解決循環(huán)強(qiáng)引用的方法。這樣編寫HTMLElement類來避免循環(huán)強(qiáng)引用:
class HTMLElement { let name: String let text: String? lazy var asHTML: () -> String={ [unowned self] in if let text=self.text { return "<\(self.name)>\(text)</\(self.name)>" } else { return "<\(self.name) />" } } init(name: String, text: String?=nil) { self.name=name self.text=text } deinit { print("\(name) 被析構(gòu)") } }//創(chuàng)建并打印HTMLElement實(shí)例var paragraph: HTMLElement?=HTMLElement(name: "p", text: "hello, world")print(paragraph!.asHTML())// HTMLElement實(shí)例將會(huì)被銷毀,并能看到它的析構(gòu)函數(shù)打印出的消息paragraph=nil
以上程序執(zhí)行輸出結(jié)果為:
<p>hello, world</p>p 被析構(gòu)
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。