1. JS 흐름 제어문
(1) if 판단
if 문은 조건이 참인 경우에만 해당 코드를 실행합니다.
if...else 문은 지정된 조건이 true일 때 if가 실행되는 이후의 코드이고, 조건이 true가 아닐 때 else가 실행되는 이후의 코드입니다.
If...else 중첩 문은 여러 조건에서 실행할 해당 코드 블록 중 하나를 선택합니다.
if 문은 모든 유형의 데이터에 적합하며 복잡한 논리적 관계를 처리할 수 있습니다.
(2), switch 문
선택사항이 많을 때 if...else보다 switch를 사용하는 것이 더 편리하며, 구조가 간단하고 다중 선택을 위해 특별히 설계되었습니다. 그러나 다중 열거된 논리적 관계만 처리할 수 있습니다. 이 문은 개인 취향에 따라 if를 사용하여 완성할 수도 있습니다.
switch 문의 작동 방식: 먼저 일반적으로 변수인 식을 만든 다음 식의 값을 switch 문의 각 Case 값과 비교합니다. 일치하는 경우 Case 뒤의 문은 다음과 같습니다. Case 값과 일치하지 않으면 default 이후의 문이 실행됩니다. switch 문을 사용할 때 루프를 중단하고 다음 Case가 실행되지 않도록 하려면 각 Case 문 다음에 break를 사용해야 합니다.
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 loop
많은 일이 한 번만 수행되는 것이 아니라 반복적으로 수행되어야 합니다. 예를 들어 문서 10장을 한 번에 한 장씩 인쇄하고 인쇄가 완료될 때까지 이 작업을 반복합니다. 이런 종류의 작업은 for 루프를 사용하여 수행됩니다. 루프는 매번 다른 값을 가진 코드 조각을 반복적으로 실행하는 것입니다.
다음은 for 루프의 작은 응용 프로그램입니다. 1.2.3 ... 10 RMB가 있고 총 몇 개가 있는지 계산합니다.
var sum = 0; for(var rmb=1; rmb<=10; rmb++){ sum += rmb; } alert('一共有: ' + sum + '元'); //返回:一共有:55元
(4), while 루프
while 루프와 for 루프는 동일한 기능을 가지며, 지정된 조건이 true인 한, 조건이 no가 될 때까지 루프를 실행할 수 있습니다. 더 오랫동안 만났습니다.
//使用while循环输出5个数字 var i = 0; //第一部分:初始化,给一个初始的值,让i从0循环 while(i < 5){ //第二部分:条件。成立继续循环,不成立退出 alert(i); //第三部分:语句 i++; //第四部分:自增 } //while循环使用比较复杂,使用for循环更简洁。 //for(初始化;条件;自增){语句}
(5), do...while 루프
do...while 루프와 while 루프의 원리 구조는 기본적으로 동일하지만 루프는 조건이 맞는지 확인합니다. true 이전에 한 번 코드 블록이 실행되었으며, 조건이 true이면 루프가 반복됩니다. 이 루프에는 코드를 먼저 실행한 다음 조건을 결정하기 때문에 약간의 문제가 있습니다. 조건이 부적절하면 무한 루프에 들어가 브라우저가 충돌하게 됩니다.
/* 语法: 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 문을 사용하여 사용자 정의 오류를 생성할 수 있습니다. 공식적인 용어는 예외를 생성하거나 발생시키는 것입니다. 구문: '예외 객체'를 발생시킵니다.
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 out of the loop
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 구문의 작성 형식: "name" : "value", "name" : "value"
이름과 값은 큰따옴표 안에 포함되며 각 부분은 콜론으로 구분됩니다. 데이터는 쉼표로 구분됩니다. 이는 JS의 name = "value"와 달리 이해하기 쉽습니다.
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에는 길이 속성이 없고 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)!