一、函式補充
1.1 arguments類陣列物件
arguments 是一個對應於傳遞給函式的引數的類陣列物件。
在函式中,使用特殊物件 arguments,開發者無需明確指出引數名,就能訪問它們。
在其它程式語言中,比如java:如果一個函式被定義過兩次,每次引數個數都不同。相當於定義了兩個不同的函式,根據引數個數不同,會選擇不同的函式執行。一個函式名定義了兩個函式,稱為“函式過載”(overloaded)。
複習一下函式,定義函式時,引數要羅列在圓括號中,這些引數,叫“形參”:
function fun(a,b,c){ console.log(a,b,c); } function fun(a,b){ console.log(a,b); } fun(1,2); fun(1,2,3); //合法的,引數多了和少了,都不報錯。
呼叫函式時,傳的引數叫“實參”,JS不要求形參和實引數量一樣多。
實際上,這種叫做方法的過載。在Java中,同一個函式名,但是引數個數不一樣,視為是兩個函式。
也就是說,Java中能夠定義兩個同名函式。
同名的兩個function,都是fun函式,java允許這樣做,但是js沒有這種操作。
JavaScript函式中有一個非常強大的引數,就是每一個函式內部,都可以使用arguments這個類陣列物件。
這個arguments物件,就是涵蓋了所有實參(呼叫函式時傳入的所有實際引數)
function fun(a,b){ console.log(a,b); //等價於arguments[0]和arguments[1] //此時函式內部,arguments就有下標,依次等於實參的值 console.log(arguments); console.log(arguments[0]); console.log(arguments[1]); console.log(arguments[2]); arguments[5] = 88888; console.log(arguments[8]); console.log(arguments.length); //輸出實參的個數 console.log(arguments.callee); //callee等價於函式本身 console.log(arguments.callee.length); //callee的length是形參的個數 console.log(arguments.callee.name); //函式的名稱 } fun(10,20,30,40,50,88,99,100,999); // console.log(arguments); 由於是函式內部的物件,所以函式外是沒有定義的,報錯。
arguments的功能,可以模擬函式的過載,使用一個函式根據引數的個數不同,有不同的作用。
比如我們設計一個sum函式:
如果傳進來一個引數,就得到當前值+1
如果傳進來兩個引數,就返回兩個數的和
function sum(a,b){ switch(arguments.length){ case 1: return ++a; break; case 2: return a + b; break; } // if(arguments.length == 1){ // return ++a; // }else if(arguments.length == 2){ // return a + b; // return arguments[0] + arguments[1]; // } } console.log(sum(10)); console.log(sum(10,88));
設計一個無限參的函式:累加和
function sum(){ var count = 0; //累加器 for(var i = 0; i < arguments.length;i++){ count += arguments[i]; } return count; } console.log(sum(10,20,30,88,99,100,888,999));
設計一個函式,check如果只有一個引數,那麼就檢測這個數是否是偶數,如果有兩個引數,檢測這兩個引數奇偶性是否相同:
function check(){ switch(arguments.length){ case 1: return arguments[0] % 2 == 0 ? true : false; break; case 2: return (arguments[0] + arguments[1]) % 2 == 0 ? true : false; break; default: // console.log("輸入錯誤,請檢測,只支援1到2個引數!"); throw new Error("輸入錯誤,請檢測,只支援1到2個引數!"); } } console.log(check(8)); console.log(check(8,8)); console.log(check(8,8,8));
1.2 IIFE
IIFE:Immediately-Invoked Function Expression,即時呼叫函式表示式。
如果一個函式,在定義的時候,就想直接呼叫它,就是一個IIFE。
函式執行方法:在函式名或變數名後面加()運算子。
函式關鍵字形式:定義時直接在後面加()執行。
function fun(){ console.log("哈哈"); }(); |
不能給函式關鍵字方法直接()呼叫。
函式表示式形式:能立即執行,函式在賦值給變數時就已經被矮化成表示式了,可以直接在後面加圓括號執行
var fun = function(){ console.log("哈哈"); }(); |
結論:如果函式能被矮化成表示式,就能在定義時直接呼叫,就能實現IIFE。
函式矮化為表示式的方法:在函式定義之前加運算子。
數學運算子中:+ - ()符號,其他不能用。
邏輯運算子中:!
其他:~
注意也可以不寫函式名。
+function (){ console.log("哈哈"); }(); -function (){ console.log("哈哈"); }(); !function (){ console.log("哈哈"); }(); ~function (){ console.log("哈哈"); }(); (function (){ console.log("哈哈"); })(); (function (){ console.log("哈哈"); }());
IIFE還可以鍵函式傳參,IIFE能夠關住函式的作用域。在使用某個變數時,如果變數定義在函式內,或函式的引數,只會使用立即呼叫的變數值,不會對被人造成影響。
(function fun(a){ console.log(a); })(8); fun(5); //IIFE關住fun的作用域,外面找不到fun函式的定義
IIFE可以不寫函式名,寫匿名函式;IIFE關住函式的作用域,可以用於解決函式閉包帶來的錯誤影響。
最標準常用的IIFE格式:所以IIFE裡面的函式,都是匿名函式。
(function fun(a){ console.log(a); })(8); |
1.3結合陣列觀察閉包
我們說過,陣列中,什麼都可以放,比如String、Number、Boolean、undefined、function等。
通過動態的方法,遍歷陣列,給陣列每一項新增一個函式:
var arr = []; //空陣列接收函式的定義 for(var i = 0;i <= 10;i++){ //函式定義時,作用域全域性的,全域性中有個變數i //函式值只是定義給陣列的每一項,並不會執行 //函式這個閉包,記住了全域性作用域,裡面有個i,記住了自己內部的語句 arr[i] = function(){ console.log(i); } } console.log(arr); arr[0](); //呼叫了數值中的函式,執行了console.log(i) arr[1](); //呼叫了數值中的函式,執行了console.log(i) ... arr[9](); //呼叫了數值中的函式,執行了console.log(i) arr[10]();//呼叫了數值中的函式,執行了console.log(i)
注意:上面的案例並沒有實現想要的結果,我們希望每一項輸出時,i對應的值應該與每次的下標相同,閉包記住僅僅是一個函式的定義,記住內部的語句輸出i。
輸出的都是11,而不是預想的0~10,原因是每個函式定義時,都產生閉包,函式只認識i,而不是把i這個值複製一份記憶住,而是動態的認識這個i。i呼叫時得幾,這個函式的i就是幾。
所以在呼叫函式時,i已經變成11,所以陣列中每個函式都是11。
想要實現輸出i與下標相同的值,解決方法:
var arr = []; //空陣列接收函式的定義 for(var i = 0;i <= 10;i++){ //函式定義時,作用域全域性的,全域性中有個變數i //函式值只是定義給陣列的每一項,並不會執行 //函式這個閉包,記住了全域性作用域,裡面有個i,記住了自己內部的語句 // (arr[i] = function(){ // console.log("下標:"+ i); // })(); (function(a){ arr[a] = function(){ console.log("下標:"+ a); } })(i); } // console.log(arr); arr[0](); //呼叫了數值中的函式,執行了console.log(i) arr[1](); //呼叫了數值中的函式,執行了console.log(i) ... arr[9](); //呼叫了數值中的函式,執行了console.log(i) arr[10](); //呼叫了數值中的函式,執行了console.log(i)
二、DOM
前期課程中,學習的ECMAscript語言核心部分,都是在控制檯,天天console.log()、prompt()等。
之前學的屬於語言,瞭解JS這個語言的特性,從今天開始要學習JS控制頁面上的元素。迴歸到操作HTML和CSS。
2.1 DOM概述
DOM(Document Object Model,文件物件模型)描繪了一個層次化的節點樹,允許開發人員新增、移除和修改頁面的某一部分。使用JavaScript操作HTML,不是在操作字串,而是在操作節點,極大地降低了程式設計難度。
DOM規範在1998年10月制定,稱為“DOM1級規範”。隨著ECMAScript的升級,DOM也發展出了2級規範、3級規範。另外,早於1998年的DOM也有事實上的標準,我們稱為0級規範。
DOM對很多東西做了抽象,提供了豐富的API:取得元素、css樣式、事件、運動、元素尺寸位置、節點操作。每個知識體系都非常龐大,千絲萬縷。我們今天的課程,把一些線頭都掐出來,日後的課程深入研究每個線頭。
<body> <div id="box"></div> <img src="images/ssh.jpg" id="timg" title="邵老師" > </body> <script type="text/javascript"> //獲取HTML標籤元素 var timg = document.getElementById("timg"); var oBox = document.getElementById("box"); //給圖片新增點選事件,讓img標籤修改src和title屬性 timg.onclick = function(){ timg.src = "images/timg.jpg"; timg.title = "考拉吃樹葉"; timg.width = "300"; } //點選盒子變色 oBox.onclick = function(){ oBox.style.backgroundColor = "skyblue"; } </script>
2.2獲取HTML標籤元素
HTML負責頁面的佈局、結構。JS要操作HTML標籤,第一件事就是要得到這個標籤。
說白了就是把HTML標籤拿到JS裡面來操作。
JS中,最基本的得到標籤元素的方法有兩個:
document.getElementById(); //通id獲取一個標籤元素 document.getElementsByTagName(); //通過標籤名獲取一組元素 |
JS通過document物件(表示文件集合),它表示整個頁面,有很多屬性和方法,包含絕大多數的特徵和操作。
學習DOM說白了就是學習document物件
console.log(document.URL); //獲取當前檔案路徑 console.log(document.title); //獲取當前頁面標題 document.title = "愛前端官網"; //設定當前頁面標題 |
DOM操作,往往都是從某個HTML元素開始,然後對這個元素進行一些操作,所以得到元素是非常關鍵,得到元素的操作可以使用document物件的方法。
注意:
1、方法名稱都是駝峰命名法,首個單詞字母都是小寫,後面的單詞首字母都是大寫。
2、方法需要通過引數獲取元素,引數就是標籤的id屬性,必須寫在引號內,不需要寫#
3、獲取元素,一定要在HTML元素位置後面,HTML有載入順序,獲取元素時,元素必須已經找到
4、id不能重複,頁面中如果設定表單元素的name屬性,也不能與id同名,如果重複,只會選擇第一個出現。
注意程式碼書寫位置,現在要用JS得到oBox元素,所以JS程式碼就要寫在HTML標籤後面,這樣瀏覽器先渲染HTML節點,然後再執行到JS,否則會報錯找不到元素。
<script type="text/javascript"> //獲取HTML標籤元素 var oBox = document.getElementById("box"); oBox.onclick = function(){ oBox.style.backgroundColor = "skyblue"; } </script> <body> <div id="box"></div> </body> |
如果頁面上沒有匹配id的HTML元素,將返回null,null就是空物件。
通過id獲得的元素資料型別:
console.log(typeof oBox); //object |
獲取的元素是物件資料型別。
IE7及低版本有一個怪癖,表單元素name屬性也會被當做id,為了避免這個問題,所以頁面上的name不要和id同名。
<input type="text" name="oInput" value="預設文字"> var oInput = document.getElementById("oInput"); oInput.value = "被js改了";
IE6、7都會把name當做id。
2.3操作HTML(內容和屬性)
更多的是操作HTML標籤屬性,JS可以操作HTML任何屬性,比如:src、href、title、style等
操作方法:獲取標籤屬性,更改標籤屬性。
2.3.1操作HTML內容
通過innerHTML屬性獲取和修改標籤內容:
var oBox = document.getElementById("box"); console.log(oBox.innerHTML); //獲取div標籤的內容 console.log(oBox.innerText); //獲取div標籤的文字內容 oBox.innerHTML = "通JS修改標籤內容"; //通過=號給oBox物件賦值
2.3.2操作HTML屬性
得到一個元素後,直接打點呼叫它的屬性名,就能對HTML相應的屬性進行修改。(有什麼就點什麼)
第一種:“點”語法,獲取和更改,都是同元素打點呼叫。
//獲取img標籤元素 var oImg = document.getElementById("timg"); //獲取標籤屬性的值 console.log(oImg.src); console.log(oImg.alt); console.log(oImg.title); console.log(oImg.id); console.log(oImg.width); //設定標籤屬性的值,和變數賦值一樣 oImg.src = "images/2.jpg"; oImg.alt = "趙麗穎是大圓臉"; oImg.title = "趙麗穎還是吃貨";
class屬性,要換成className,因為class是JS的保留字,不能用。
console.log(oImg.className); //獲取class類名 oImg.className = "abc"; //設定class類名 |
不僅是class屬性要用className這種方式避諱一下,還有以下幾個屬性:
class 要寫成className for 要寫成htmlFor rowspan 寫成rowSpan colspan 寫成colSpan |
第二種:通過以下兩個方法獲取和設定,需要用元素打點呼叫方法:
getAttribute() 獲取標籤元素的屬性 setAttribute() 設定標籤元素的屬性 |
//獲取標籤屬性的第二種方法: var oImg = document.getElementById("timg"); console.log(oImg.getAttribute('src')); //獲取圖片src屬性的路徑 oImg.setAttribute('src','images/2.jpg'); //設定圖片src屬性的路徑
getAttribute()、setAttribute()和“點”語法的區別。
第一:所有自定義屬性,都不能通過點語法設定
console.log(oImg.data); //undefined自定義屬性,不是w3c的屬性,所以不能打點呼叫 console.log(oImg.getAttribute('data')); //自定義屬性用getAttribute()方法可以得到 oImg.setAttribute('data','不知道寫什麼');//自定義屬性用setAttribute()方法可以設定 |
第二:所有行內樣式,點語法.style得到一個樣式物件,我們可以通過.style.background繼續得到小樣式,但是getAttribute()得到的是字串。
console.log(oBox.style.background); console.log(oBox.getAttribute("style")); console.log(typeof oBox.style); //object console.log(typeof oBox.getAttribute("style")); //String
第三:getAttribute()、setAttribute()設定和獲取class等屬性不需要避諱,直接寫屬性名:
console.log(typeof oBox.getAttribute("class")); typeof oBox.setAttribute("class","box"); //String |
點語法效率高於getAttribute()、setAttribute()
所以,如果要獲取自定義屬性或操作物件,會用到,除此之外都用點語法。
2.4操作CSS樣式
通過給元素物件使用“點”語法呼叫style屬性,得到的是一個所有行內樣式組成的樣式物件。可以繼續去打點呼叫css樣式的屬性。
通過點語法呼叫style屬性,呼叫的和設定的都是行內樣式。
var oBox = document.getElementById("box"); console.log(oBox.style); //獲取當前div的css樣式集合 oBox.style.color = "red"; //設定文字顏色 oBox.style.backgroundColor = "skyblue"; //獲取當前div的css樣式集合 oBox.style.width = "200px"; oBox.style.height = "200px"; oBox.style.lineHeight = "200px"; oBox.style.textAlign = "center"; oBox.style.marginLeft = "100px";
設定時,新增的新樣式都是新增在行內:
獲取CSS屬性:如果單一屬性直接寫點語法呼叫對應的屬性名,如果是複合屬性,必須用駝峰命名法:
1 font- line- background- padding- margin- border- 等等都要轉為駝峰命名法 |
正確寫法:
1 oBox.style.backgroundColor = "skyblue"; |
錯誤寫法:
1 oBox.style.background-color = "skyblue"; |
三、事件監聽
JavaScript製作互動效果,離不開事件,所謂的事件就是使用者的某個行為,能夠觸發一個函式的執行。
今天只學DOM標準中的0級繫結事件方法。
繫結事件監聽方法:如果給一個元素新增某個事件,事件就有自己要執行的事件函式。
事件的定義:當什麼時候做什麼事情 事件的作用:可以捕獲使用者的行為 |
事件分為三要素:
事件源:就是這個事件的源頭 事件型別:指的是事件什麼時候發生(點選、觸控等) 執行指令:匿名函式function(){} |
中文語法解釋:
事件源.事件型別 = function(){ } |
語法:
var oBox = document.getElementById('box'); oBox.onclick = function(){ alert("點選事件執行"); //這裡的程式碼要被點選才執行,否則永遠不會被觸發 }
DOM0級規範的事件型別:
onclick 滑鼠單擊事件 ondblclick 滑鼠雙擊事件 onmouseover 滑鼠移入事件 onmouseout 滑鼠移出事件 onmouseenter 滑鼠移入事件 onmouseleave 滑鼠移出事件 onmousedown 滑鼠按下不鬆手事件 onmouseup 滑鼠抬起事件 onmousemove 滑鼠移動事件 onfocus 獲取焦點事件 onblur 失去焦點事件 onkeydown 鍵盤按下事件 onkeyup 鍵盤松開事件 onchange 當元素的值發生變化時觸發 onresize 視窗或框架被重新調整大小。 onselect 標籤的文字被選中 onload 載入事件(某個物件載入完後,觸發的事件) |
所有事件都要被觸發才執行,否則匿名函式中的代表永遠都不會生效。
onload載入事件:給元素新增一個載入事件時,程式碼只要執行到這個位置,立即新增事件監聽,直到瀏覽器的元素載入完後,才觸發onload事件,不需要使用者的行為參與,它是瀏覽器自己的載入事件。
window.onload = function(){ //這裡的程式碼是當瀏覽器載入完HTML和CSS才回頭執行程式碼 var oBox = document.getElementById("box"); oBox.onclick = function(){ oBox.style.backgroundColor = "skyblue"; } }
window物件的載入事件,觸發條件:所有的HTML和CSS載入完後,才觸發
特殊用途:幫我們實現事件函式內部的語句需要在HTML結構載入完後,才執行JS程式碼。
window 表示瀏覽器視窗 onload 表示載入後的事件 |
總結:其實就是整個頁面都載入完後才執行JS程式碼,就是執行了一個頁面載入完成後的事件。