如果有特殊目的使用空语句,最好在代码中添加注释,这样能更清楚的说明这条空语句是有用的 复制代码 代码如下: for (i = 0; i < a.length; a[i++] = 0) /*empty*/;
3.声明语句 var和function都是声明语句,它们声明或定义变量或函数。这些语句定义标识符(变量名和函数名)并给其赋值,这些标识符可以在程序任意地方使用。声明语句本身什么也不做,但它有一个重要意义:通过创建变量和函数,可以更好的组织代码的语义。 接下几节将讲述var语句和function语句,但并不包含变量和函数的全部内容。 i.var var语句用来声明一个或者多个变量,它的语法如下: var name_1[ = value_1][, ..., name_n[ = value_n]] 关键字var之后跟随的是要声明的变量列表,列表中的每一个变量都可以带有初始化表达式,可用于指定它的初始值。例如: 复制代码 代码如下: var i; //一个简单的变量 var j = 0; //一个带有初始值的变量 var p, q; //两个变量 var greet = "hello" + name; //更复杂的初始化表达式 var x = 2.34,y = Math.cos(0.75),r, theta; //很多变量 var x = 2,y = x * x; //第二个变量使用了第一个变量 var x = 2, f = function(x) {return x * x}, //每个变量都独占一行 y = f(x)
如果var语句出现在函数体内,那么定义的是一个局部变量,其作用域就是这个函数。如果在顶层代码中使用var语句,那么它声明的是全局变量,在整个javascript中,都是可见的。在第三章10节提到:全局变量是全局对象的属性,然后和其它全局对象属性不同的是,var声明的变量是无法通过delete删除的。 如果var语句中的变量没有指定初始化表达式,那么这个变量的值初始为undefined。所以,在声明语句之前的变量值就是undefined。 需要注意的是,var语句同样可以作为for循环或者for/in循环的组成部分。(在循环之前声明的变量声明一样,这里声明变量也会"提前"),例如: 复制代码 代码如下: for (var i = 0; i < 10; i++) console.log(i); for (var i = 0, j = 10; i < 10; i++, j--) console.log(i * j); for (var i in o)console.log(i);
注意,多次声明同一变量是无所谓的。 ii.function 关键字function用来声明函数的,我们已经学过函数表达式(4.3).函数定义可以写成语句的形式。例如:下面示例代码中的两种定义写法: 复制代码 代码如下: var f = function f(x) {return x + 1;} //将表达式赋值给一个变量 function f(x){return x + 1;} //含有变量名的语句
函数声明的语法如下: 复制代码 代码如下: function funcname([arg1[, arg2[..., argn]]]) { statements }
funcname是要声明的函数的名称标识符。函数名之后是参数列表,参数之间使用逗号隔开。当调用函数的时候,这些标识符则指代传入函数的实参。 函数体是由javascript语句组成的,语句数量不限,且用花括号括起来。在定义函数时,并不执行函数体内的语句,它和调用函数时待执行的新函数对象相关联。注意,function函数语句里的花括号是必须的,这和while循环和其它一些语句锁使用的语句块是不同的,即使函数体只有一条语句,仍然需要花括号将其括起来。 复制代码 代码如下: function hyteus(x, y) { return Math.sqrt(x * x + y * y); } hyteus(1, 2) //=>2.23606797749979 function facial(n) { //一个递归函数 if (n <= 1) return 1; return n * facial(n - 1); } facial(11) //=>39916800
在执行while语句之前,javascript解释器首先计算expression的值,如果它的值是假值,那么程序将跳过循环体中的逻辑statement转而执行程序中的下一条语句。如果它的值是真值,则执行循环体statement内的逻辑,然后再计算表达式expression的值,种循环会一直持续下去,知道expression的值为假值为止。换一种说法 就是表达式为expression是真值的时候则循环执行statement,注意,使用while(true)则会创建一个死循环。 通常来说,我们不想让javascript反复执行同一操作。在几乎每一次循环中,都会有一个或多个变量随着循环而迭代改变。正是由于改变了变量这些变量,因此每次循环执行的statement的操作也不尽相同,而且,如果改变变量在expression中用到,那么每次循环表达式的值也不同。这一点非常重要,负责初始值为真值的表达式永远是真值,循环也不会结束,下面的这个示例所示while循环输出0-9值。 复制代码 代码如下: var count = 0; while (count < 10) { console.log(count); count++; }
可以发现,在这个例子中,变量count的初始值为0,在循环的过程中,它的值每次都递增1,当循环执行了十次。表达式的值就编程了false,这时while就会结束,javascript解释器将执行程序下一条语句。大多循环都有一个像count这样的计数器变量。尽管计数器常用i j k这样的变量名,但如果想让代码的可读性更强,就应当使用更具体的语法名。 ii.do/while do/while循环和while循环非常相似,只不过它是在循环的尾部而不是顶部检测循环表达式,这就意味这循环体至少执行一次。do/while循环的语法如下: 复制代码 代码如下: do statement while(expression);
do/while循环并不像while循环那么常用。这是因为在实践中想要循环至少执行一次的情况并不常见。下面是一个do/while循环的例子 复制代码 代码如下: function printArray(a) { var len = a.length, i = 0; if (len == 0) console.log("空数组"); else do { console.log(a[i]); } while (++i < len); } printArray([1,5,2,6])
在do/while循环和普通while循环之间有两点语法方面的不同之处。首先,do循环要求必须使用关键字do来标识循环的开始,用while变标识循环的结尾并进入循环条件判断;其次,和while循环不同,do循环使用分号结尾的。如果while的循环体使用花括号括起来,则while循环也不使用分号结尾。 iii.for for语句提供了一种比while更方便的循环语句控制结构。for语句对常用的循环模式做了一些简化。大部分的循环都具有特定的计数器变量。在循环开始之前要初始化这个变量,然后在每次循环之前检查下它的值。最后,计数器变量做自增操作,否则就在循环结束后、下一次判断前做修改。在这类循环中,计数器的三个关键操作是初始化、检测和更新。for语句就将这三部操作明确声明为循环语法的一部分,各自使用一个表达式来表示。for语句的语法如下: 复制代码 代码如下: for (initialize; test; increment) statement
当然,有些循环更加复杂,而且循环中一次迭代多个变量。在javascript,这种情况必须用到逗号运算符,它将初始化表达式和自增表达式合并入一个表达式中以用于for循环。 复制代码 代码如下: var i, j; for (i = 0, j = 10; i < 10; i++, j--) console.log(i * j);
到目前为止,在示例代码中的循环变量都是数字。当然是数字是最常用的,但不是必须的。下面这段代码就使用for循环来遍历表数据结果,并返回链表中最后一个对象(也就是第一个不包含next属性的对象) 复制代码 代码如下: function tail(o) { //返回链表的最后一个节点对象 for (; o.next; o = o.next) /*empty*/ //根据判断o.next是不是真值来执行遍历 return o; }
需要注意的是,这段代码不包含initialize表达式,for循环中的那三个表达式中的人和一个都可以忽略,但两个分号必不可少。如果省略test表达式,那么将是一个死循环。同样和while(ture)类型,死循环的令一种写法是for(;;)。 iiii.for/in for/in语句使用for关键字,但它和常规的for循环是不同的一类循环。for/in循环的语法如下 复制代码 代码如下: for (variable in object) statement
variable通常是一个变量名,也可以是一个可以产生左值的表达式或者一个通过var语句声明的变量。总之是一个适用于赋值表达式左侧的值。object是一个表达式,这个表达式的计算结果是一个对象。同样,statement是一个语句或语句块,它构成了循环的主体。 使用for循环来遍历数组元素是非常简单的 复制代码 代码如下: var a = [1, 3, 5, "44"]; for (var i = 0; i < a.length; i++) //i代表了数组元素的索引 console.log(a[i]) //输出每个数组的元素
而for/in循环则是用来方便的遍历对象成员属性 复制代码 代码如下: for (var p in o) //将属性的名字赋值给变量p console.log(o[p]); //输出每一个属性的值
在执行 for/in语句的过程中,javascript解释器首先计算object表达式。如果表达式为null或undefined,javascript解释器将跳过循环并执行后续的代码。如果表达式等于一个原始值,这个原始值将会转换为与之对于的包装对象(wapper object)(3.6节)。否则,expression本身已经是对象了。javascript会依次枚举对象的属性来执行循环。然而在每次循环之前,javascript都会计算variable表达式的值,并将属性名(一个字符串)赋值给它。 需要注意的是,只要for/in循环中,varibale的值可以当做赋值表达式的左值,它可以是任意表达式。每次循环都会计算这个表达式,也就是说每次循环它计算的值可能不同。例如,可以使用下面的这段代码将所有对象属性复制到一个数组中: 复制代码 代码如下: var o = {x: 1,y: 2,z: 3}; var a = [],i = 0; for (a[i++] in o) /*empty*/; document.write(a)//=> x,y,z
javascript数组只不过是一种特殊的对象,因此,for/in循环可以像枚举对象属性一样枚举数据索引。例如在上面的代码之后添加这段代码,就可以枚举数据索引0,1,2: 复制代码 代码如下: var o = {x: 1,y: 2,z: 3}; var a = [],i = 0; for (a[i++] in o) /*empty*/; document.write(a)//=> x,y,z将对象属性复制到一个数组中 for(i in a) document.write(i) //=>枚举数据索引 0 1 2
这里做标签的indentifier必须是一个合法的javascript标识符,而不能是一个保留字。标签的命名空间和变量或函数的命名空间是不同的,因此可以使用同一个标识符作为语句标签和作为变量名或函数名。语句标签只在它所起作用的语句(当然可以在它的子句)内是有定义的。一个语句标签不能和它内部的语句标签重名,但在两个代码不相互嵌套的情况下是可以出现同名语句标签的。带有标签的语句还可以带有标签,也就是说,任何语句可以有很多个标签。 ii.break 单独使用break语句的作用是立即退出最内存的循环或switch语句。它的语法如下: break; 由于它能够使循环和switch语句退出,因此这种形式的break只能出现在这类语句中才是合法的。 我们在switch语句的例子中已经见到果break语句。在循环中,无论出于什么原因,只要不想继续执行整个循环,就可以用break提前退出。当循环终止条件非常复杂时,要函数体内使用break语句实现这样些条件判断的做法要比直接在循环表达式中写出这个复杂的终止条件做法简单的多。 下面的例子中循环遍历整个数组元素来查找某个特定的值,当整个数组遍历完成后正常退出循环,如果找到 了需要查找的数组元素,则使用break语句退出循环: 复制代码 代码如下: for (var i = 0; i < a.length; i++) { if (a[i] == target) break; }
javascript中同样允许break关键字后跟随一个语句标签,(只有标识符,没有冒号) break labelname; 当break和标签一块使用时,程序将跳转到这个标签所识别的语句块的结束,或者直接终止这个闭合语句块的执行。当没有任何闭合语句块指定break所用的标签,这时会产生一个语法错误。当使用这种形式的break语句时,带标签的语句不应该是循环或者switch语句,因为break语句可以“跳出”任何闭合的语句块。这里的语句可以是由花括号组起来的一组语句,使用同一个标签来识别一组语句。 break关键字和labelname之间不能换行。因为javascript可以给语句自动补全省略掉的分号,如果break关键字和标签之间有换行,javascript解释器会认为你在使用break不带标签的最简形式,因此会在break后补充分号. 当你希望通过break来跳出非就近的循环体或者switch语句时,就会用到带标签的break语句。下面是示例代码: 复制代码 代码如下: var matrix = getData(); //从某处获得一个二维数组 //将矩阵中所有元素进行求和 var sum = 0, success = false; //从签名处开始,以便在报错时推出程序。 compure_sum: if (matrix) { for (var x = 0; x < matrix.length; x++) { var row = matrix[x]; if (!row) break compure_sum; for (var y = 0; y < row.length; y++) { var cell = row[y]; if (isNaN(cell)) break compure_sum; sum += cell; } } success = true; } //break语句跳转至此 //如果success =false条件到达这里,说明我们给出的矩阵中有错误 //否则对矩阵中所有的元素进行求和
最后,需要注意的是,不管break语句带不带标签,它的控制权都无法越过函数的边界。比如:对于一条带标签的函数定义语句来说,不能通过函数内部通过这个标签来跳转到函数外部. iii.continue语句 continue语句和break语句非常类似,但它不退出循环,而是转而执行下一次循环。continue语句的语法和break的语句语法一样简单 continue; continue语句会也会带有标签 continue lebname; 不管continue语句带不带标签,它只能在循环体使用,在其它地方使用将会 报语法错误。 当执行到continue语句的时候,当前的循环逻辑就终止了,随即执行下一次循环,在不同类型的循环中,continue的行为也有区别 1. 在while循环中,在循环开始处指定expression会重复检测,如果检测结果为true,循环体会从头执行。 2. 在do/while循环中,程序的执行至今跳转到循环的结尾处,这时会重新判断循环条件,之后才会继续下一次循环。 3. 在for循环中,首先会计算自增表达式,然后再检测test表达式,用以判断是否执行循环体。 4. 在for/in循环中,循环开始遍历下一个属性名,这个属性名赋给了指定的变量。 需要注意continue语句在while和for循环中的区别,while循环直接进入下一轮的循环条件判断,但for循环首先计算器increment表达式,然后判断循环条件。之前的章节讨论了和while循环“等价”的for循环行为。但由于continue在这两种循环中行为表现不同,因此使用while循环不可能完美的模拟等价的for循环。 下面这段代码展示了不带标签的continue语句,产生一个错误的时候跳过当前循环的后续逻辑 复制代码 代码如下: for (i = 0; i < data.length; i++) { if (!data[i]) continue; //不能处理undefined数据 total += data[i]; }
和break语句类似,带标签的continue语句可以用在嵌套的循环中,用以跳出层次嵌套的循环体逻辑。同样和break语句类似,在continue语句和labname之间不能有换行。 iiii.return 回想一下,函数调用的一种表达式,而且所有的表达式都有值。函数中的return语句即是指函数调用后的返回值。这里是return语句的语法: return expression; return语句只能在函数体内出现,如果不是的话会报语法错误。当执行到return语句的时候,函数终止执行,并返回expression的值给调用程序。例如: 复制代码 代码如下: function square(x) {return x * x} //一个包含return的语句函数 square(4) //执行为16
如果没有return语句,则函数调用仅依次执行函数体内的每一条语句直到函数结束,最后返回调用程序。这种情况下,调用表达式的结果是undefined。return语句经常作为函数内最后的一条语句出现,但并不是说一定一定要放在函数的最后,即使在执行return语句的时候还有很多代码没有执行到,这时候函数也还返回调用程序。 return语句可以单独使用而不必带有expression,这样的话函数也会想调用程序返回undefined.例如: 复制代码 代码如下: //如果参数是null或者undefined则立即返回 if (!o) return; //其它逻辑
由于javascript可以自动插入分号,因此,return关键字和它后面的表达式之间不能有换行。 iiiii.throw语句 所谓异常(excepion)是当发生了某种异常情况或错误时产生的一个信号。抛出异常,就是用信号通知发生了错误或异常状况。捕获异常是指处理这个信号,抛出异常,就是用信号通知发生了错误或异常状况。捕获异常是指处理这个信号,即采取必要的手段从异常中汇丰。在javascript中,当产生运行时错误或者程序使用throw语句时就会显式的抛出异常。使用try/catch/finally语句可以捕获异常,下一节会对它作详细介绍。 throw语句的语法如下: throw expression expression的值可以是任意类型的。可以抛出一个代表错误码的数组,或者包含可错误消息的字符串。当javascript解释器抛出异常的时候,通常采用Eeeor类型或其子类型,当然也可以使用它们。一个error对象有一个那么熟悉表示错误类型,一个message属性用来传递构造函数的字符串(参照第三部分的Error类),在下面的例子中,当使用非法参数调用函数时就抛出一个Error对象: 复制代码 代码如下: function fa(x) { //如果输入的参数是非法的,则抛出一个异常 if (x < 0) throw new Error("x不能是负数。"); //否则计算出一个值,正常地返回它 for (var f = 1; x > 1; f *= x, x--) /*empty*/; return f; }