JavaScript通常嵌在互联网页中执行,再HTML页面中嵌入执行JavaScript代码有两种方式:
对于第一种方式而言,所有可以设置URL的地方都可使用这种以javascript:作为前缀的URL,当用户触发该URL时,javascript:之后的代码就会获得执行。
如果页面需要包含大量的JavaScript代码,建议将这些脚本放在<script>标签之间。script元素即可作为head子元素,也可作为body子元素。
<!DOCTYPE html> <html> <head> <meta name="author" content="Yeeku.H.Lee(CrazyIt.org)" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title> 直接运行的JavaScript </title> </head> <body> <a href="javascript:alert('运行JavaScript!');">运行JavaScript</a> <script type="text/javascript"> alert("直接运行的JavaScript!"); </script> </body> </html>
可以将JS脚本单独保存在一个*.js文件中再导入即可。语法格式如下:
<script src="test.js" type="text/javascript"></script> src:指定脚本文件所在的URL。 type:指定该元素内包含的脚本语言的类型。 charset:指定外部文件所用的字符集。 defer:指定是否延迟执行。 async:指定脚本是否异步执行。
defer属性告诉浏览器要等整个页面载入之后、解析完毕后才执行该script元素中的脚本。
下面是初学者经常犯的一个错误:
<html> <head> <meta name="author" content="Yeeku.H.Lee(CrazyIt.org)" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title> JavaScript的执行时机 </title> <script type="text/javascript" src="defer.js"> </script> </head> <body> <div id="target"></div> </body> </html>
defer.js脚本文件代码如下:
var tg = document.getElementById("target"); tg.innerHTML = "疯狂HTML 5/CSS 3/JavaScript讲义"; tg.style.backgroundColor = "#aab";
上面的JS脚本先获取id为target的元素然后修改元素的内容和背景色。但如果使用浏览器浏览该页面,可以看到target元素没有改变。
因为对于HTML5以前的script元素,当浏览器解析到script元素时,浏览器会停止继续解析、执行HTML页面,而是根据script元素的src属性下载对应的JS脚本文件,解析并执行。当浏览器执行JS脚本时,浏览器还没有去解析HTML后面的内容,他还不知道后面有id为target的元素,因此脚本中的var tg = document.getElementById("target");代码获取的元素不存在,所以后面的代码也出错了。
解决上面的做法是:将head部分的script元素移动到body元素的最后面。此外,使用defer属性也可:它会告诉浏览器必须等整个页面载入之后、解析完毕才执行script元素中的脚本,因此只要将script元素改为如下形式:
<script type="text/javascript" src="defer.js" defer></script>
defer属性只能作用于外部脚本文件,对于script元素内嵌的脚本不起作用。
在传统模式下,浏览器会按从上到下的方式解析HTML页面的元素,如果页面上出现script元素,浏览器会解析它,完成后再解析HTML页面。
假设有一种极端情况,script元素导入的脚本文件非常耗时,这会导致浏览器无法向下执行,页面将长时间显示一片空白。此时指定async的script元素会启动新线程、异步执行script元素导入的脚本文件,浏览器也会继续向下解析。
<html> <head> <meta name="author" content="Yeeku.H.Lee(CrazyIt.org)" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title> 异步执行JavaScript </title> <script type="text/javascript" src="async.js" async> </script> </head> <body> <div>疯狂HTML 5/CSS 3/JavaScript讲义</div> </body> </html>
async.js脚本文件会比较耗时,在JS循环完成、alert弹出之前,script元素就没有执行完成,那么浏览器就不会向下执行script后面的内容,因此页面一片空白,但如果加了async属性,浏览器就会以异步方式执行JS文件。
acync属性只能作用于外部脚本文件。
noscript元素用来不支持JavaScript或禁用了JavaScript的浏览器显示提示信息。直接在该元素内放提示信息,无需指定任何属性。
<!DOCTYPE html> <html> <head> <meta name="author" content="Yeeku.H.Lee(CrazyIt.org)" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title> noscript </title> <script type="text/javascript" src="defer.js" defer> </script> </head> <body> <noscript> <h1>必须支持JavaScript</h1> <p>必须使用支持JavaScript的浏览器,并打开浏览器的JavaScript功能</p> </noscript> <div id="target"></div> </body> </html>
隐式定义:
<script type="text/javascript"> // 隐式定义变量a a = "Hello JavaScript"; // 使用警告框输出a的值 alert(a); </script>
显式定义:
<script type="text/javascript"> //显式声明变量a var a ; //给变量a赋值,赋值后a的数据类型为布尔型 a = true; //使用警告框输出a的值 alert(a); </script>
JavaScript支持自动类型转换:
<script type="text/javascript"> // 定义字符串变量 var a = "3.145"; // 让字符串变量和数值执行算术运算 var b = a - 2; // 让字符串变量和数值执行运算,到底是算术运算,还是字符串运算呢? var c = a + 2; // 输出b和c的值 alert (b + "\n" + c); </script>
如果需要让"3.145"+2这种表达式的结果为5.145,可以使用强制转换:
<script type="text/javascript"> // 定义值为3.145的字符串变量 var a = "3.145"; // 直接相加,使用自动类型转换。 var b = a + 2; //3.1452 // 使用强制类型转换 var c = parseFloat(a) + 2; alert (b + "\n" + c); </script>
对于3.145这种可以正常转换为数值的字符串,可以成功转换为数值,但对于包含其他字符的字符串,将转换为NaN。
当使用parseInt或parseFloat将各种类型的变量转换为数值类型时,结果如下:
当使用toString将各种类型的值向字符串转换时,结果全是object。
在全局范围内(不管是否使用var)或者是不使用var定义的变量都是全局变量;在函数内使用var定义的变量是局部变量。
<script type="text/javascript"> // 定义全局变量test var test = "全局变量"; // 定义函数myFun function myFun() { // 函数内不使用var定义的age也是全局变量 age = 20; // 函数内使用var定义的age是局部变量 var isMale = true; } myFun(); alert(test + "\n" + age); alert(isMale); </script>
test、age是全局变量,isMale是局部变量;在函数外使用局部变量将会报错。
如果全局变量和局部变量命名相同,则局部变量将会覆盖全局变量。
<script type="text/javascript"> // 定义全局变量test var test = "全局变量"; // 定义函数checkScope function checkScope() { // 定义局部变量 var test = "局部变量"; // 输出局部变量 alert(test); } checkScope(); alert(test); </script>
先输出了局部变量再输出了全局变量,函数中的局部变量覆盖了全局变量,但是在函数外使用test还是输出全局变量,因为局部变量离开函数就失效了。
与Java、C等语言不同的是,JS的变量没有块范围:
<script type="text/javascript"> function test(o) { // 定义变量i,变量i的作用范围是整个函数 var i = 0; if (typeof o == "object") { // 定义变量j,变量j的作用范围是整个函数内,而不仅仅是在if块内。 var j = 5; for(var k = 0; k < 10; k++) { // 因为JavaScript没有代码块范围 // 所以k的作用范围是整个函数内,而不是循环体内 document.write(k); } } // 即使出了循环体,k的值依然存在 alert(k + "\n" + j); } test(document); </script>
如果使用var定义变量,那么程序会强制定义一个新变量;
如果没有使用var定义变量,系统将总是把该变量当成全局变量,不管前面是否曾定义过该全局变量。如果前面已经定义过同名的全局变量,此时就是对已有的全局变量赋值;如果前面没有定义过同名的全局变量,此时就是一个新的全局变量。
全局变量的作用范围对于执行HTML事件处理一样有效:
<html> <head> <meta name="author" content="Yeeku.H.Lee(CrazyIt.org)" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title> 事件处理中的局部变量和全局变量 </title> <script type="text/javascript"> //定义全局变量 var x = "全局变量"; </script> </head> <body> <!-- 在onclick事件中重新定义了x局部变量变量 --> <input type="button" value="局部变量" onclick="var x = '局部变量'; alert('输出x局部变量的值:' + x);"/> <!-- 直接输出全局变量x的值 --> <input type="button" value="全局变量 " onclick="alert('输出x全局变量的值: ' + x);" /> </body> </html>
<script type="text/javascript"> // 定义全局变量 var scope = "全局变量"; function test() { // 变量提升,所以全局变量被局部变量覆盖 // 而此时scope局部变量尚未赋值,故此处输出undefined document.writeln(scope + "<br />"); // 定义scope的局部变量,其作用范围为整个函数内 var scope = "局部变量"; // 再次输出scope的值。 document.writeln(scope + "<br />"); } test(); </script>
变量提升指的是变量声明总是会被解释器提升到函数体的顶部。所以上面先执行定义了局部变量。
变量提升只是提升变量声明部分,不会提升赋值部分。
因此,上面的test函数实际上等同于如下形式:
function test() { var scrope; document.writeln(scope + "<br />"); // 定义scope的局部变量,其作用范围为整个函数内 scope = "局部变量"; // 再次输出scope的值。 document.writeln(scope + "<br />"); }
所以局部变量scope变量从函数开始时就出现了,它覆盖了全局变量scope,但此时还未赋值,所以输出undefined。
JS变量提升甚至不需要定义变量的语句真正执行,只要在函数中包括了定义变量的语句,该变量就会被提升到函数顶部:
<script type="text/javascript"> var x = 100; var y = 200; function foo() { document.writeln(x + "<br>"); document.writeln(y); if (false) { var x = 1; } return; var y = 2; } // function foo() // { // var x, y; // if (false) // { // x = 1; // } // return; // y = 1; // } foo(); </script>
上面第10行位于条件为false的if块内,第13行位于return之后,这两行代码根本不会获得执行的机会,但JavaScript解释器依然会提升这两个变量,因此foo函数等同于如下形式:
function foo() { var x,y; if (false) { x=1; } return; y=2; }
这意味着从foo函数开始,全局变量xy就被局部变量xy覆盖,在foo函数内无法访问全局变量xy。
使用var定义变量可能出现以下问题:
let关键字正是为了解决上述问题出现的。
先看let定义循环变量:
<script type="text/javascript"> for (let i = 0; i < 10 ; i++) { console.log(i); } // 报错:Uncaught ReferenceError: i is not defined console.log("循环之外:" + i); </script>
上面程序在for循环中使用let来定义循环计数器,这样循环计数器i将只在for循环中有效,因此程序在循环体外访问i变量时将会导致报错。
如果将let改为var,那么循环体中定义的i变量的作用域将会扩散到循环体外。
<script type="text/javascript"> let name = 'yeeku'; console.log(name); // 输出yeeku console.log(window.name); // window.name不存在 </script>
上面代码定义了name变量,这个变量不在任何函数内。因此它是一个全局变量。但与使用var定义全局变量不同的是,使用let定义的全局变量不会变成window对象的属性。
使用var定义的变量会提前装载,而使用let定义的变量要等到程序流执行到定义变量的代码时才会装载:
<script type="text/javascript"> var name = 'yeeku' function func() { // 下面name变量不存在,因此程序导致错误 console.log(name); let name = 'fkit'; console.log(name); } func(); </script>
上面程序先定义了一个全局的name变量,然后函数中使用let定义了同名的name变量,此时局部变量会覆盖全局变量。但由于使用let不会提前装载,因此func函数在使用let定义局部变量name之前访问name变量会导致出错。如果将let改为var,则先输出undefined再输出局部变量name的值。
使用conset定义的常量只能在定义时指定初始值且必须指定初始值。使用conset声明常量以后不允许改变常量的值。
conset MAX_AGE = 120; //正确 MAX_AGE = 200; //语法错误
包括所有整型变量,也包括所有的浮点型变量。
<script type="text/javascript"> // 显式声明变量a , b var a , b; // 给a , b使用科学记数法赋值,其值应该为500 a = 5E2; b = 1.23e-3; // 使用警告提示框输出变量a的值 alert(a + "\n" + b); </script>
如果数值只有小数部分,则可以省略0:
<script type="text/javascript"> // 使用隐式变量定义全局变量b b = 3.12e1; // 使用隐式变量定义全局变量c c = 45.0; // 使用隐式变量定义全局变量d d = .34e4; // 使用隐式变量定义全局变量e e = .24e-2; // 使用警告框输出四个全局变量值 alert(b + '---' + c + '---' + d + '---' + e); </script>
JS除了支持十进制外也支持十六进制和八进制。十六进制数以0X或0x开头,9以上的数以a~f表示;八进制数以0开头,只能出现0到7的数值:
<script type="text/javascript"> // 显式定义变量a var a; // 使用16进制数给a赋值 a = 0x13; // 显式定义变量b var b; //使用8进制数给b赋值 b = 014; // 使用警告框输出两个变量的值 alert(a + "---" + b); </script>
当数值变量的值超出了其表数范围时将出现Infinity和-Infinity。
Infinity和-Infinity之间进行算术运行时结果将变成NaN。
Infinity和-Infinity可以执行比较运算,两个Infinity总是相等的。
JS中允许除数为0,整数除0为Infinity,负数除0为-Infinity。除数和被除数都为0结果为NaN。
NaN不会与任何数值变量相等,NaN==NaN也返回false。
可以用isNaN()函数判断某个变量是否为NaN:
<script type="text/javascript"> // 定义x的值为NaN var x = 0 / 0; // 判断两个NaN是否相等 if (x != x) { alert("NaN不等于NaN"); } // 调用isNaN判断变量 if (isNaN(x)) { alert("x是一个NaN"); } </script>
关于浮点型,注意其精度丢失的问题:
<script type="text/javascript"> // 显式定义变量a var a = .3333; // 定义变量b,并为其赋值为a * 5 var b = a * 5; // 使用对话框输出b的值 alert(b); </script> //实际上是1.6665
字符串必须用引号,单双都行。
JS以内建类String表示字符串,有如下简单属性和方法:
<script type="text/javascript"> // 定义字符串变量a var a = "abc中国"; // 获取a的长度 var b = a.length; // 将系列的Unicode值转换成字符串 var c = String.fromCharCode(97,98,99); // 输出a的长度,以及字符串a在索引4处的字符和 // 对应的Unicode值,以及c字符串变量的值 alert(b + "---" + a.charAt(4) + "---" + a.charCodeAt(4) + "---" + c); </script>
charAt():获取字符串特定索引处的字符。
charCodeAt():获取字符串中特定索引处的字符所对应的Unicode值。
length:属性,直接返回字符串长度。
toUpperCase():将字符串的所有字母转换为大写。
toLowerCase():将字符串的所有字母转换为小写。
fromCharCode():静态方法,直接通过String类调用该方法,将一系列Unicode值转换成字符串。
indexOf():返回字符串中特定字符串第一次出现的位置。
lastIndexOf():返回字符串中特定字符串最后一次出现的位置。
substring():返回字符串的某个字串。
slice():返回字符串的某个子串,支持负数参数。
match():使用正则搜索目标子字符串。
search():使用正则搜索目标子字符串。
concat():用于将多个字符串拼接。
split():将某个字符串分割。
replace():将某个字串代替。
indexOf()和lastIndexOf()
<script type="text/javascript"> var a = "hellojavascript"; // 搜索llo子串第一次出现的位置 var b = a.indexOf("llo"); // 跳过左边3个字符,开始搜索llo子串 var c = a.indexOf("llo" , 3); // 搜索a子串最后一次出现的位置 var d = a.lastIndexOf("a"); alert(b + "\n" + c + "\n" + d); </script>
substring()和slice()
<script type="text/javascript"> var s = "abcdefg"; // 取得第1个(包括)到第5个(不包括)的子串 a = s.slice(0 , 4); // 取得第3个(包括)到第5个(不包括)的子串 b = s.slice(2 , 4); // 取得第5个(包括)到最后的子串 没有指定end参数就一直到最后 c = s.slice(4); // 取得第4个(包括)到倒数第1个(不包括)的子串 d = s.slice(3 , -1); // 取得第4个(包括)到倒数第2个(不包括)的子串 e = s.slice(3 , -2); // 取得倒数第3个(包括)到倒数第1个(不包括)的子串 f = s.slice(-3 , -1); alert("a : " + a + "\nb : " + b + "\nc : " + c + "\nd : " + d + "\ne : " + e + "\nf : " + f ); </script>
match()和search()
前者返回匹配的子字符串,后者返回匹配的索引值。match通过使用g标志表示全局匹配,返回所有匹配正则表达式的子串组成的数组。
search()返回值为整型变量,如果搜索到匹配子串则返回子串的索引值,否则返回-1.
<script type="text/javascript"> // 定义字符串s的值 var s = "abfd--abc@d.comcdefg"; // 从s中匹配正则表达式 a = s.search(/[a-z]+@d.[a-zA-Z]{2}m/); 两个/里面是正则表达式。a为6 // 定义字符串变量str var str = "1dfd2dfs3df5"; // 查找字符串中所有单个的数值 var b = str.match(/\d/g); 增加了g选项表示全局匹配,返回1,2,3,5 // 输出a和b的值 alert(a + "\n" + b); </script>
布尔类型的值通常是逻辑运算的结果,或者用于标识对象的某种状态。
例如判断浏览器是否允许使用Cookie:
<script type="text/javascript"> // 如果浏览器支持Cookie if (navigator.cookieEnabled) { alert("浏览器允许使用Cookie"); } // 如果浏览器不支持Cookie else { alert("浏览器禁用Cookie"); } </script>
undefined类型的值只有一个undefined,表示某个变量不存在,或者没有为其分配值,也用于表示对象的属性不存在。null用于表示变量的值为空。
对象是一系列命名变量、函数的集合。其中命名变量的类型既可以是基本数据类型也可以是复合类型。对象中的命名变量叫属性,对象中的函数叫方法。
JS提供了如下常用内置类:
数组是一系列的变量。元素类型可以不同。
<script type="text/javascript"> // 定义一个数组,定义时直接给数组元素赋值。 var a = [3 , 5 , 23]; // 定义一个空数组 var b = []; // 定义一个空数组。 var c = new Array(); // 直接为数组元素赋值 b[0] = 'hello'; // 直接为数组元素赋值 b[1] = 6; // 直接为数组元素赋值 c[5] = true; // 直接为数组元素赋值 c[7] = null; // 输出三个数组值和数组长度 alert(a + "\n" + b + "\n" + c + "\na数组的长度:" + a.length + "\nb数组的长度:" + b.length + "\nc数组的长度:"+ c.length); </script>
JS数组作为栈使用的两个方法:
JS数组作为队列使用的两个方法:
<script type="text/javascript"> // 将数组当成栈使用 var stack = []; // 入栈 stack.push("孙悟空"); stack.push("猪八戒"); stack.push("白骨精"); // 出栈 console.log(stack.pop()); console.log(stack.pop()); // 将数组当成队列使用 var queue = []; // 入队列 queue.unshift("疯狂Java讲义"); queue.unshift("轻量级Java EE企业应用实战"); queue.unshift("疯狂前端开发讲义"); // 出队列 console.log(queue.shift()); console.log(queue.shift()); </script>
Array对象还定义了如下方法:
定义语法如下:
function functionName(param1,param2,...)
定义了一个简单的函数:
<script type="text/javascript"> // 定义一个函数,定义函数时无需声明返回值类型,也无需声明变量类型 function judgeAge(age) { // 如果参数值大于60 if(age > 60) { alert("老人"); } // 如果参数值大于40 else if(age > 40) { alert("中年人"); } // 如果参数值大于15 else if(age > 15) { alert("青年人"); } // 否则 else { alert("儿童"); } } // 调用函数 judgeAge(46); </script>
要先判断传入的参数的数据类型,要加上if(typeof age === "number")。
用=为变量指定变量值。
+、-、*、/、%、++、--、
++:自加。有a++、++a。
<script type="text/javascript"> var a = 5; // 让a先执行算术运算,然后自加 var b = a++ + 6; alert(a + "\n" + b); </script> a为6,b是11。++在右边时,程序先用a变量的值参与运算,此时a为5。
<script type="text/javascript"> var a = 5; //让a先执行自加,执行算术运算。 var b = ++a + 6; alert(a + "\n" + b); </script> a是6,b是12。a先自加再运算。
乘方、开方等:
<script type="text/javascript"> // 定义变量a为3.2 var a = 3.2; // 求a的5次方,并将计算结果赋为b。 var b = Math.pow(a , 5); // 输出b的值 alert(b); // 求a的平方根,并将结果赋给c var c = Math.sqrt(a); // 输出c的值 alert(c); // 计算随机数 var d = Math.random(); // 输出随机数d的值 alert(d); </script>
==:等于,变量值相同则返回true。
===:严格等于,比较两个变量的值相等,数据类型也相同。
两者的区别在于是否支持自动类型转换。
<script type="text/javascript"> // 判断5是否等于"5" alert(5 == "5"); // 判断5是否严格等于"5" alert(5 === "5"); </script>
&&:与。||:或。!:非。
<script type="text/javascript"> // 使用三目运算符 5 > 3 ? alert("5大于3") : alert("5小3") ; </script>
将多个表达式排在一起,整个表达式返回最右边表达式的值。
用于强行指定表达式不会返回值。
<script type="text/javascript"> // 声明变量a,b,c,d。 var a , b , c , d; // 虽然最右边的表达式为56, // 但由于使用了void强制取消返回值,因此a的值为undefined。 a = void(b = 5, c = 7, d = 56); // 输出四个变量的值。 document.write('a = ' + a + ' b = ' + b + ' c = ' + c + ' d = ' + d); </script> 输出:a = undefined b = 5 c = 7 d = 56
typeof用于判断某个变量的数据类型,既可作为函数使用,比如typeof(a),也可作为一个运算符使用,比如typeof a。
instanceof:用于判断某个变量是否为指定类的实例。
<script type="text/javascript"> // 定义一个数组 var a = [4, 5]; // 判断a变量是否为Array的实例 alert(a instanceof Array); // 判断a变量是否为Object的实例 alert(a instanceof Object); </script>
就是使用花括号包含的多个语句,语句块是一个整体的执行流,类似于一个单独的语句。
最简单的就是一个;
空语句主要用于没有循环体的循环:
<script type="text/javascript"> // 声明一个数组 var a = []; // 使用空语句,完成数组的初始化 for (var i = 0 ; i < 10 ; a[i++] = i + 20); // 遍历数组元素 for ( index in a) { document.writeln(a[index] + "<br />"); } </script>
语法如下:throw new Error(errorString);
<script type="text/javascript"> // 对计数器i循环 for (var i = 0 ; i < 10 ; i++) { // 在页面输出i document.writeln(i + '<br />'); // 当i > 4时,抛出用户自定义异常 if (i > 4) throw new Error('用户自定义错误'); } </script>
在浏览器的控制台抛出。
使用catch捕捉异常,JavaScript代码运行中一旦出现异常就会跳转到对应的catch块。
使用try...catch...finally语法,finally可省略,有的话就会执行。
<script type="text/javascript"> try { for (var i = 0 ; i < 10 ; i++) { // 在页面输出i值 document.writeln(i + '<br />'); // 当i大于4时,抛出异常 if (i > 4) throw new Error('用户自定义错误'); } } // 如果try块中的代码出现异常,自动跳转到catch块执行 catch (e) { document.writeln('系统出现异常' + e.message + '<br/>'); } // finally块的代码总可以获得执行的机会 finally { document.writeln('系统的finally块'); } </script>
可以避免重复书写对象。如果with后面只有一行语句则可省略花括号。
document.writeln("A<br>"); document.writeln("B<br>"); document.writeln("C<br>"); 可以使用: with(document) { writeln("A<br>"); writeln("B<br>"); writeln("C<br>"); }
主要有if语句、switch语句。
通常不要省略if、else、else if 后执行块的花括号,但如果执行块只有一行语句时可以省略。
<script type="text/javascript"> // 定义变量a ,并为其赋值 var a = 5; // 如果a>4,执行下面的执行体 if (a > 4) alert('a大于4'); // 否则,执行下面的执行体 else alert('a不大于4'); </script>
<script type="text/javascript"> // 声明变量score,并为其赋值为C var score = 'C'; // 执行swicth分支语句 switch (score) { case 'A': document.writeln("优秀."); break; case 'B': document.writeln("良好."); break; case 'C': document.writeln("中"); break; case 'D': document.writeln("及格"); break; case 'F': document.writeln("不及格"); break; default: document.writeln("成绩输入错误"); } </script>
<script type="text/javascript"> var count = 0; // 只要count < 10,程序将一直执行循环体 while (count < 10) { document.write(count + "<br />"); count++; } document.write("循环结束!"); </script>
do while 和 while的区别在于:while循环是先判断循环条件为真再执行,do while是先执行循环体然后判断循环条件,为真则执行下一次循环。
<script type="text/javascript"> // 定义变量count var count = 0; // 执行do while循环 do { document.write(count +"<br />"); count++; // 当count < 10时执行下一次循环 }while (count < 10); document.write("循环结束!"); </script>
使用for 循环代替while循环
<script type="text/javascript"> for (var count = 0 ; count < 10 ; count++) { document.write(count + "<br />"); } document.write("循环结束!"); </script>
<script type="text/javascript"> // 定义数组 var a = ['hello' , 'javascript' , 'world']; // 遍历数组的每个元素 for (str in a) document.writeln('索引' + str + '的值是:' + a[str] + "<br />" ); </script>
<script type="text/javascript"> // 以i为计数器循环 for (var i = 0 ; i < 5 ; i++) { // 以j为计数器循环 for (var j = 0 ; j < 5 ; j++) { document.writeln('j的值为:' + j); // 当i >= 2时候,使用break中止循环。 if (i >= 2) break; document.writeln('i值为:' + i); document.writeln('<br />'); } } </script>
定义命名函数
<script type="text/javascript"> hello('yeeku'); // 定义函数hello,该函数需要一个参数 function hello(name) { alert(name + ",你好"); } </script>
定义匿名函数
<script type="text/javascript"> var f = function(name) { document.writeln('匿名函数<br />'); document.writeln('你好' + name); }; f('yeeku'); </script>
使用function类匿名函数
<script type="text/javascript"> // 定义匿名函数,并将函数赋给变量f var f = new Function('name' , "document.writeln('Function定义的函数<br />');" + "document.writeln('你好' + name);"); // 通过变量调用匿名函数 f('yeeku'); </script>
<script type="text/javascript"> // 定义求阶乘的函数 function factorial(n) { // 如果n的类型是数值,才执行函数 if (typeof(n) == "number" && n > 0) { // 当n等于1时,直接返回1 if (n == 1) { return 1; } // 当n不等于1时,通过递归返回值。 else { return n * factorial(n - 1); } } // 当参数不是数值时,直接返回 else { alert("参数类型不对!"); } } // 调用阶乘函数 alert(factorial(5)); </script>
在函数里使用var定义的变量称为局部变量,在函数外定义的变量和在函数内不使用var定义的变量称为全局变量。局部变量和全局变量名称相同,局部会覆盖全局。局部变量只能在函数内访问。
局部函数也在函数内定义。
<script type="text/javascript"> // 定义全局函数 function outer() { // 定义第一个局部函数 function inner1() { document.write("局部函数11111<br />"); } // 定义第二个局部函数 function inner2() { document.write("局部函数22222<br />"); } document.write("开始测试局部函数...<br />"); // 在函数中调用第一个局部函数 inner1(); // 在函数中调用第二个局部函数 inner2(); document.write("结束测试局部函数...<br />"); } document.write("调用outer之前...<br />"); // 调用全局函数 outer(); document.write("调用outer之后...<br />"); </script>
函数可作为函数被调用,它本身也是一个对象,是Function类的实例。
<script type="text/javascript"> // 定义一个函数,并将它赋给hello变量 var hello = function(name) { return name + ",您好"; } // 判断函数是否为Function的实例、是否为Object的实例 alert("hello是否为Function对象:" + (hello instanceof Function) + "\nhello是否为Object对象:" + (hello instanceof Object)); alert(hello); //输出源码 </script>
JavaScript的函数也是一个类,定义函数时也得到了一个与函数同名的类,该函数也是该类唯一的构造器。
因此,定义函数后有两种方式调用函数:
<script type="text/javascript"> // 定义一个函数 var test = function(name) { return "你好," + name ; } // 直接调用函数 var rval = test('leegang'); // 将函数作为类的构造器 // 将该函数当成类使用,得到一个对象。 var obj = new test('leegang'); alert(rval + "\n" + obj); </script>
下面定义了一个Person函数,也就是定义了一个类,该Person函数也会作为Person类唯一的构造器。定义Person函数时希望为该函数定义一个方法。
<script type="text/javascript"> // 定义了一个函数,该函数也是一个类 function Person(name , age) { // 将参数name的值赋给name属性 this.name = name; // 将参数age的值赋给age属性 this.age = age; // 为函数分配info方法,使用匿名函数来定义方法 this.info = function() { document.writeln("我的名字是:" + this.name + "<br />"); document.writeln("我的年纪是:" + this.age + "<br />"); }; } // 创建p对象 var p = new Person('yeeku' , 29); // 执行info方法 p.info(); </script>
被this关键字修饰的变量不再是局部变量,它是该函数的实例属性。
JavaScript的函数可以附加到某个对象上作为该对象的方法。如果没有明确指定将函数附加到哪个对象上,该函数将附加到window对象上,作为window对象的方法。
<script type="text/javascript"> // 直接定义一个函数,并未指定该函数属于哪个对象。 // 该对象默认属于window对象 function hello(name) { document.write(name + ", 您好<br />"); } // 以window作为调用者,调用hello函数 window.hello("孙悟空"); // 定义一个对象 var p = { // 定义一个函数,该函数属于p对象。 walk: function() { for(var i = 0 ; i < 2 ; i++) { document.write("慢慢地走..."); } } } p.walk(); </script>
定义函数、变量时尽量不要重名,否则会出现变量值覆盖函数的情形。
由于JavaScript函数不仅仅是一个函数,还是一个类,该函数还是此类唯一的构造器,只要在调用函数时使用new关键字就可以返回一个Object,这个Object不是函数的返回值,而是函数本身产生的对象。因此在JS中定义的变量不仅有局部变量,还有实例属性和类属性两种。根据函数中声明变量的方式,函数中的变量有3种:
<script type="text/javascript"> // 定义函数Person function Person(national, age) { // this修饰的变量为实例属性 this.age = age; // Person修饰的变量为类属性 Person.national = national; // 以var定义的变量为局部变量 var bb = 0; } // 创建Person的第一个对象p1。国籍为中国,年纪为29 var p1 = new Person('中国' , 29); document.writeln("创建第一个Person对象<br />"); // 输出第一个对象p1的年纪和国籍 document.writeln("p1的age属性为" + p1.age + "<br />"); document.writeln("p1的national属性为" + p1.national + "<br />"); document.writeln("通过Person访问静态national属性为" + Person.national + "<br />"); // 输出bb属性 document.writeln("p1的bb属性为" + p1.bb + "<br /><hr />"); // 创建Person的第二个对象p2 var p2 = new Person('美国' , 32); document.writeln("创建两个Person对象之后<br />"); // 再次输出p1的年纪和国籍 document.writeln("p1的age属性为" + p1.age + "<br />"); document.writeln("p1的national属性为" + p1.national + "<br />"); // 输出p2的年纪和国籍 document.writeln("p2的age属性为" + p2.age + "<br />"); document.writeln("p2的national属性为" + p2.national + "<br />"); // 通过类名访问类属性 document.writeln("通过Person访问静态national属性为" + Person.national + "<br />"); </script>
直接调用函数
//调用window对象的alert方法 window.alert("测试代码") //调用p对象的walk方法 p.walk()
以call()方法调用函数
动态地调用函数。
例如需要定义一个形如each(array,fn)的函数,这个函数可以自动迭代处理array数组元素,而fn函数则负责对数组元素进行处理。此时需要在each函数中调用fn函数,但目前fn函数并没确定,所以无法采用直接调用的方式来调用fn,需要用call()方法来调用。
语法格式:函数引用.call(调用者,参数1,参数2...)
<script type="text/javascript"> // 定义一个each函数 var each = function(array , fn) { for(var index in array) { // 以window为调用者来调用fn函数, // index、array[index]是传给fn函数的参数 fn.call(null , index , array[index]); } } // 调用each函数,第一个参数是数组,第二个参数是函数 each([4, 20 , 3] , function(index , ele) { document.write("第" + index + "个元素是:" + ele + "<br />"); }); </script>
以apply()方法调用函数
与call()类似,区别如下:
<script type="text/javascript"> // 定义一个函数 var myfun = function(a , b) { alert("a的值是:" + a + "\nb的值是:" + b); } // 以call()方法动态地调用函数 myfun.call(window , 12 , 23); // 以apply()方法动态地调用函数 myfun.apply(window , [12 , 23]); // ① var example = function(num1 , num2) { // 可以直接用arguments代表调用example函数时传入的所有参数 myfun.apply(this, arguments); } example(20 , 40); // 为apply()动态调用传入数组 myfun.apply(window , [12 , 23]); </script>
<script type="text/javascript"> function Person(name) { this.name = name; // 定义一个info方法 this.info = function() { alert("我的name是:" + this.name); } } var p = new Person("yeeku"); // 调用p对象的info方法 p.info(); var name = "测试名称"; // 以window对象作为调用者来调用p对象的info方法 p.info.call(window); </script>
在Person类中定义了info()方法,但它是独立的,程序只要通过p.info()即可引用这个函数。因此在第16行以call()调用,此时window对象是调用者,因此info()方法中的this代表的就是window对象,访问this.name将返回测试名称。
当使用匿名内嵌函数定义某个类的方法时,该内嵌函数也是独立存在的,可以被分离出来使用,包括成为另一个对象的函数。
在同一个<script.../>元素内,JS允许先调用函数,然后在后面在定义函数,这就是函数提升。
<script type="text/javascript"> // 调用add函数 console.log(add(2, 5)); // 定义add函数(会发生函数提升) function add(a , b) { console.log("执行add函数"); return a + b; } </script>
如果使用程序先定义匿名函数,然后将匿名函数赋值给变量,依然会发生函数提升,但此时只提升被赋值的变量,函数定义本身不会被提升。
<script type="text/javascript"> // 调用add函数 console.log(add(2, 5)); // 定义add函数,此时只提升add变量名,函数定义不会被提升,所以会报错。 var add = function(a , b) { console.log("执行add函数"); return a + b; } </script>
局部函数会被提升到所在函数的顶部。同理,如果先定义匿名函数然后将匿名函数赋值给局部变量,那么只会提升该局部变量的变量定义,不会提升函数定义。
如果匿名函数被赋值的变量没有使用var声明,那么该变量就是全局变量,因此该匿名函数将会变成一个全局函数。
<script type="text/javascript"> function test(){ // 定义add函数,此时只提升add变量名,函数定义不会被提升 add = function(a , b) { console.log("执行add函数"); return a + b; } } test(); // test()函数执行之后,该函数内定义的add变成全局函数 console.log(add(2, 5)); </script>
如果函数名和变量名相同:
相当于其它语言的Lambda表达式或闭包语法。
如果箭头函数的执行体只有一条return语句,则允许省略函数执行体的花括号和return关键字。
如果箭头函数的形参列表只有一个参数则允许省略形参列表的圆括号。
<script type="text/javascript"> var arr = ["yeeku", "fkit", "leegang", "crazyit"]; // 使用函数作为map()方法的参数 var newArr1 = arr.map(function(ele){ return ele.length; }); // 使用箭头函数作为map()方法的参数 var newArr2 = arr.map((ele) => { return ele.length; }); // 由于箭头函数只有一个形参,可以省略形参列表的圆括号 // 箭头函数执行体只有一条return语句,可以省略return关键字 var newArr3 = arr.map(ele => ele.length); console.log(newArr3); // 使用函数作为forEach()方法的参数 arr.forEach(function(ele){ console.log(ele); }); // 使用箭头函数作为map()方法的参数 arr.forEach((ele) => { console.log(ele); }); // 由于箭头函数只有一个形参,可以省略形参列表的圆括号 // 箭头函数执行体只有一条语句,可以省略执行体的花括号 arr.forEach(ele => console.log(ele)); </script>
与普通函数不同,箭头函数没有this关键字,对于普通函数而言,如果程序通过new调用函数创建对象,那么this代表所创建的对象;如果直接调用普通函数,那么该函数中的this代表全局对象(window)。例如,下面示范了普通函数中的this关键字的功能:
<script type="text/javascript"> function Person() { // Person()作为构造器使用时,this代表该构造器创建的对象 this.age = 0; setInterval(function growUp(){ // 对于普通函数来说,直接执行该函数时,this代表全局对象(window) // 因此下面的this不同于Person构造器中的this console.log(this === window); this.age++; }, 1000); } var p = new Person(); setInterval(function(){ console.log(p.age); // 此处访问p对象的age,将总是输出0 }, 1000); </script>