目錄
- 常用事件和事件處理函數(shù)
- 遞歸函數(shù)
- 函數(shù)的屬性和方法
- 函數(shù)的作用域
常用事件和事件處理函數(shù)
事件參考--MDN
在DOM
當中,存在有很多的事件茴扁,通過這些事件,我們能夠很好地監(jiān)聽用戶在瀏覽器網(wǎng)頁當中的一些動作疙教,并且根據(jù)這些動作來設置相關的事件處理函數(shù)蔑匣。
我們可以將事件分成三個部分筷厘,事件源氮发,事件榜旦,以及事件發(fā)生后執(zhí)行的動作籍胯。
對應著元素竟闪、事件以及事件處理函數(shù)。
例如:
按鈕.單擊= function () {事件發(fā)生后執(zhí)行的動作}
例如我們在網(wǎng)頁中有一個按鈕杖狼,當用戶點擊按鈕的時候我們讓網(wǎng)頁彈出彈窗炼蛤,彈出信息為hello,world。
具體代碼可以如下:
// 找到按鈕
var oBtn = document.getElementById('btn');
// 綁定事件
oBtn.onclick = function () { // 事件處理函數(shù)
// 當用戶發(fā)生點擊操作之后彈出彈窗
alert('hello,world');
};
在上面的代碼中蝶涩,我們通過給一個元素綁定單擊事件理朋,并且添加了事件處理函數(shù)絮识。
一旦用戶點擊了按鈕,那么就立刻會彈出彈窗嗽上。
當然次舌,我們除了上面的寫法以外,對于給一個元素綁定事件和事件處理函數(shù)的寫法還可以換做另外一種形式:
<button id="btn" onclick="sayHello()">點擊</button>
<script >
function sayHello(){
alert('hello,world');
}
</script>
在上面的代碼中兽愤,我們把事件和事件處理函數(shù)都綁定到了元素的身上彼念,這樣做的好處是能夠省略了獲取元素的過程。
在我們執(zhí)行事件處理函數(shù)的時候浅萧,在某些時刻可能需要進行參數(shù)的傳遞逐沙,可以采用下面的幾種方式。下面同樣是點擊問好的案例洼畅,但是問好的對象是對指定的人進行問好吩案。
第一種方式:
demo:
<button onclick="sayHello('張三')">點擊問好</button>
<script >
function sayHello(name){
alert('hello,' + name );
}
</script>
第二種方式:
demo:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<input type="text" id="name">
<button id="btn">點擊問好</button>
</body>
<script>
// 獲取input
var oInput = document.getElementById('name');
// 獲取按鈕
var oBtn = document.getElementById('btn');
// 點擊按鈕然后執(zhí)行問好
oBtn.onclick = function () {
var userName = oInput.value;
sayHello(userName);
};
function sayHello(name) {
alert('hello,' + name);
}
</script>
</html>
上面的代碼中,我們可以在input中輸入用戶名土思,當用戶點擊按鈕的時候务热,我們會將通過單擊事件的事件
處理函數(shù)來調用sayHello方法,在這個方法中己儒,獲取用戶輸入的用戶名崎岂,然后問好。
上面的代碼基本上已經完成了我們的需求闪湾,但是我們仍然需要讓我們的代碼變得更加的完善冲甘。
例如,當我們點擊按鈕的時候途样,發(fā)現(xiàn)用戶并沒有輸入用戶名江醇,我們該怎么辦?
demo:
// 點擊按鈕然后執(zhí)行問好
oBtn.onclick = function () {
var userName = oInput.value;
// 判斷一下用戶輸入的內容是否為空
if (userName === "") {
alert("請輸入用戶名");
}else {
sayHello(userName);
}
};
在上面的代碼中何暇,當用戶點擊按鈕調用了事件處理函數(shù)之后陶夜,我們進行了一個判斷,判斷用戶輸入的內容是否為空裆站,如果為空条辟,我們就可以繼續(xù)提示用戶,請輸入用戶名宏胯。
當然羽嫡。上面的代碼其實演示的僅僅是一種思路,代碼仍然不是太嚴謹肩袍。
在我們的實際項目開發(fā)中杭棵,必須要讓我們的代碼盡可能的變得更加嚴謹,使我們的代碼變得更加的強壯氛赐。
鼠標事件:
魂爪。click
當用戶按下并釋放鼠標按鍵或其他方式“激活”元素時觸發(fā)
先舷。contextmenu
可以取消的事件,當上下文菜單即將出現(xiàn)時觸發(fā)甫窟。當前瀏覽器在鼠標右擊時顯示上下文菜單
密浑。dblclick
當用戶雙擊鼠標時觸發(fā)
蛙婴。mousedown
當用戶按下鼠標按鍵時觸發(fā)
粗井。mouseup
當用戶釋放鼠標按鍵時觸發(fā)
。mousemove
當用戶移動鼠標時觸發(fā)
街图。mouseover
當鼠標進入元素時觸發(fā)浇衬。relatedTarget(在IE中是
fromElement)指的是鼠標來自的元素
。mouseout
當鼠標離開元素時觸發(fā)餐济。relatedTarget(在IE中是toElement)指的是鼠標要去往的元素
耘擂。mouseenter
類似mouseover
,但不冒泡絮姆。IE將其引入醉冤,HTML5將其標準化,但尚未廣泛實現(xiàn)
篙悯。mouseleave
類似mouseout
蚁阳,但不冒泡。IE將其引入鸽照,HTML5將其標準化螺捐,但尚未廣泛實現(xiàn)鍵盤事件
。keydown
按下
矮燎。keypress
點擊
定血。keyup
抬起加載事件
。load
頁面完全加載后會觸發(fā)該事件
诞外。error
當加載失敗后觸發(fā)澜沟,所有可以觸發(fā)load事件的元素,都可以觸發(fā)該事件
峡谊。abort
元素加載中止時茫虽,(如加載過程中按下ESC鍵,停止加載)靖苇,觸發(fā)該事件席噩,常用于圖片加載 (只有IE支持)
。unload
與load
事件對應的是unload事件贤壁,該事件在文檔被完全卸載后觸發(fā)悼枢。刷新頁面時,也會觸發(fā)該事件脾拆。chrome/firefox/safari瀏覽器會阻止alert等對話框馒索,IE瀏覽器會阻止console.log()等控制臺顯示莹妒。
。DOMContentLoaded
則在形成完整的DOM樹之后就會觸發(fā)绰上,而不理會圖像旨怠、javascript文件、CSS文件或其他資源是否下載完畢蜈块。(IE8不支持)表單事件
鉴腻。submit
表單提交時觸發(fā),綁定給form元素
百揭。reset
表單發(fā)生重置時觸發(fā)爽哎,綁定為form元素
。blur
失去焦點時觸發(fā)
器一。change
表單控制的內容發(fā)生改變時觸發(fā)其他事件
课锌。scroll
元素內部的內容滾動時觸發(fā)
。resize
窗口尺寸發(fā)生變化時觸發(fā)
并不是所有元素都具有所有事件
1.
box.onclick = function(){
console.log(1)
};
2.
box.onclick = function fn2(){
console.log(2)
};
3.在事件后,直接放函數(shù)名,別加()
box.onclick = fn3;
function fn3(){
console.log(3)
}
4.在這個函數(shù)內部,執(zhí)行其他函數(shù)
box.onclick = function(){
fn4()
};
function fn4(){
console.log(4)
}
根據(jù)執(zhí)行方式不同的分類:
1.被事件直接執(zhí)行的函數(shù)叫事件處理函數(shù)
;
2.通過參數(shù)執(zhí)行函數(shù),這個作為參數(shù)的函數(shù),叫回調函數(shù)
祈秕。
遞歸函數(shù)
當函數(shù)發(fā)生在函數(shù)體內自己調用自己這樣的情況時渺贤,這個函數(shù)我們可以稱為遞歸函數(shù)
。
例如下面的 demo
请毛,就是一個遞歸函數(shù)
:
// 遞歸函數(shù)
function fib(num) {
if (num === 0) return 0;
if (num === 1) return 1;
return fib(num - 2) + fib(num - 1);
}
console.log(fib(6)); // 8
在上面的demo
中志鞍,通過遞歸函數(shù)計算了一下斐波那契數(shù)列
。
上面代碼中获印,fib函數(shù)內部又調用了fib述雾,計算得到斐波那契數(shù)列的第6個元素是8。
第一等公民的函數(shù)
在js
中兼丰,函數(shù)被稱為一等公民
玻孟。
什么意思呢,就是說函數(shù)
被看作是一種值鳍征,與其他的數(shù)據(jù)值(數(shù)值黍翎、字符串、布爾值等)地位相等艳丛。凡是可以使用值的地方都可以使用函數(shù)匣掸。
例如,我們可以將一個值賦值給一個變量氮双,對應的碰酝,同樣也可以將函數(shù)賦值給一個變量。
var fn = function () {};
同時戴差,因為函數(shù)是一等公民
送爸,所以也可以將函數(shù)當做參數(shù)傳遞給其他函數(shù),或者作為函數(shù)的返回結果。
function fn1() {}
function fn2(parm) {
parm();
}
fn2(fn1);
函數(shù)的屬性和方法
name 屬性
函數(shù)的name屬性返回函數(shù)的名字袭厂。
function fn1() {}
fn1.name; // "fn1"
如果是通過變量賦值定義的函數(shù)墨吓,那么name屬性返回函數(shù)的名字。
var fn2 = function () {};
fn2.name; // fn2
上面的案例中纹磺,函數(shù)是一個匿名函數(shù)帖烘,我們使用name屬性就可以獲得存儲函數(shù)的變量的名稱,但是一旦我們給匿名函數(shù)也定義了一個函數(shù)名橄杨,那么我們通過name屬性查看到的將會是函數(shù)的名字而不是變量名秘症。
var fn2 = function test () {};
fn2.name; // test
需要注意的是,雖然打印出來的名稱是test,但是真正的函數(shù)名還是fn2,test只是表達式的名字讥珍,并且test這個名字只能夠在表達式內部使用历极。
我們可以通過name屬性獲取參數(shù)函數(shù)的的名字。
function fn1() {}
function fn2(parm){
console.log(parm.name);
}
fn2(fn1); // fn1
在上面的代碼中衷佃,在函數(shù)的內部通過name屬性就獲取了要傳入的函數(shù)的名字。
length屬性
函數(shù)的length屬性返回函數(shù)預期傳入的參數(shù)個數(shù)蹄葱,即函數(shù)定義之中的參數(shù)個數(shù)也就是形參
的個數(shù)氏义。
function fn1(a,b) {
}
console.log(fn1.length);//2
上面代碼定義了空函數(shù)f,它的length屬性就是定義時的參數(shù)個數(shù)图云。不管調用時輸入了多少個參數(shù)惯悠,length屬性始終等于2。
length屬性提供了一種機制竣况,判斷定義時和調用時參數(shù)的差異克婶,以便實現(xiàn)面向對象
編程的“方法重載”(overload)。
toString()
函數(shù)的toString
方法返回一個字符串丹泉,內容是函數(shù)的源碼
情萤。
function fn1(a,b) {
console.log("hello,world!");
}
console.log(fn1.toString());
通過toString
方法可以將函數(shù)全部的源碼返回。
而toString
方法可以返回function(){native code}
摹恨。
console.log(Math.abs.toString()); // function abs() { [native code] }
上面代碼中筋岛,Math.abs
是js
提供的原生
的函數(shù),toString
方法就會返回原生代碼的提示晒哄。
需要注意的是睁宰,當我們使用toString方法
時,函數(shù)內部的注釋也會被返回
函數(shù)的作用域
定義
作用域(scope)指的是變量可以生存的范圍或者說存在的范圍寝凌。
在老版本的ES5的規(guī)范里柒傻,JS只有兩種作用域:一種是全局作用域,變量在整個程序中一直存在较木,在任何的地方都可以讀群旆;另外一種
是函數(shù)作用域,變量只在函數(shù)內部存在违孝,在函數(shù)的外部沒有辦法訪問到函數(shù)內部的變量刹前。
ES6當中新增加了塊級作用域。
對于頂層函數(shù)來說雌桑,函數(shù)外部聲明的變量就是全局變量(global variable)喇喉,可以在函數(shù)內部讀取。
var a = 10;
function fn1() {
console.log(a);
}
fn1(); // 10
上面的代碼中校坑,我們在函數(shù)的外部聲明了一個變量拣技,我們在函數(shù)的內部也讀取到了全局變量的值。
在函數(shù)內部定義的變量耍目,在函數(shù)的外部則無法讀取膏斤,我們將其稱為局部變量(local variable)。
function t_fn1() {
var a = 10;
}
console.log(a); // ReferenceError: a is not defined 引用錯誤:未定義
上面的案例中邪驮,我們嘗試從函數(shù)的外部訪問函數(shù)內部的局部變量(local variable)莫辨,發(fā)現(xiàn)js提示我們變量沒有定義,訪問失敗毅访。
函數(shù)內部定義的局部變量沮榜,會覆蓋掉函數(shù)外部定義的同名的全局變量
例如:
var x = 10;
function fn1(){
var x = 20;
console.log(x);
}
fn1(); // 20
在上面的代碼中,變量x同時在函數(shù)外部和函數(shù)內部定義喻粹。結果顯而易見蟆融,函數(shù)內部的變量x覆蓋了函數(shù)外部的變量x。
但是需要注意的是守呜,這種覆蓋的變量型酥,生效范圍只能是在函數(shù)內部,如果出了函數(shù)查乒,則變量x恢復到本身的10弥喉。
例如:
var x = 10;
function fn1() {
var x = 20;
console.log(x);
}
fn1(); // 20
console.log(x); // 10
在上面的代碼中,我們在函數(shù)內部訪問變量,x的值輸出為函數(shù)內部變量的值20,而當出了函數(shù)侣颂,我們再來訪問档桃,你會發(fā)現(xiàn)變量x的值仍然為10。
所以說函數(shù)內部變量的覆蓋僅停留于函數(shù)內部憔晒,出了函數(shù)就會恢復過來藻肄。
注意,對于var
命令來說拒担,局部變量
只能在函數(shù)內部聲明嘹屯,在其他區(qū)塊中聲明,一律都是全局變量
从撼。
if (true) {
var x = 5;
}
console.log(x); // 5
函數(shù)內部的變量提升
與全局作用域一樣州弟,函數(shù)作用域內部也會產生變量提升現(xiàn)象钧栖。var
命令聲明的變量,不管在什么位置婆翔,變量聲明都會提升到函數(shù)體的頭部拯杠。
例如:
function fn1() {
var x = 2;
if (x > 3){
var tmp = x - 100;
x = x + 1;
}
}
上面的代碼并沒有什么實際的意義,但是這樣一段代碼在發(fā)生變量提升之后等同于下面的代碼:
function fn1() {
var x,tmp;
x = 2;
if(x > 3) {
tmp = x - 100;
x = x + 1;
}
}
函數(shù)本身的作用域
上面我們說過啃奴,函數(shù)
在js
當中是一等公民
潭陪。本質上來講也就是一個能夠運行的值
。So,函數(shù)既然是一個值最蕾,那么函數(shù)也就有著自己的作用域依溯。
函數(shù)的作用域與變量相同,就是其聲明時所在的作用域瘟则,一定要注意的是黎炉,函數(shù)的作用域與其運行時所在的作用域沒有關系。
例如:
var a = 1;
var x = function () {
console.log(a);
};
// 函數(shù)x在全局作用域環(huán)境下創(chuàng)建醋拧,所以說x函數(shù)的作用域就是全局慷嗜,雖然x函數(shù)是在函數(shù)f中調用,但是作用域仍然是在全局趁仙。
function f() {
var a = 2;
x();
}
f() // 1
在上面的代碼中洪添,函數(shù)x是在全局作用域聲明的函數(shù),所以此時函數(shù)的作用域被綁定到全局雀费。
函數(shù)的參數(shù)
函數(shù)的運行時,有時需要提供外部數(shù)據(jù)痊焊,我們提供不同的數(shù)據(jù)會導致不同的結果盏袄。這種外部的數(shù)據(jù)就叫做參數(shù)。
// 定義一個函數(shù)薄啥,此時函數(shù)需要外部的數(shù)據(jù)a和b
function fn1(a,b) {
return a + b;
}
// 調用函數(shù)并且傳入數(shù)據(jù)
var x = 10;
var y = 20;
console.log(fn1(x,y));
在上面的案例中辕羽,a和b就是函數(shù)fn1的參數(shù)。在調用的時候垄惧,我們需要從外部傳入數(shù)據(jù)來使用刁愿。
函數(shù)參數(shù)使用時需要注意的點:
- 函數(shù)參數(shù)不是必須的,JavaScript允許省略參數(shù)到逊。
function f1(a,b) {
// 如果函數(shù)在運行時沒有使用參數(shù)铣口,那么參數(shù)也可以省略
console.log('hello,world!');
}
f1(); // hello,world
上面的代碼中,我們的函數(shù)在運行過程中并不需要參數(shù)a和b觉壶,所以我們在調用函數(shù)時候可以選擇省略脑题。但是需要注意的是,我們在函數(shù)運行的過程中如果定義了參數(shù)并且使用了參數(shù)而你卻沒有傳入實參铜靶。那么函數(shù)體內使用該參數(shù)會返回undefined
叔遂。
function f1(a,b) {
// console.log('hello,world!');
console.log(a,b);// undefined
}
// 調用函數(shù)的時候并沒有傳入實參
f1();
如果通過length屬性查看參數(shù),那么length屬性返回的值其實是形參的長度而不是實參的長度,也就意味著已艰,只要你定義了函數(shù)
的形參痊末,即使你在調用函數(shù)的時候沒有傳入實參,length屬性也可以返回值哩掺。
- 函數(shù)的參數(shù)不能只省略個別
當我們調用函數(shù)的時候凿叠,我們不能省略只省略靠前的參數(shù),而保留靠后的參數(shù)疮丛。如果一定要這么做幔嫂,那么只有顯示的傳入undefined才可以。
function f1(a,b,c,d) {
console.log(a,b,c,d);
}
// 調用函數(shù)的時候誊薄,如果想省略其中的個別參數(shù)履恩,只能顯示的傳入undefined
// 而如果省略其中的一個參數(shù)就會報錯。
f1(undefined,undefined,10,20);
- 函數(shù)參數(shù)傳遞方式
函數(shù)參數(shù)如果是原始類型的值(數(shù)值呢蔫、字符串切心、布爾值),傳遞方式是傳值傳遞(passes by value)片吊。這意味著绽昏,在函數(shù)體內修改參數(shù)值,不會影響到函數(shù)外部俏脊。
例如:
var a = 10;
function fn1(param) {
param += 3;
console.log('在函數(shù)內部的變量值為:' + param);
}
fn1(a); // 在函數(shù)內部的變量值為:13
console.log(a);// 10
上面代碼中全谤,變量p是一個原始類型的值,傳入函數(shù)f的方式是傳值傳遞爷贫。因此认然,在函數(shù)內部,p的值是原始值的拷貝漫萄,無論怎么修改卷员,都不會影響到原始值。
但是腾务,如果函數(shù)參數(shù)是復合類型的值(數(shù)組毕骡、對象、其他函數(shù))岩瘦,傳遞方式是傳址傳遞(pass by reference)未巫。也就是說,傳入函數(shù)的原始值的地址担钮,因此在函數(shù)內部修改參數(shù)橱赠,將會影響到原始值。
var obj = { p: 1 };
function f(o) {
o.p = 2;
}
f(obj);
obj.p // 2
上面代碼中箫津,傳入函數(shù)f的是參數(shù)對象obj
的地址狭姨。因此宰啦,在函數(shù)內部修改obj
的屬性p
,會影響到原始值饼拍。
注意赡模,如果函數(shù)內部修改的,不是參數(shù)對象的某個屬性师抄,而是替換掉整個參數(shù)漓柑,這時不會影響到原始值。
var obj = [1, 2, 3];
function f(o) {
o = [2, 3, 4];
}
f(obj);
obj // [1, 2, 3]
上面代碼中叨吮,在函數(shù)f
內部辆布,參數(shù)對象obj
被整個替換成另一個值。這時不會影響到原始值茶鉴。這是因為锋玲,形式參數(shù)(o)
的值實際是參數(shù)obj
的地址,重新對o
賦值導致o
指向另一個地址涵叮,保存在原地址上的值當然不受影響惭蹂。
- 同名參數(shù)
如果有同名的參數(shù),則取最后出現(xiàn)的那個值割粮。
例如:
function f1(a,a){
console.log(a);
}
f1(1,2); // 2
上面代碼中盾碗,函數(shù)f有兩個參數(shù),且參數(shù)名都是a舀瓢。取值的時候廷雅,以后面的a為準,即使后面的a沒有值或被省略京髓,也是以其為準榜轿。
function f(a, a) {
console.log(a);
}
f(1) // undefined
調用函數(shù)f的時候,沒有提供第二個參數(shù)朵锣,a的取值就變成了undefined
。這時甸私,如果要獲得第一個a的值诚些,可以使用arguments
對象。
function f(a, a) {
console.log(arguments[0]);
}
f(1) // 1