ホームページ > 記事 > ウェブフロントエンド > JavaScript 学習まとめ [4] JS の詳細
1.JSフロー制御文
(1)、if判定
if文は成立した条件に基づいて対応するコードを実行します。
if...else文とは、指定した条件が真の場合にifが実行され、条件が真ではない場合にelseの後のコードが実行されます。
if...else ネストされたステートメントは、複数の条件下で実行する対応するコード ブロックの 1 つを選択します。
if ステートメントはあらゆる種類のデータに適しており、複雑な論理関係を処理できます。
(2)、switch文
選択肢が多い場合、if...elseよりもswitchの方が使いやすいです。単純な構造で複数の選択肢に特化して設計されていますが、扱えるのは複数の列挙型論理のみです。関係。このステートメントは、個人の好みに応じて if を使用して完成させることもできます。
switch ステートメントの仕組み: まず式 (通常は変数) を作成し、その式の値を switch ステートメント内の各 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ループ
多くのことは一度だけでなく、繰り返し行う必要があります。たとえば、文書を一度に 1 部ずつ 10 部印刷し、印刷が完了するまでこの操作を繰り返します。このようなことは、for ループを使用して行われます。ループとは、毎回異なる値を使用してコードを繰り返し実行することです。
以下は for ループの小さな応用です。異なる金種の 1.2.3... 10 人民元があると仮定し、合計で何人民元があるかを計算します。
var sum = 0; for(var rmb=1; rmb<=10; rmb++){ sum += rmb; } alert('一共有: ' + sum + '元'); //返回:一共有:55元
(4)、whileループ
whileループは、条件がtrueに指定されている限り、条件が満たされなくなるまでループを実行できます。
//使用while循环输出5个数字 var i = 0; //第一部分:初始化,给一个初始的值,让i从0循环 while(i < 5){ //第二部分:条件。成立继续循环,不成立退出 alert(i); //第三部分:语句 i++; //第四部分:自增 } //while循环使用比较复杂,使用for循环更简洁。 //for(初始化;条件;自增){语句}
(5), do...while ループ
do...while ループの原理構造は基本的に while ループと同じですが、ループは条件が満たされているかどうかを確認する前にコード ブロックを 1 回実行します。 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 ステートメントは、現在のループから飛び出し、直接ループを抜けて次のコードを実行する、つまり、それ以上の判断を行わずにループ全体を終了するために使用されます。 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 の値は次のとおりです: 数値 (整数と 10 進数を含む)、文字列 (二重引用符で囲まれた)、ブール値 (true または false)、オブジェクト (中括弧で囲まれた)、配列 (角括弧で囲まれた) 、またはnull。
JSONはプレーンテキストであり、通常、サーバーがWebページにデータを転送し、サーバーからJSONデータを取得し、Webページでデータを使用するために使用されます。
(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)!