JS循環(huán)
for循環(huán)
- 格式:
for( var i=0; i<5 ; i++){
循環(huán)代碼;
} - i=0,是初始化條件,即循環(huán)從幾開始,在整個循環(huán)過程中,它只會在最開始的時候執(zhí)行一次;
- i<5,是退出條件,規(guī)定了循環(huán)多少次結(jié)束;
- i++增量參數(shù)
- 整個流程就是,先執(zhí)行初始化條件,然后判斷條件,滿足循環(huán)一次,然后執(zhí)行增量,再判斷條件,再循環(huán)一次,之后就是反復的增量-判斷-循環(huán),直到條件不滿足退出循環(huán)。
多重for循環(huán)
- 當多個for循環(huán)嵌套時,可以把里層的for循環(huán)看作外層for循環(huán)的代碼
- 外層循環(huán)一次,就執(zhí)行內(nèi)層的代碼,執(zhí)行內(nèi)層for時再開始循環(huán),當內(nèi)層for循環(huán)次數(shù)完畢后,再回到外層,外層繼續(xù)循環(huán)在執(zhí)行內(nèi)層代碼
- 所以通俗來說就是每外層執(zhí)行一次循環(huán),內(nèi)層for就要循環(huán)完所有次數(shù),再返回外層循環(huán)。比如:外層循環(huán)5次,內(nèi)層循環(huán)10次,那么內(nèi)層一共需要循環(huán)50次。
- 如果不能理解,可用斷點調(diào)試來看執(zhí)行順序。
雙重for循環(huán)小案例解析
- 倒直角三角形,代碼:
for(var i=1; i<10; i++) {
for(var j=i; j<10; j++) {
document.write("*");
}
document.write("<br />");
} - 這個案例的重點在于,內(nèi)層循環(huán)出來的*號要越來越少,才能實現(xiàn)倒三角行。而滿足這個條件很簡單,讓內(nèi)層循環(huán)j=i就能實現(xiàn),因為外層i循環(huán)自增會越來越大,如果內(nèi)層j=i,那么在規(guī)定了j<10的情況下,就會讓內(nèi)層循環(huán)次數(shù)越來越少。這樣就能出現(xiàn)倒直角三角形的效果。
- 九九乘法表,代碼:
<style>
table, td {
border: 1px solid #aaa;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
</style>
<script>
document.write("<table>");
for(var i=1; i<=9; i++) {
document.write("<tr>");
for(var j=1; j<=i; j++) {
document.write("<td>" + j + "×" + i + "=" + j*i + "</td>");
}
document.write("</tr>");
}
document.write("</table>");
</script> - 要實現(xiàn)九九乘法表要滿足兩個條件,一:表是9行;二:第幾行對應的就有幾列。
- 那么開始分析思路:既然要9行,就讓外層的i循環(huán)來控制行數(shù),i<=9就可以循環(huán)出9行內(nèi)。
- 重點在于怎么讓每行顯示對應的列數(shù),這里可以用內(nèi)層循環(huán)j<=i來實現(xiàn)。每次循環(huán)次數(shù)由i的值來控制,從而達到目的。
- 又因為九九乘法表每行都是從1開始乘并遞增,所以讓
j=1再輸出jxi的字符串即可。
斐波那契數(shù)列循環(huán)案例
- 代碼如下:
var num1 = 1;
var num2 = 1;
var temp = 0;
for(var i = 1; i<= x; i++){
temp = num2;
num2 = num2 + num1;
num1 = temp;
} - 斐波那契數(shù)列循環(huán)有一個很重要的規(guī)律的就是:第三個值等于前兩個值的和。根據(jù)兔子計算的題來看,一般是求到x月時有多少只兔子。
- 這個可以理解為是一種遞增的模式,我們需要使用循環(huán)來讓num1和num2通過上面的規(guī)律來增加,相當于往后走了一級。
- 這樣就很好實現(xiàn)了,創(chuàng)建一個臨時變量來中轉(zhuǎn),把num2的值給臨時變量,讓num2 = num1 + num2,再把臨時變量中原num2的值賦給num1,這樣就相當于num1的值變成了num2的值,num2的值變成了后一個數(shù)字的值。不就是相當于往后走了一層嗎。
- 然后就可以根據(jù)需求來循環(huán),求第幾個月后,就循環(huán)幾次,而num2的值因為是和所以就是最終結(jié)果。
- 總結(jié):
- 并不是要記住這兩個案例怎么寫,兩個案例都是循環(huán)值交叉作用的體現(xiàn)。要透徹的理解案例的原理,并舉一反三學會在實際工作中具體的應用。
for in遍歷
- 使用for in不僅可以遍歷數(shù)組,還可以遍歷非數(shù)字索引的對象。key是一個存儲鍵的變量,in后面跟上要遍歷的數(shù)據(jù)名稱。
for (var key in obj) {
console.log(obj[key]);
}
while循環(huán)
- 格式:
var i = 0;
while(條件表達式) {
內(nèi)容代碼
i++;
} - 可以看到while和for雖然寫法上略有不同,但是循環(huán)規(guī)律都是一樣的,都是通過初始值、循環(huán)條件、自增量來組成?;旧蟜or能做的while也能做,但是如果做數(shù)字方面的循環(huán),for會看得更清晰一些。
- 利用循環(huán)求1加到100值時,除了初始值外還需要聲明一個存放結(jié)果的變量來配合。
do while循環(huán)
- 格式:
var i = 0;
do {
內(nèi)容代碼;
i++;
} while(條件表達式) - do while是while循環(huán)的一種拓展方法,從書寫順序就能知道,do while是先執(zhí)行再判斷。
- 那么就可以得出do while和while的區(qū)別了:while是先判斷,如果不滿足則不執(zhí)行;do while是先執(zhí)行一次,再進行判斷,不滿足就不執(zhí)行。所以如果使用do while循環(huán),就算條件不滿足也會執(zhí)行一次。
continue和break的區(qū)別
- continue跳過本次循環(huán),略過下面的代碼,直接進入下一次循環(huán),可用“計算1-100偶數(shù)和案例”來深入理解;
- break跳出所有循環(huán),直接結(jié)束??捎谩把h(huán)出1-100之間第一個能被3整除的數(shù)案例”來深入理解。如果要使用break跳出多層循環(huán),需要使用標記來結(jié)束。例如:
tag{ for(){ for(){ break tag}}},在循環(huán)外部做一個標記,然后在內(nèi)層使用break時跟上標記,就能結(jié)束多層循環(huán)
JS數(shù)組
- 數(shù)組的三種聲明賦值方式
- 第一種使用構(gòu)造函數(shù)創(chuàng)建:
var arr = new Array("值1","值2".....);
注意這里如果構(gòu)造函數(shù)參數(shù)寫多個值就是正常的數(shù)組賦值,如果只寫一個是設置數(shù)值的長度。 - 第二種通過字面量直接創(chuàng)建:
var arr = ["值1","值2"....]; - 第三種:(第三種稍微特殊一點,先聲明空的數(shù)組,然后再根據(jù)數(shù)字索引來寫入值,數(shù)組的數(shù)字索引默認是從0開始)
var arr = [];
arr[0] = "值1";
arr[1] = "值2"; - 因為JS弱類型的特性,可以數(shù)組內(nèi)可以放任意類型的值,但是實際工作中基本不會這樣做。
數(shù)組的訪問
- 普通訪問:
數(shù)組名[索引值]; - 獲取數(shù)組的長度:
數(shù)組名.length;- 數(shù)組length屬性
- 數(shù)組length屬性是非常強大的,因為它的會隨著數(shù)組個數(shù)變化而動態(tài)改變,所以能應用到的地方很多。比如:
- 通過length來個數(shù)組追加值。數(shù)組[數(shù)組.length] = 值,通過規(guī)律可以發(fā)現(xiàn),空數(shù)組是length為0,那么第一次賦值就是給0索引號賦值。第二次length變成1了,那么再次賦值就給1索引號賦值。以此類推,你會發(fā)現(xiàn)不不用寫索引號,直接放入length屬性就可以保證從0開始連續(xù)追加值。
- 另外給length賦值可以直接影響數(shù)組內(nèi)容的個數(shù)。
- 數(shù)組length屬性
- for循環(huán)遍歷數(shù)組:
for( var i = 0; i < 數(shù)組名.length; i++) {
console.log(arr[i]);
} - for循環(huán)直接讓循環(huán)值小于數(shù)組長度即可(因為索引值是從0開始,所以必須小于才不會多循環(huán)一次)
- for反向遍歷數(shù)組
for( var i = 數(shù)組名.length-1 ; i >= 0 ; i--) {
console.log(arr[i]);
} - 反向遍歷,就讓循環(huán)初始值等于該數(shù)組的最大索引號,然后用遞減的方式倒序來輸出即可。
- 使用for循環(huán)來給數(shù)組賦值時,可以配合length屬性來用。一個空數(shù)組,它的length就是0,那么就可以把length當作是它的初始索引號,然后循環(huán)過程中,每附一個值length會自動增加1。但是這個方法僅限初始數(shù)組是空數(shù)組。
如何找出數(shù)組中最大最小值及其索引
- 首先要分析需求,目的是要找出四個值。那么肯定需要四個變量來裝這四個值。
- 接下來用假設值的方法來遍歷數(shù)組并獲取結(jié)果
- 代碼如下:
var arr = [99,22,38,45,23,84,48];
//假設最大值最小值和索引,然后通過循環(huán)判斷來改變
var max = arr[0];
var maxIndex = 0;
var min = arr[0];
var minIndex = 0;
//這時就可以通過for循環(huán)來把數(shù)組中所有值找出來,并依次判斷
for(var i=0 ; i< arr.length; i++) {
//因為假設的max是最大的值,如果它小于當前循環(huán)出來的值,那么證明當前值更大,就把當前值賦給max,同時索引號也要變
if(max < arr[i]) {
max = arr[i];
maxIndex = i;
}
//因為假設的min是最小的值,如果它大于當前循環(huán)出來的值,那么證明當前值更小,就把當前值賦給min,同時索引號也要變
if(min > arr[i]) {
min = arr[i];
minIndex = i;
}
} - 記住,找出最大或者最小值,必須要先設置一個假設值,再遍歷比較。
翻轉(zhuǎn)數(shù)組
- 翻轉(zhuǎn)數(shù)組實際上是利用for循環(huán),把一個數(shù)組的所有值以反向的順序賦給另一個數(shù)組
- 那么根據(jù)這個邏輯,就可以分析出一個規(guī)律:翻轉(zhuǎn)數(shù)組[索引號] = 原數(shù)組[數(shù)組長度-1-當前循環(huán)索引號],這樣就實現(xiàn)了數(shù)組的翻轉(zhuǎn)務必親手寫幾次,理解原理)。
- 代碼如下:
var arr = ["值1", "值2", "值3", "值4", "值5"];
var arr1 = []; 要聲明一個空數(shù)組來裝翻轉(zhuǎn)的數(shù)據(jù)
for(var i=0; i<arr.length; i++) {
arr1[i] = arr[arr.length-1-i]; 這樣arr1[0]的值就是arr最后一個索引的值,然后循環(huán)i的值變化,arr1的索引值越來越大,arr的索引值越來越小,這樣就實現(xiàn)了一個反順序的數(shù)組值賦值。
} - 也可以通過倒序遍歷來把值正序賦給一個新的變量,也能實現(xiàn)翻轉(zhuǎn)。
var arr= [];
var newArr = [];
for (var i=arr.length-1; i>=0; i--) {
newArr[newArr.length] = arr[i];
}
數(shù)組冒泡排序案例
- 需求是把數(shù)組中所有的值按從小到大或者從大到小的順序進行排列
- 實現(xiàn)代碼(以從小到大為例,比較符反過來就是從大到小了)如下:
var arr = [19,20,85,24,28,94,30,1,22];
//外層循環(huán)控制一共循環(huán)多少次才能把所有值都排序好,根據(jù)排序規(guī)律可以得出,需要數(shù)組值總數(shù)-1次循環(huán)才能完成排序,又因為是從0開始,所以得小于數(shù)組值總數(shù)-1次
for (var i=0; i<arr.length-1; i++){
//聲明一個外層循環(huán)的值
var flag = true;
//內(nèi)層循環(huán)控制數(shù)組每個值要比較多少次才能到位置,正常情況也是需要數(shù)組值總數(shù)-1次循環(huán),但是又因為每次循環(huán)會固定好一個值的位置,因此循環(huán)次數(shù)是有規(guī)律的遞減。
for( var j=0; j<arr.length-1-i; j++) {
//開始前后兩個值兩兩比較,如果前面的大就放到后面去
if(arr[j] > arr[j+1]){
flag = false; //只要重新賦值就說明順序不對,在交換,如果沒有重新賦值就是沒有進來,說明所有順序是對的
var temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
//如果經(jīng)過循環(huán)flag的值沒有改變,則表示已經(jīng)是正確順序,直接跳出不再執(zhí)行
if(flag) {
break;
}
} - 大致原理代碼已經(jīng)說清楚了,多敲幾遍理解就好。只要明白冒泡排序需要雙層for來控制比較次數(shù)和所有值排序次數(shù),以及循環(huán)次數(shù)的規(guī)律、前后值交換需要一個臨時變量即可。
JS函數(shù)
- 函數(shù)是可以重復執(zhí)行的代碼塊
- 格式:
function fun(形參1,形參2...) {
函數(shù)內(nèi)部代碼
返回值
}
fun(實參1,實參2) 在調(diào)用函數(shù)時,傳入?yún)?shù)實現(xiàn)不同結(jié)果 - 函數(shù)是可以多次重復調(diào)用的代碼塊,有助于節(jié)省代碼量易于維護。有點類似于css的類選擇器。
- 參數(shù)可以寫也可以不寫。實參可以是任意形式的值。如果實參比形參多,多出來的實數(shù)會浪費掉;如果實參比形參少,多出來的形參會默認為undefined。另外傳入?yún)?shù)時,只是把實參的值取出來復制了一份給形參,并不是直接把實參傳入了。
- return就是返回值,在函數(shù)中return有結(jié)束函數(shù)運行的作用,執(zhí)行到它函數(shù)就結(jié)束了,并通過return把執(zhí)行結(jié)果返回到函數(shù)調(diào)用的地方。
- js中沒有重載,所以函數(shù)名不能相同,否則會覆蓋。
- 函數(shù)因為有預解析,執(zhí)行順序會先執(zhí)行調(diào)用代碼,再進入函數(shù)內(nèi)部執(zhí)行,不受書寫順序的影響,在瀏覽器調(diào)試工具中,有對應進入函數(shù)內(nèi)調(diào)試的按鈕。
函數(shù)聲明提升
- 在JS中,會默認把函數(shù)聲明提升到最頂端,就是說最優(yōu)先加載。變量聲明也會聲明提升,但是變量只提升的聲明卻不提升賦值。所以用表達式形式聲明函數(shù),并在函數(shù)前調(diào)用會出錯。
全局變量和局部變量
- JS中在外部聲明的變量就叫做全局變量,它的特點是在任何地方都能正常使用
- 局部變量是指在函數(shù)內(nèi)部聲明的變量,局部變量只能在聲明的函數(shù)內(nèi)使用,在外部調(diào)用會報錯。
函數(shù)的遞歸
- 通俗來說,遞歸就函數(shù)在內(nèi)部自己調(diào)用自己。遞歸一定要有跳出條件。這里只是做一個基礎的了解,后面會深入了解。
- 一個遞歸累加的案例,稍作了解:
- 分析累加的規(guī)律,1的累加是1,2的累加是2+1,3的累加是3+2+1,4的累加是4+3+2+1。可以看到實際上累加是當前值加上前一個數(shù)的累加,那么得出累加公式n = n + (n-1的累加),那么通過遞歸實現(xiàn)就是:
function num(n) {
//必須要作一個判斷當做結(jié)束條件,不然遞歸會無限循環(huán)
if( n == 1) {
return 1;
}
var num = n + num(n-1);
return num;
}
num(n); - 實際是就是通過遞歸函數(shù),來反復調(diào)用自己來計算前一個數(shù)的累加值,一直到1就結(jié)束遞歸,并返回給第一次遞歸調(diào)用時。
回調(diào)函數(shù)
- 因為函數(shù)實際上也是一種數(shù)據(jù),它既可以調(diào)用,也可以當作值來賦給其他類型。那么把函數(shù)當作參數(shù)來傳給其他函數(shù),也是可以實現(xiàn)的。
- 這種把一個函數(shù)當作參數(shù)傳給另一個函數(shù),就叫做回調(diào)函數(shù)。注意傳入回調(diào)函數(shù)時,只傳入函數(shù)名,不需要帶上括號,等于是把函數(shù)內(nèi)部的代碼傳進去,并不是立刻執(zhí)行
函數(shù)求三個數(shù)中的最大值
- 代碼如下:
function getMax(a, b, c) {
renturn (a>b ? a : b) > c ? (a>b? a : b) : c;
} - 比較三個值就用三元先比較前兩個,得到大的,再和第三個用三元比,再返回比較出來大的那個。
JS復雜數(shù)據(jù)類型:object對象
- js的數(shù)據(jù)類型分為基本數(shù)據(jù)類型(數(shù)值型、字符型、布爾型、空型、未定義型),復雜數(shù)據(jù)類型(對象)
- 對象實際上是對存在事物的抽象化表示,所以也有種說法叫萬物皆為對象。
- 對象和基本數(shù)據(jù)類型的最大的區(qū)別就是,對象有屬性和方法。
- 屬性代表這個對象的名詞(年齡、性別、姓名等),方法代表這個對象的動詞(說話、走路、唱歌、跳舞等)
- 注意對象和它的屬性與方法是通過.號或者[]來連接。
- 對象的聲明和賦值的兩種方法:
- 構(gòu)造函數(shù)創(chuàng)建:
var obj = new Object(); 聲明一個叫obj的對象
obj.name = xxx; 聲明obj對象的姓名屬性
obj.sex = "男"; 聲明obj對象的性別屬性
obj.run = function(形參) { 聲明obj對象的走路方法
return "我在走路"
};
obj.talk = function(形參) { 聲明obj對象的說話方法
return "我在說話"
}; - 字面量創(chuàng)建
var obj = {
name: xxx;
sex: "男";
run: function() {
return "走路";
}
} - 因為方法是動作,所以需要用函數(shù)來承載,也可以說函數(shù)在對象中叫做方法。以上聲明的屬性和方法都是屬于obj這個對象的。
- 當然因為對象也是無序的鍵值對組成,所以也可以使用類似數(shù)組的方法來賦值和訪問。這種方式更加靈活,可以再中括號內(nèi)使用字符串拼接或者變量等。
var obj = {};
obj['name'] = xxx;
obj['run'] = function(){}; - 對象屬性可以存放任意類型的數(shù)據(jù)。
- 對象的訪問:
console.log(obj); 打印出這個對象所有的屬性和方法
console.log(obj.name); 訪問屬性值需要將值打印出來
obj.run(實參); 方法調(diào)用與函數(shù)類似,直接寫對象名.方法名()就可以 - 總結(jié),任何對象的屬性和方法,它們的區(qū)別都是方法有小括號(),屬性沒有。
- 對象的訪問:
鍵值對的概念
- 實際上我們已經(jīng)見過很多鍵值對了,比如數(shù)組、css、字典等,都是鍵值對。
- 鍵值對,就是一個鍵對應一個值,當我們想使用或查找某個值時,直接調(diào)用對應的鍵就可以了,這樣可用大大調(diào)高效率。而對象也是鍵值對,屬性名或者方法名就是鍵,存儲的內(nèi)容就是值。調(diào)用名字就能獲取值或者執(zhí)行代碼。
this偽變量
- 三種情況:
- 1.當this在全局上下文時(即函數(shù)外部),它對應的是window
- 2.當this在函數(shù)內(nèi)部時,它對應的是當前函數(shù)所屬的對象
- 3.在構(gòu)造函數(shù)中時,會改變this的指向,直接指向?qū)ο蟛辉俑鶕?jù)所屬,并返回這個對象
批量創(chuàng)建對象
- 工廠函數(shù)模式:方法很簡單,就是把創(chuàng)建一個對象的流程代碼放進去函數(shù)封裝起來,并給函數(shù)寫上所有對象屬性對應的形參(方法一般是在內(nèi)部寫好,很少傳進去),在函數(shù)最后返回創(chuàng)建好的對象。當外部調(diào)用這個函數(shù)時就創(chuàng)建了一個對象,而且根據(jù)實參的不同,每次創(chuàng)建的對象值也不同。就像是其他編程語言中的類。
- 構(gòu)造函數(shù)模式:為了解決工廠函數(shù)模式的一些缺點(不能查看對象類別),構(gòu)造函數(shù)就誕生了。大致的寫法相似,但有一些變化。構(gòu)造函數(shù)模式中,不需要手動創(chuàng)建對象,所有的屬性和方法賦值時都用this.名稱的方式來寫,并且因為this的特性不用再寫返回值。創(chuàng)建對象時,使用new關(guān)鍵字。
- 一個手機構(gòu)造函數(shù)案例:
//一般構(gòu)造函數(shù)使用名詞命名,且首字母大寫
function Phone(brand, color, price) {
this.brand = brand;
this.color = color;
this.price = price;
this.call = function() {
console.log("打電話");
}
this.message = function() {
console.log("發(fā)短信");
}
}
var huaweiPhone = new Phone("華為","黑色",5000);
console.log(huaweiPhone.color); //因為huaweiPhone是通過Phone創(chuàng)建的實例對象,所以創(chuàng)建后直接用實例名調(diào)用屬性和方法就可以了
huaweiPhone.call(); - 可以通過(A instanceof B)來比較A是不是通過B創(chuàng)建出來的實例對象。是返回true,否則返回false
數(shù)據(jù)存放的位置
- 在JS中基本數(shù)據(jù)類型的值存放在棧內(nèi)層中,復雜數(shù)據(jù)類型的值存放在堆內(nèi)存中。
- 因為基本數(shù)據(jù)類型存儲數(shù)據(jù)比較少,所以直接把值放在棧內(nèi)層中訪問速度很快。把一個基本數(shù)據(jù)賦值給另一個基本數(shù)據(jù),它們雖然值相同,但實際因為直接把值放過去所以是兩個引用地址。它們完全獨立,所以任意一基本數(shù)據(jù)再重新賦值后另一個不受影響 。
- 復雜數(shù)據(jù)類型因為占用空間較大,所以是把值放到堆內(nèi)存中,而棧內(nèi)層開辟的空間只是存放了一個指向堆內(nèi)存引用地址。這是如果把一個復雜數(shù)據(jù)賦值給另一個復雜數(shù)據(jù),只是在棧內(nèi)存中把引用地址賦給了它,而實際上還是共用的存放在堆內(nèi)存中的值,所以當修改了堆內(nèi)存中數(shù)據(jù)時,因為引用地址相同所以值會一起變。
- 把基本數(shù)據(jù)類型和復制數(shù)據(jù)類型作為實參,傳入函數(shù)中,再做修改,結(jié)果和上面說一樣。結(jié)論就是:在棧內(nèi)存中存放的值,那么賦值過去,怎么修改都不會影響。但是如果存放的是引用地址,那么賦值過去,修改會一起變(有一種特殊情況,就是傳入之后,有又創(chuàng)建了一個對象賦值給形參,那么形參的引用地址就變了)。
- 可以找具體的圖來看,更容易理解。
內(nèi)置數(shù)學對象
- 因為內(nèi)置的數(shù)學對象太多,這里不做記錄,去課件里看。
JS基礎測試題總結(jié)
- 未定義的變量,使用.toString()轉(zhuǎn)換字符串函數(shù),會直接報錯。
- continue只能在循環(huán)中使用。
- 'var a= + 任意類型',JS加法運算中,加號左邊不寫或者null或者undefined+任意類型,都會轉(zhuǎn)換成數(shù)值型。無效數(shù)字返回NaN
- JS是面向?qū)ο蟮恼Z言,不是面向過程
- ++在前先自增再執(zhí)行,++在后先執(zhí)行后自增