1、JS流程控制語句
(1)、if 判斷
if 語句是基於條件成立時才執行對應的程式碼。
if...else 語句是在指定的條件成立時執行if後的代碼,在條件不成立時執行else後的代碼。
if...else 嵌套語句是在多種條件下選擇對應的程式碼快之一來執行。
if 語句適用於任意類型的數據,可處理複雜的邏輯關係。
(2)、switch語句
當有很多種選擇的時候,switch 比 if...else 使用更方便,結構簡潔,專為多重選擇設計,但是僅能處理多個枚舉型邏輯關係。該語句使用 if 也可以完成,這個看個人喜好。
switch 語句的工作原理:首先創建一個表達式,通常為變量,之後表達式的值與switch 語句中每個case 的值做比較,如果匹配,則執行該case 後的語句,若與所有case值都不匹配,則執行default 後的語句。使用 switch 語句時,每個 case 語句後必須使用 break 跳出循環,阻止執行下一個 case。
var d = new Date().getDay(); //如果今天不是周末,则提示默认消息 switch (d){ case 6: alert("今天是星期六"); break; case 0: alert("今天是星期天"); break; default: alert("同志尚未努力,革命仍需成功。"); }
switch 語句在做比較時,使用的是全等,而不是相等,所以在字串與數字匹配時,需要特別注意。
//使用switch语句将字符串与数字做比较 //返回:不等于,执行default语句 var n = '5'; switch(n){ case 5: alert('等于,执行case语句'); break; default: alert('不等于,执行default语句'); } //使用if语句将字符串与数字做比较 //返回:等于 var n = '2'; if(n == 2){ alert('等于'); }else{ alert('不等于'); } //将case的值改为字符串再做比较 //返回:等于,执行case语句 var n = '2'; switch(n){ case '2': alert('等于,执行case语句'); break; default: alert('不等于,执行default语句'); } //使用全等再做比较 //返回:不等于 var n = '2'; if(n===2){ alert('等于'); }else{ alert('不等于'); }
(3)、for 循環
很多事情不只是做一次,需要重複做。例如列印 10 份文件,每次列印 1 份,重複這個動作,直到列印完成。這樣的事情就用 for 迴圈來完成,迴圈就是重複執行一段程式碼,每次的值不同。
下面是一個 for 迴圈的小應用,假設有 1.2.3. ... 10 不同面值的 RMB,計算一共有多少 RMB。
var sum = 0; for(var rmb=1; rmb<=10; rmb++){ sum += rmb; } alert('一共有: ' + sum + '元'); //返回:一共有:55元
(4)、while 迴圈
while 迴圈和 for 迴圈具有相同的功能,只要指定條件為 ture,迴圈就可以一直執行,直到條件不再滿足。
//使用while循环输出5个数字 var i = 0; //第一部分:初始化,给一个初始的值,让i从0循环 while(i < 5){ //第二部分:条件。成立继续循环,不成立退出 alert(i); //第三部分:语句 i++; //第四部分:自增 } //while循环使用比较复杂,使用for循环更简洁。 //for(初始化;条件;自增){语句}
(5)、do...while 循環
do...while 循環與while 循環的原理結構是基本相同的,但是該循環會在檢查條件是否為ture 之前執行一次代碼塊,如果條件為ture,則重複循環。這個循環有一點小問題,因為他是先執行程式碼,後判斷條件,如果條件不當,則進入死循環,導致瀏覽器崩潰。
/* 语法: do{ 执行语句 } while(条件); */ //操作有风险,尝试需谨慎 //若将循环条件改为:num <= 6 会导致浏览器崩溃 var num = 6; do{ document.write("数字:" + num + "<br>"); num -= 1; } while(num > 0);
(6)、JS 錯誤處理語句
try...catch 語句用於進行異常處理。 try 語句用來偵測程式碼區塊的錯誤,指明需要處理的程式碼片段,catch 語句用於處理 try 語句中拋出的錯誤。 try 語句首先被執行,如果運行中發生了錯誤,try 語句中的程式碼將被跳過執行 catch 中的語句。如果沒有發生錯誤,則不執行 catch 中的語句。一般針對可預見性的錯誤,可使用 try...catch 語句進行處理。
try{ document.write("开始执行try语句" + '<br>'); document.write("还没抛出错误" + '<br>'); alert(x); //抛出错误 alert('123'); //没被执行 } catch(e){ document.write("捕捉到错误,开始执行catch语句" + '<br>'); document.write("错误类型: " + e.name + '<br>'); document.write("错误信息: " + e.message); alert('x'); }
throw 語句可用來建立自訂錯誤。官方術語為:創建或拋出異常(exception)。語法:throw '異常物件'。
throw 語句可以配合 try...catch 語句一起使用,以達到控製程式流,產生精確的錯誤訊息。
//输入0到10之间的数字,如果输入错误,会抛出一个错误,catch会捕捉到错误,并显示自定义的错误消息。 <body> <input id="txt" type="text"/> <span id="demo" style="font-weight:bold;"></span><br> <input type="button" value="检测输入" onclick="error()"> <script> function error(){ try{ var x = document.getElementById("txt").value; var y = document.getElementById("demo"); y.style.color = 'red'; if(x == '') throw '输入不能为空'; if(isNaN(x)) throw '请输入数字'; var num = [7,8,9]; for(var i=0; i<num.length; i++){ if(x == num[i]){ throw '该数字已经存在'; } } if(x == 0){ throw '输入不能为0'; } else if(x > 10){ throw '数字太大了'; } else if(x <= 3){ throw '数字太小了'; } else{ y.style.color = 'green'; y.innerHTML = 'OK'; } } catch(e){ y.innerHTML = '错误提示:' + e + '!'; } } </script> </body>
(7)、跳出迴圈
break 語句用來跳出目前循環,直接退出迴圈執行後面的程式碼,即終止整個循環,不再判斷。 continue 語句只是跳出本次循環,繼續執行後面的循環,即結束本次循環,接著去判斷是否執行下次循環。 return 可以終止函數體的運行,並傳回一個值。
for(var i=0; i<6; i++){ if(i == 3) break; //当i=3时跳出整个循环,不再执行循环 alert(i); //返回:0,1,2 } for(var i=0; i<6; i++){ if(i == 3) continue; //当i=3时跳出本次循环,继续执行后面循环 alert(i); 返回:0,1,2,4,5 }
2、JSON
JSON(JavaScript Object Notation):JS 物件表示法。 JSON 主要用於儲存和交換資料訊息,類似於 XML,但相比 XML,JSON 易於閱讀和編寫,也易於解析。
JSON 語法是 JS 物件表示語法的子集:資料在鍵值對中,並由逗號分隔,花括號保存對象,方括號保存陣列。
JSON 語法的書寫格式:"名稱" : "值", "名稱" : "值"
名稱和值包含在雙引號中,並用冒號分隔,每條數據用逗號分隔。這很容易理解,相對於 JS 中 名稱 = "值"。
JSON 的值可以是:數字(包括整數和小數),字串(包含在雙引號中),布林值(true 或false),物件(包含在花括號中),陣列(包含在方括號中),或為null。
JSON 是純文本,通常用於服務端向網頁傳遞數據,從伺服器上獲取 JSON 數據,然後在網頁中使用該數據。
(1)、JSON物件
var json = {"a": 12, "b": "abc", "c":[1,2,3]}; //返回第一项的值: alert(json.a); //修改第二项的值 alert(json.b = "xyz"); //返回第三项数组中第一项的值 alert(json.c[0]);
(2)、JSON 和陣列
相同點:
都可以透過下標傳回某項的值。都可以使用循環。雖然 JSON 沒有 length 屬性,不能使用 for 循環,但可以使用 for...in 循環,完成與 for 迴圈相同的動作。
數組也可以使用 for...in 循環,但最好還是使用 for 循環。 for...in 迴圈遍歷的是物件的屬性,而不是陣列元素。
不同點:
JSON 的下标是字符串,数组的下标为数字。JSON 没有 length 属性,数组有该属性。
var arr = [12,5,7]; var json = {"a":12,"b":5,"c":7}; alert(arr[0]); //返回:12 alert(json["a"]); //返回:12 alert(arr.length); //返回:3 alert(json.length); //返回:undefined //数组for循环 for(var i=0; i<arr.length; i++){ alert('第' + (i+1) + '个数据是:' + arr[i]); } alert(typeof i); //返回:number //数组使用for...in循环 for(var i in arr){ alert('第' + (i+1) + '个数据是:' + arr[i]); } alert(typeof i); //返回:string //JSON使用for...in循环 for(var i in json){ alert('第' + i + '个数据是:' + json[i]); }
(3)、JSON 数组对象
<body> <p> 姓 名: <span id="fname"></span><br> 性 别: <span id="gender"></span><br> 员工号: <span id="num"></span><br> 修改姓名: <span id="lname"></span><br> </p> <script> var staff = [ {"name" : "小明", "sex" : "男", "id" : 1}, {"name" : "小白", "sex" : "男", "id" : 2}, {"name" : "小红", "sex" : "女", "id" : 3} ]; var x = document.getElementById("fname"); var y = document.getElementById("gender"); var z = document.getElementById("num"); var n = document.getElementById("lname"); //访问对象数组中第一项的值: x.innerHTML = staff[0].name; y.innerHTML = staff[0].sex; z.innerHTML = staff[0].id; //修改数据: n.innerHTML = staff[1].name = '大白'; </script> </body>
(4)、JSON 字符串对象
var str = '{"name":"小明", "sex":"男", "age":21}'; var toObj = JSON.parse(str); //JSON字符串转换为JSON对象alert(toObj.name); alert(typeof toObj); //返回:object var json = {"name":"小红", "sex":"女", "age":18}; var toStr = JSON.stringify(json); //JSON对象转换为JSON字符串 alert(toStr); //返回字符串alert(json.age); alert(typeof toStr); //返回:string
(5)、JSON 应用
当需要表示一组数据时,JSON 不但能够提高可读性,而且还可以减少复杂性。JSON 能够表示多个值,每个值又可包含多个值,例如要表示一个用户列表信息,就可以将所有信息存储在一个变量中,分成多项,每项中又可分成多个条目,每个条目中记录一个用户的信息。
var userName = { "first": [{ "name": "路飞", "sex": "男", "tel": "aaa" }, { "name": "索罗", "sex": "男", "tel": "bbb" }, { "name": "娜美", "sex": "女", "tel": "ccc" }], "second": [{ "name": "卡卡西", "sex": "男", "tel": "ddd" }, { "name": "鸣人", "sex": "男", "tel": "fff" }, { "name": "佐助", "sex": "男", "tel": "eee" }, { "name": "皱田", "sex": "女", "tel": "sss" }], "third": [{ "name": "小明", "sex": "男", "tel": "xxx" },{ "name": "小红", "sex": "女", "tel": "zzz" }] }; //获取用户的信息: alert(userName.first[1].name + ' \n ' + userName.first[1].sex + '\n '+ userName.first[1].tel); alert(userName.second[3].name + ' \n ' + userName.second[3].sex +' \n '+ userName.second[3].tel); alert(userName.third[0].name + ' \n ' + userName.third[0].sex + ' \n '+ userName.third[0].tel);
说到 JSON,就不得不提一下 JSONP。JSONP (JSON with Padding) 是 JSON 的一种 "使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据。可用于解决主流浏览器的跨域数据访问的问题。为什么我们从不同的域(网站)访问数据需要一个特殊的技术 (JSONP) 呢?这是因为同源策略。同源策略,它是由 Netscape 提出的一个著名的安全策略,现在所有支持 JavaScript 的浏览器都会使用这个策略。由于该策略,一般来说位于 server1 的 demo.com 的网页无法与不是 server1 的 demo.com 的网页的服务器沟通,而 HTML 的 3f1c4e4b6b16bbbd69b2ee476dc4f83a 元素是一个例外。利用 3f1c4e4b6b16bbbd69b2ee476dc4f83a 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSON 资料,而这种使用模式就是所谓的 JSONP。用 JSONP 抓到的资料并不是 JSON,而是任意的 JavaScript,用 JavaScript 直译器执行而不是用 JSON 解析器解析。
3、JS 定时器
定时器可以在指定的时间间隔之后再执行代码,而不是在函数被调用后立即执行。定时器在网页中应用非常广泛,最常见的就是动态时钟,还有比如购物网站的倒计时抢购。定时器的类型可分为两类:一类是间隔型,即 setInterval,在执行时,从页面加载后每隔一段时间执行一次,可无限执行。另一类是延迟型,即 setTimeout,在页面加载后延迟指定的时间,去执行一次,而且仅仅只执行一次。该方法属于 window 对象的两个方法。
(1)、setInterval
setInterval(function, time) 方法可间隔指定的毫秒数,不停的执行指定的代码。该方法有两个参数,第一个参数是函数,指定定时器要调用的函数或要执行的代码串,第二个参数是时间,用毫秒计,1000 毫秒是 1 秒,指定执行的间隔时间。
(2)、setTimeout
setTimeout(function, time) 方法可延迟指定的毫秒数后,再执行一次指定的代码。该方法也有两个参数,第一个参数为函数,指定要调用的函数或代码串,第二个参数指定在执行代码前需要等待多少毫秒。
function show(){ alert(1); } //当页面加载后,每隔1秒弹出一个1,无限次执行 setInterval(show,1000); //当页面加载后,在1秒后弹出一个1,只执行一次 setTimeout(show,1000);
setInterval 动态时钟效果:
//动态显示时钟 <p id="demo"></p> <script> function clock(){ var d = new Date(); var time = d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds() ; var oP = document.getElementById("demo").innerHTML = time; } setInterval(clock,1000); </script>
setTimeout 统计效果:
//setTimeout可实现统计: <input type="text" id="demo" > <script> var num = 0; function start() { document.getElementById('demo').value = num; num += 1; setTimeout(start,1000); } setTimeout(start,1000); </script>
可以开启定时器,也就可以关闭定时器。两种类型对应着两种方法。
(1)、clearInterval
clearInterval() 方法可关闭由 setInterval() 方法执行的函数代码。使用该方法关闭定时器时,在创建间隔定时器时必须使用全局变量。
开始、停止动态时钟效果:
//开始、停止动态时钟 <input type="text" id="txt1" > <input type="button" value="停止" onclick="stop()" > <input type="button" value="开始" onclick="start()" > <script> var time = null; function start(){ time = setInterval(function (){ var d = new Date(); var t = d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds(); var oTxT = document.getElementById("txt1").value = t; },1000); }; start(); function stop(){ clearInterval(time); } </script>
(2)、clearTimeout
clearTimeout() 方法用于停止执行由 setTimeout() 方法执行的函数代码。使用该方法时关闭定时器时,在创建延迟定时器时必须使用全局变量。
开始、停止统计效果:
//开始、停止统计: <input type="text" id="txt1" > <input type="button" value="停止" onclick="stop()" > <input type="button" value="开始" onclick="start()" > <script> var num = 0; var time = null; function start(){ var oTxt = document.getElementById('txt1').value = num; num += 1; time = setTimeout('start()',1000); } start(); function stop(){ clearTimeout(time); } </script>
4、Event 对象
Event 对象代表事件的状态,用于获取事件的详细信息,如鼠标按钮、鼠标位置、键盘按键。事件通常与函数一起使用,函数不会在事件发生前被执行。
(1)、获取鼠标坐标
screenX 和 screenY 返回鼠标相对于屏幕的水平坐标和垂直坐标。参照点为屏幕的左上角。
clientX 和 clientY 返回鼠标相对于当前窗口可视区的水平坐标和垂直坐标。参照点为浏览器页面的左上角。
document.onclick = function (){ //可视区坐标 alert(event.clientX + ',' + event.clientY); //屏幕坐标 alert(event.screenX + ',' + event.screenY); }
(2)、获取鼠标钮按
button 事件属性用于获取鼠标哪个按钮被点击了。返回一个整数,0 代表左键,1 代表中键,2 代表右键。
//鼠标左右按键 document.onmousedown = function (){ alert(event.button); }
(3)、获取键盘按键
keyCode 事件属性用于获取按下了键盘的哪个键,返回键码,表示键盘上真实键的数字。
//键盘按键 document.onkeydown=function (){ alert(event.keyCode); };
键盘按键的 ctrlKey、shiftKey 和 altKey 快捷属性,可判断是否按下了该键,返回一个布尔值,指示在事件发生时,改键是否被按下。1 表示被按下,0 表示没有按下。
document.onkeydown = function (){ if(event.ctrlKey == 1){ alert('Ctrl键被按了'); } else if(event.shiftKey == 1){ alert('Shift键被按了'); } else if(event.altKey == true){ alert('Alt键被按了'); } else{ alert('都没被按下'); } };
实例:按键提交消息
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>JavaScript示例</title> <script> window.onload=function (){ var oTxt1 = document.getElementById('txt1'); var oTxt2 = document.getElementById('txt2'); var oBtn = document.getElementById('btn1'); //点击按钮提交 oBtn.onclick = function (){ //输入框的值等于文本框的值。 oTxt2.value += oTxt1.value + '\n'; //清空输入框的值,以便再次输入 oTxt1.value = ''; }; //按Enter或Ctrl+Enter提交 oTxt1.onkeydown = function (){ //回车键的键码为13 if(event.keyCode == 13 || event.keyCode == 13 && event.ctrlKey){ oTxt2.value += oTxt1.value + '\n'; oTxt1.value = ''; } }; } </script> </head> <body> <input id="txt1" type="text" > <input id="btn1" type="button" value="提交"><br> <textarea id="txt2" rows="10" cols="50" ></textarea> </body> </html>
(4)、事件流
事件的传递有两种方式:冒泡与捕获。事件传递定义了元素触发事件的顺序。
事件冒泡:当一个元素发生事件后,事件会顺着层级(父级 - 父级的父级 --)关系依次向上传递直到 document。
事件捕获:事件捕获与事件冒泡正好相反,外部元素的事件会先被触发,然后才会触发内部元素的事件,即从祖先到后代。
事件流同时支持两种事件方式,冒泡型事件和捕获型事件,但是捕获型事件先发生。
两种事件流会触发 DOM 中的所有对象,从 document 对象开始,也在 document 对象结束。
语法:addEventListener('事件名称',函数,冒泡/捕获)
addEventListener() 方法用于向指定元素添加事件,该方法不会覆盖已存在的事件,可同时向一个元素添加多个事件。该方法有三个参数,第一个参数定义事件的类型,
第二个参数规定事件触发后调用的函数,第三个参数是布尔值,用于定义该事件是冒泡还是捕获,若为 false,则表示冒泡事件,若是 ture,则表示捕获事件。
这里需要注意是的该方法的事件类型,不需要加”on“,比如平时写点击事件:“onclick”,该方法中则使用“click”即可。
<!DOCTYPE html> <html id="htm"> <head> <meta charset="utf-8" /> <title>JavaScript示例</title> <style> div{padding:50px;} </style> <script> window.onload = function (){ var x = document.getElementById("div1"); var y = document.getElementById("div2"); var z = document.getElementById("div3"); var o = document.getElementById("bod"); var n = document.getElementById("htm"); x.addEventListener("click", function() { alert("1冒泡"); }, false); y.addEventListener("click", function() { alert("2冒泡"); }, false); z.addEventListener("click", function() { alert("3冒泡"); }, false); o.addEventListener("click", function() { alert("body捕获"); }, true); n.addEventListener("click", function() { alert("html冒泡"); }, false); }; </script> </head> <body id="bod"> <div style="background:lightgreen;margin-bottom:10px;">我是body元素,我捕获。祖先html也会冒泡。</div> <div id="div3" style="background:#ccc;">我是div3,我冒泡 <div id="div2" style="background:green;">我是div2,我冒泡 <div id="div1" style="background:red;">我是div1,我冒泡</div> </div> </div> </body> </html>
removeEventListener() 方法用于移除由 addEventListener() 方法添加的事件监听。这里需要注意在绑定函数时不能使用匿名函数,否则无法删除。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>JavaScript示例</title> <style> #div1{ background:green; padding:50px; color:white; } </style> </head> <body> <div id="div1"> div元素添加了onmousemove事件监听,鼠标在绿色区域内移动时会显示随机数。 <p>点击按钮可移除div的事件监听。</p> <button id="btn1">点击移除</button> </div> <p id="p1"></p> <script> var oDiv = document.getElementById("div1"); var oP = document.getElementById("p1"); var oBtn = document.getElementById("btn1"); oDiv.addEventListener("mousemove", block, false); function block(){ oP.innerHTML = Math.random()*10; } oBtn.onclick = function (){ oDiv.removeEventListener("mousemove", block, false); }; </script> </body> </html>
cancelBubble 方法可取消事件冒泡,不会往父级传递。实例:仿百度翻译效果,点击显示按钮显示 div,随便点击页面其他位置隐藏 div。
如果不取消事件冒泡,则在点击按钮的同时 div 先是显示了,然后又立马被隐藏了,可以注释掉取消事件冒泡代码,用弹窗查看效果。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>JavaScript示例</title> <style> #div1{ width:500px; height:300px; background:lightgreen; display:none; } </style> <script> window.onload = function (){ var oBtn = document.getElementById("btn1"); var oDiv = document.getElementById("div1"); //点击按钮显示div oBtn.onclick = function (){ oDiv.style.display = 'block'; //alert('显示'); //取消事件冒泡,不会往父级传递。 event.cancelBubble = true; }; //点击document隐藏div document.onclick = function (){ oDiv.style.display = 'none'; }; }; </script> </head> <body> <input id="btn1" type="button" value="显示"> <div id="div1"></div> </body> </html>
(5)、默认事件
所谓的默认事件,就是浏览器自带的事件。比如按下键盘按键,浏览器会自动将按键值写入输入框。再比如新建一个空白页面在浏览器打开,点击右键出现菜单项。我们并没有用 JS 写相关判断,如果点击右键触发什么事件。这就是浏览器的默认事件,也叫默认行为。如果我们想弹出自定义的右键菜单项,这时候就需要阻止掉浏览器的默认行为,阻止默认事件最简单的写法就是 return false; 。
实例:只能输入数字键的输入框,不考虑小键盘区。
实现思路:键盘区数字键 0 的键码是 48,1 是 49,9 是 57,那么就可以做出判断,如果按键小于 48 或大于 57,则阻止掉,这说明按下的不是数字键,考虑到写错了需要删除,或者少写了,需要光标移动到少写的位置补上,再移回继续输入,那么就再加上判断条件,如果按键不是退格键或者不是左右方向键,则阻止掉。删除键(退格键)的键码是 8,左方向键是 37,右方向键为 39。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>JavaScript示例</title> <script> window.onload = function (){ var oTxt = document.getElementById('txt1'); oTxt.onkeydown = function (){ //如果按下的不是删除键8,并且不是左方向键37,并且不是右方向键39,并且按键键码小于48或大于57,则阻止掉 if(event.keyCode !=8 && event.keyCode != 37 && event.keyCode != 39 && event.keyCode < 48 || event.keyCode > 57){ return false; } }; }; </script> </head> <body> <input id="txt1" type="text" placeholder="请输入数字"> </body> </html>
5、JS 知识点
(1)、JS 引擎的预解析机制
JS 引擎的解析过程可分为两个阶段:预解析阶段和执行阶段。
JS 预解析是在程序进入一个新环境时,把该环境中的变量和函数预解析到他们能调用的环境中,即每一次的预解析单位是一个执行环境。当文档流中有多个 JS 代码段,每个代码段用 script 标签分隔,包括外部引入的 JS 文件,JS 引擎并非是逐行的解析程序,而是分段进行的,即一个执行环境。预解析不能跨代码段执行,简单说就是不能在一个代码段声明,在另一个代码段调用。
变量和函数的预解析就是提升,所谓提升(hoisting),就是 JS 引擎在执行时,默认将所有的变量声明和函数声明都提升到当前作用域的最前边去的行为。所以函数可以在声明之前调用。需要注意的是在使用表达式定义函数时无法提升
<script> alert(a(2)); //返回:4 function a(x){ return x * x; } alert(b); //返回:undefined var b = 'hello'; alert(c(2)); //报错 //实际上是以变量声明提升 //相当于:c(); var c = undefined; c = function (){} var c = function (y){ return y * y; } function d(){ var n = 'hello'; } alert(n); //报错 </script>
通过上面的代码可以看出,function 定义的函数声明 (a) 在代码开始执行之前(预解析阶段)对其实现了函数声明提升,先将其放入内存中,所以在函数声明之前可以调用该函数。和函数声明一样,变量声明 (b) 也会在一开始被放入内存中,但是并没有赋值,所以在他赋值之前,他的值就是 undefined。但是函数表达式 (c) 不同,函数表达式用 var 声明,也就是说解析器会对其变量提升,并对其赋值为 undefined,然后在执行期间,等到执行到该 var 变量的时候再将其变量指向一个 function 函数,所以在函数表达式之前执行该函数就会报错。函数 (d) 是在函数内声明的变量,那么这个变量是属于该函数的私有变量,所以在外部调用时会报错。
下面实例实例说明了每一次 JS 预解析的单位是一个执行环境,不会跨一个代码段去执行,直接会报错。
<script> alert(a);//报错:a is not defined </script> <script> var a = 'hello'; </script>
若定义了两个同名的函数 (b),则在预解析时后面的一个会覆盖掉前边的一个。若变量 (a) 和函数重名 (a),则函数的优先级高于变量的优先级。
<script> alert(a); //返回:function a(){alert('hi');} var a = 'hello'; function a(){ alert('hi'); } alert(a); //返回:hello b(); //返回:2 function b(){ alert(1); } b(); //返回:2 function b(){ alert(2); } </script>
(2)、回调函数
简单理解,所谓回调,就是回头调用,那么回调函数就是一个函数调用的过程。比如函数 a 有一个参数,这个参数是一个函数 b,当函数 a 执行完以后再执行函数 b,那么这就是一个回调的过程。用官方术语解释就是:回调是一个函数作为参数传递给另一个函数,其母函数完成后执行。那么函数 a 就是母函数。这里需要注意:函数 b 是以参数的形式传递给函数 a 的,那么这个函数 b 就被称为回调函数。回调函数不是由母函数直接调用的,而是在特定的事件或者条件发生时由另外的一方调用,用于对该事件进行响应。回调函数必须使用关键字 callback,并且回调函数本身必须是全局函数。
JS 回调的原理是一个异步的流程,在异步调用的情况下使用性能很好,举个简单的例子更能具体的说明,比如朋友要来找你,等到门口了给你打电话。"来找你" 就是函数 a 开始执行,而这时候"你"可以去做任何事情,"到门口"就是函数 a 执行完毕,"给你打电话"这就属于回调函数 b,然后你们就可以一起愉快的玩耍了。
下面是一个简单的回调函数实例,点击按钮,当函数 a 执行完成后,分别调用回调函数 b、c、d。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>JavaScript示例</title> <script> function a(callback){ alert("我是母函数a"); alert("现在要开始调用回调函数"); callback(); } function b(){ alert("我是回调函数b"); } function c(){ alert("我是回调函数c"); } function d(){ alert("我是回调函数d"); } function back(){ a(b); a(c); a(d); } </script> </head> <body> <button onclick="back()">点击按钮</button> </body> </html>
(3)、自调用函数
自调用函数也叫立即执行函数。使用关键字 function 定义一个函数,在给这个函数指定一个函数名,这叫函数声明。使用关键字 function 定义一个函数,未给函数命名,在将这个匿名函数赋值个一个变量,这叫做函数表达式,这也是最常见的函数表达式语法。匿名函数就是使用关键字 function 定义一个函数,但是不给函数命名,匿名函数属于函数表达式。匿名函数有很多作用,可赋予变量创建函数,赋予一个事件则称为事件处理程序。
函数声明和函数表达式的区别:JS 引擎在解析 JS 代码时当前执行环境中的函数声明会提升,而函数表达式只能等到 JS 引擎执行到他所在的作用域时,才会逐行的解析函数表达式。函数声明不能使用自调用,而函数表达式可以使用自调用。
函数的自调用就是在函数体后加括号,再用括号整个包起来。这样说明该函数是一个函数表达式。
(function (a, b) { alert(a * b); }(2, 3)); //返回:6
(4)、构造函数
函数通过关键字 function 定义,也可以使用关键字 new 定义。函数即对象,对象具有属性和方法,构造函数就是将定义的函数作为某个对象的属性,函数定义作为对象的属性,则称之为对象方法。函数如果用于创建新的对象,就叫做对象的构造函数。
(5)、闭包
简单理解就是:子函数可以使用父函数中的局部变量。之前好几个例子中都用到了闭包,就是 window.onload 函数下定义的点击事件函数。
JS 的变量可以是全局变量或局部变量,在函数之外声明的变量就是全局变量,函数之内声明的变量就是局部变量,私有变量可以用到闭包。函数可以访问全局的变量,也可以访问局部的变量。作用域就是变量和函数的可访问范围,即作用域控制着变量和函数的可见性与生命周期,忽略块级作用域,作用域可分为全局作用域和局部作用域。全局变量是全局对象的属性,局部变量是调用对象的属性。全局变量属于 window 对象,全局变量在任何地方都可以访问,局部变量只能用于定义他的函数内部,这就是 JS 的作用域链,即内层函数可以访问外层函数的局部变量,外层函数不能访问内层函数的局部变量。全局变量和局部变量即便名称相同,他们也是两个不同的变量。修改其中一个,不会修改另一个的值。这里需要注意:在函数内声明的量,如果不使用关键字 var ,那么他就是一个全局变量。
所有函数都能访问全局变量,也就是所有的函数都可以访问他上一层的作用域。JS 支持函数嵌套,嵌套函数可以访问上一层的函数变量。闭包就是可以访问上一层函数作用域中的变量函数,即便上一层函数已经关闭。
闭包实例解析:
如果想实现点击按钮计数,可以声明一个变量,并赋初始值为 0,函数设置值加 1。但是这个全局变量在任何地方都可以使用,即便没有调用这个函数,计数也会改变。
<body> <input type="button" value="全局变量计数" onclick="show()"> <input type="button" value="调用一次变量" onclick="change()"> <p id="p1">0</p> <script> var num = 0; function count() { return num += 1; } var oP = document.getElementById("p1"); function show(){ oP.innerHTML = count(); } function change(){ alert(num = 10); } </script> </body>
点击按钮可以看到事与愿违,虽然这样不能在函数外部使用变量,也就不能修改计数,但是每次点击按钮值都为 1。因为变量是在函数内声明的,只有该函数可以使用,每点击按钮一次,调用一次该函数,每次调用变量的初始值都为 0,再加 1 就是 1。那么使用 JS 的嵌套函数可以完成这一问题,内嵌函数可以访问父函数的变量。
<body> <input type="button" value="计数" onclick="show()"> <p id="p1">0</p> <script> function count(){ var num = 0; function add(){ num += 1; } add(); return num; } add(); //报错:未定义 function show(){ document.getElementById("p1").innerHTML = count(); } </script> </body>
虽然这样可以解决变量的问题,但是如果可以在外部调用 add() 函数的话,那么点击按钮计数就完美了,梦想总是美好的,现实却是残酷的,内嵌函数不能在外部被调用。这时候我们的闭包就来了,我们需要闭包,有了闭包这个问题就真的完美了。
<body> <input type="button" value="计数" onclick="show()"> <p id="p1">0</p> <script> var count = (function (){ var num = 0; return function (){ return num += 1; }; }()); var oP = document.getElementById('p1'); function show(){ oP.innerHTML = count(); } </script> </body>
变量 count 指定了函数自我调用返回值,自我调用函数只执行一次,计数初始值为 0,并返回函数表达式,计数受匿名函数作用域的保护,只能通过 count() 方法修改。
变量 count 可以作为一个函数使用,他可以访问函数上一层作用域的计数,这就叫做 JS 闭包,函数拥有自己的私有变量。
以上就是 JavaScript学习总结【4】JS深入的内容,更多相关内容请关注PHP中文网(www.php.cn)!