Cocos Creator_JavaScript权威指南(第六版)_第5章_语句

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39710961/article/details/81097401
语句 语法 用途
break break[label]; 退出最内层循环或者退出switch语句,又或者退出label指定的语句
case case expression: 在switch语句中标记一条语句
continue continue [label]; 重新开始最内层的循环或重新开始label指定的循环
debugger debugger; 断点器调试
default default; 在switch中标记默认的语句
do/while do statement while(expression); while循环的一种替代形式
empty ; 什么都不做
for for(init;test;incr)statement) 一种简写的循环
for/in for(var in object)statement 遍历一个对象的属性
function function name([param[],…]){body} 声明一个函数
if/else if(expr) statement1 [else statement2] 执行statement1或者statement2
label label:statement 给statement指定一个名字:label
return return[expression]; 给函数返回一个值
swith swith(expression){statement} 用case或者”default:”语句标记的多分支语句
throw throw expression; 抛出异常
try try {statements} [catch {handler statements}] [finally {cleanup statements}] 捕获异常
use strict “use strict” 对脚本和函数应用严格模式
var var name =[=expr][,…]; 声明并初始化一个或多个变量
while while(expression) statement 基本的循环结构
with with(object) statement 扩展作用域链(不赞成使用)

5 语句
表达式在JavaScript中是短句,那么语句就是JavaScript整句或命令。正如英文是用句号做结尾来分隔语句,JavaScript语句是以分号结束(2.5)。表示式计算出一个值,但语句用来执行以使某件事发生。
“时某件事发生”的一个方法是计算带有副作用的表达式。诸如赋值和函数调用这些有副作用的表达式,是可以作为单独的语句的,这种把表达式当做语句的用法也称做表达式语句。类似的语句还有声明语句,声明语句用来声明新变量或定义新函数。
JavaScript程序无非就是一系列可执行语句的集合。默认情况下,JavaScript解释器依照语句的编写顺序依次执行。另一种“使某件事发生”的方法是改变语句的默认执行顺序。JavaScript中有很多语句和控制结构来改变语句的默认执行顺序:
·条件语句,JavaScript解释器可以根据一个表达式的值来判断是执行还是跳过这些语句,如if语句和switch语句。
·循环语句,可以重复执行语句,如while和for语句。
·跳转语句,可以让解释器跳转至程序的其他部分继续执行,如break、return和throw语句。
一个JavaScript程序无非是一个以分号分隔的语句集合,所以一旦掌握了JavaScript语句,就可以开始编写JavaScript程序了。

5.1 表达式语句
具有副作用的表达式是JavaScript中最简单的语句(5.7.3介绍了一种重要的无副作用的表达式语句)。这类语句已经在第四章讲述了。赋值语句是一类比较重要的表达式语句,例如:

greeting = “Hello”+ name;
i*=3;

递增运算符(++)和递减运算符(–)和赋值语句有关。它们的作用是改变一个变量的值,就像执行一条赋值语句一样:counter++;
delete运算符的重要作用是删除一个对象的属性,所有,他一般作为语句使用,而不是作为复杂表达式的一部分:delete o.x;
函数调用是表达式语句的另一个大类,例如:

alert(greeting);
window.close();

虽然这些客户端函数调用都是表达式,但他们都对Web浏览器造成了一些影响,所以我们认为它们也是语句。调用一个没有任何副作用的函数是没有意义的,除非他是复杂表达式或赋值语句的一部分,例如,不可能计算了一个余弦值随即把它丢弃:Math.cos(x);
相反,得出了余弦值就得把它赋值给一个变量,以便将来才能使用这个值:cx = Math.cos(x);

5.2 复合语句和空语句
可以用逗号运算符将几个表达式连接在一起,形成一条复合语句。只须应花括号将多条语句扩起来即可。因此,下面几行代码就可以当成一条单独的语句,使用在JavaScript中任何希望使用一条语句的地方:

{
 x = Math.PI;
 cx =Math.cos(x);
 console.log("cos(π) = "+cs);      
}

关于语句块有几点需要注意:第一,语句块的结尾不需要分号。快中的原始语句必须以分号结束,但语句块不需要。第二,语句块中的行都有缩进,这不是必需的,但整齐的缩进能让代码可读性,跟容易理解。最后需要注意,JavaScript中没有块级作用域,在语句块中声明的变量并不是语句块私有的(3.10.1)。
将多条语句合并成一个大语句块的做法在JavaScript编程中非常常见。类似表达式通常包含子表达式一样,很多JavaScript语句包含其他子语句。从形式上讲,JavaScript语法通常允许一个语句块只包含一条子语句。例如,while循环的循环体就可以只包含一条语句。使用语句块,可以将任意数量的语句放到这个快中,这个语句块可以作为一条运距使用。
在JavaScript或只能怪,当希望多条语句被当做一条语句:
使用时,使用复合句来代替。空语句则恰好相反,它允许包含0条语句的语句。空语句如下所示:
JavaScript解释器执行空语句时他显然不会执行任何动作。但实践证明你,当创建一个具有空循环体的循环时,空语句有时是很有用的。例如下面的for循环(5.3.5):for(i=0 ;i<a.length;a[i++] = 0);
在这个循环中,所有的操作都在表达式a[i++]=0中完成,这里并不需要任何循环体。然而JavaScript需要循环体中至少包含一条语句,因此,这里只使用了一个单独的分号来表示一条空语句。
注意,在for循环、while循环或if语句的右圆括号的分号很不起眼,这很可能造成一些知名bug,而这些bug很难定位到。例如,下面的代码的执行结果可能就不是程序作者想要的效果:

if((a==0)||(b==0));
o = null

如果有特殊的目的需要使用空语句,最好的代码中添加注释,这样可以更清楚地说明这条空语句是有用的,例如:for(i=0;i<a.length;a[i++] = 0)/*empty*/;

5.3 声明语句
var和function都是声明语句,他们声明或定义变量或函数。这些语句定义标识符(变量名和函数名)并给其赋值,这些标识符可以在程序中任意地方使用。声明语句本身什么也不做,但它有一个重要的意义,通过创建变量和函数,可以更好地组织代码的语义。

5.3.1 var
var语句用来声明一个或对个变量,它的语法如下:

var name_1 [= value_1][,.....,name_n[= value_n]]

关键字var之后跟随的是要声明的变量列表,列表中的每一个变量都可以带有初始化表达式,用于指定它的初始值,例如:

var i; //一个简单的变量
var j =0//一个带有初始值的变量
var p,q; //俩个变量
var greeting = “Hello” + name;//更复杂的初始化表达式
var x= 2.34,y =Math.cos(0.75),r,theta //很多变量
var x= 2, y = x*x//第二个变量使用了第一个变量
var x = 2,f = functionx){return x
*x}, y = f(x);// 更多变量,每一个变量都独占一行。

如果var语句出现在函数体内,那么它定义的一个局部变量,其作用域就是这个函数。如果在顶层代码中使用var语句,他声明的全局变量,在整个JavaScript程序中都是可见的。这个呢如在3.10.2节提到的,全局变量是全局对象的属性。然而和其他全局对象属性不同的天赋是:
PS:var声明的变量是无法通过delete删除的。
如果var语句中的变量没有指定初始化表达式,那么这个变量的值初始为undefined。3.10.1节已经提到,变量在声明它们的脚本或函数中都是有定义的,变量声明语句会被“提前”至脚本或者函数的顶部。但是初始化的操作则还在原来var语句的位置执行,在声明语句之前变量的值是Undefined。
PS:var语句同样可以作为for循环或者for/in选循环的组成部分(和在循环之外声明的变量声明一样,这里声明的变量也会“提前”)。(3.9)

for(var i = 0i<10; i++) console.log   (i);
for(var i = 0,j = 10i<10; i++,j--) console.log    (i*j);
for(var i  in o) console.log    (i);

多次声明同一个变量是无所谓的。

5.3.2 function
关键字function用来顶一个函数。在4.3节中我们已经见过函数定义表达式。函数定义也可以写成语句的形式。例如,下面示例代码中的两种定义写法:

var f=() = functionx){return x +1;}
function fx) {return x+=1;}

函数声明语句的语法如下,

  gunction funcaname([arg1[,arg2[argn]]])[]{
  stabenebts.}

funcname 是要声明的函数的名称和标识符。函数名之后的圆括号中是参数列表,参数之间使用逗号分隔。当滴啊用函数时,这些标识符则指代传入函数的实参。
函数体是有发JavaScript语句组成的,语句的数量不限,且用花括号括起来。在定义函数是,并不执行函数体内的语句。它和调用函数时待执行的新函数对象相关连。注意,function语句里的花括号是必须的,这和while循环和其他一些语句所使用的语句块是不同的,即便函数体只包含一条语句,仍然必须使用花括号将其括起来。

function hypotenusexy){
       return Math.sqrt(x*x,y*y);  
}
function factorialn){
       if(n<=1return 1;
       return n*factorial(n-1);
}

函数声明语句通常出现在JavaScript代码的最顶层,也可以嵌套在其他函数体内。但在嵌套时,函数声明只能出现在所嵌套函数的顶部。也就是说,函数定义不能出现在if语句、while循环或其他任何语句中,正是由于函数声明位置的这种限制,ECMAScript标准规范并没有将函数声明归类为真正的语句。有一些JavaScript实现的确允许在出现语句的地方都可以进行函数声明,但是不同的实现在细节处理方式上有很大差别,因此将函数声明放在其他的语句内的做法并不具备可移植性。
尽管函数声明语句和函数定义表达式包含相同的函数,但二者仍然不同。两种方式都创建了新的函数对象,但函数声明语句中的函数名是一个变量名,变量指向函数对象。和通过var声明变量一样,函数定义语句中的函数被显式地“提前”到了脚本或函数的顶部。因此它们在整个脚本和函数内都是可见的。使用var的话,只有变量声明提前了–变量的初始化代码仍然在原来的位置。然而使用函数声明语句的话,函数名称和函数体均提前:脚本中的所有函数和函数中所有嵌套的函数都会在当前上下文中其他代码之前声明。也就是说,可以在声明一个JavaScript函数之前调用它。
和var语句一样,函数声明语句创建的变量也是无法删除的。但是这些变量不是只读的,变量值可以重写。

5.4 条件语句
条件语句是通过判断指定表达式的值来决定执行还是跳过某些语句。这些语句是代码的“决策点”,有时称为“分支”。如果说JavaScript解释器是按照代码的“路径”执行的,条件语句就是这条路径上的分叉点,程序执行到这里时必须选择其中一条路径继续执行。

5.4.1 if
if语句是一种基本的控制语句,他让JavaScript程序可以选择执行路径,更准确地说,就是有条件地执行语句,这种语句有两种形式,第一种是:

ifexpression)
    statement

在这种形式中,需要计算expression的值,如果计算结果是真值,那么就执行statement。
需要注意的是,if语句中括住expression的圆括号在语法上是必需的。
和大多数编程语言一样,JavaScript中的if、else匹配规则是,else总是和就近的if语句匹配。因此为了使if语句可读性更强、更易理解、更方便维护和调试,应当适当地使用花括号。

5.4.2 else if
else if语句并不是真正的JavaScript语句,他只不过是多条if/else语句连在一起时的一种惯用写法。

5.4.3 switch
if语句在程序执行过程中创建一条分支,并且可以使用else if来处理多条分支。然而,当所有的分支都依赖于同一个表达式的值时,else if并不是最佳解决方案。在这种情况下,重复计算多条if语句中的条件表达式是非常浪费的做法。
switch语句正适合处理这种情况。关键字switch之后紧跟着圆括号括起来的一个表达式,随后是一对花括号括起来的代码块:

switch(expropriate)
{
  statements
}
switch(expropriate)
{
  case1//执行代码块 1
  breakcase2//执行代码块 2
  breakdefault//
  break//这里停止执行switch语句
}

需要注意的是,在上面的代码中,在每一个case语句块的结尾处都使用了关键字break。

    Testname: function (x) {
        switch (typeof(x)) {
            case "number":
               return x.toString(16);              
            case "string":
               return '"'+x+'"'; 
            default:
               return String(x);            
        }
    },

在case关键字后跟随的是数字和字符串直接量,在实际中这是switch语句最常见的用法,但是ECMAScript标准允许每个case关键字跟随任意的表达式。
由于每次执行switch语句的时候,并不是所有的case表达式都能执行到,因此,应当避免使用带有副作用的case表达式,比如函数调用表达式和赋值表达式。最安全的做法就是在case表达式中使用常量表达式。

5.5 循环
循环语句就是程序路径的一个回路,可以让一部分代码重复执行。JavaScript中有4种循环语句:while、do/while、for和for/in。最常用的循环就是对数组元素的遍历,(7.6)节详细讨论这种循环和使用数组类定义的特殊循环方法。

5.5.1 while
if语句是一种基本的控制语句,用来选择执行程序的分支语句。和if一样,while语句也是一个基本循环语句,语法如下:
while(expression)
statement
上述语法的意义是,当表达式expression是真值时则循环执行statement,注意,使用while(true)则会创建一个死循环。
大多数循环都需要一个count计数器变量,用于限制循环的条件和循环的次数。为了让代码可读性更强,应当使用更具语义的变量名。

5.5.2 do/while
do/while循环和while循环非常相似,只不过它是在循环的尾部而不是顶部检测循环表达式,这就意味着循环体至少会执行一次。语法如下:

do
       statement
whileexpression);

PS:do/while循环和普通的while循环之间有两点语法方面的不同之处。首先,do循环要求必须使用关键字do来标识循环的开始,用while来标识循环的结尾并进入循环条件判断;其次,和while循环不同,do循环是用分号结尾的。如果while的循环体使用花括号扩区来的话,则while循环也不用使用分号做结尾的。如果while的循环体使用花括号括起来的话,则while循环也不用使用分号做结尾。

5.5.3 for
for语句提供了一种比while语句方便的循环控制结构。for语句对常用的循环模式做了一些简化。大部分的循环都具有特定的计数器变量。在循环开始之前要初始化这个变量,然后在每次循环执行之前都检测一下他的值。最后,计数器变量做自增操作,否则就在循环结束后、下一次判断循环条件前修改。这一类循循环中,计数器的三个关键操作是初始化、检测和更新。for语句就将这三步操作明确声明为循环语法的一部分,各自使用一个表达式来表示。for语句就将这三步操作明确声明为循环语法的一部分,各自使用一个表达式来表示。for语句的语法如下:

for(initiolizz;test;increment)
     statement

5.5.4 for/in
for/in语句也使用for关键字啊。但它是和常规的for循环完全不同的一类循环。

for(variable in object)
       statement

variable通常是一个变量名,也可以是一个可以产生左值的表达式或者一个通过var语句声明的变量,总之必须是一个适用于赋值表达式左侧的值。object是一个表达式,这个表达式的计算结果是一个对象。同样,statement是一个语句或语句块,它构成了循环的主体。
使用for循环来遍历数组元素是非常简单的:
for (var i = 0; i < a.length; i++) {//i代表了数组元素的索引
console.log(a[i]);//输出数组中的每个元素
}
而for/in循环则是用来更方便地遍历对象属性成员:

forvar p in o)//将属性名字赋值给变量p
    console.log(o[p])//输出每一个属性的值

在执行for/in语句的过程中,JavaScript解释器首先计算object表达式。如果表达为null或者undefined,JavaScript解释器将会跳过循环并执行后续的代码。如果表达式等于一个原始值,这个原始值将会转换为与之对应的包装对象(3.6)。否则,expression本身已经是对象了。JavaScript会依次枚举对象的属性来执行循环。然而在每次循环之前,JavaScript都会先计算variable表达式的值,并将属性名(一个字符串)赋值给它。
需要注意的是,只要for/in循环中variable的值可以当做赋值表达式的左值,它可以是任意表达式。每次循环都会计算这个表达式,也就是说每次循环它计算的值有可能不同。例如,可以使用下面这段代码将所有对象属性复制至一个数组中:

        var o = {x:1,y:2,Z:3};
        var a =[], i = 0;
        for (a[i++] in o) {
            console.log(a);          
        }

在这种情况下,ECMAScript3的实现可能会抛出一个类型错误异常。
JavaScript数组不过是一种特殊的对象,因此,for/in循环可以向枚举对象属性一样枚举数组索引。例如,在上面的代码之后加上这段代码就可以枚举数组的索引0、1、2:

        for(i in a)
        console.log(i);

其实,for/in循环并不会遍历对象的所有属性,只有“可枚举”的属性才会遍历到(6.7)。有JavaScript语言核心所定义的内置方法就不是“可枚举的”。比如,所有的对象都有方法toString(),但for/in循环并不枚举toString这个属性。除了内置方法之外,还有很多内置对象的属性也是“不可枚举的”。而代码中定义的所有属性和方法都是可枚举的。对象可以继承其他对象的属性,那些继承的自定义属性(6.2.2)也可以使用for/in枚举出来。
如果for/in的循环体删除了还未枚举的属性,那么这个属性将不会再枚举到。如果循环体定义了对象的新属性,这些属性通常也不会枚举到(然而,JavaScript的有些实现是可以枚举那些在循环体中增加的继承属性的)。

属性枚举的顺序
主流浏览器厂商的JavaScript实现是按照属性定义的先后顺序来枚举简单对象的属性,先定义的属性先枚举。如果使用对象直接量的形式创建对象,则按照直接量中属性的出现顺序枚举。有一些网站和JavaScript库是依赖于这种枚举顺序的,浏览器厂商不大可能会修改这个顺序。
上一段讨论了JavaScript解释器枚举“简单”对象一种交互的属性枚举顺序。在下列情况下,枚举的顺序取决于具体的实现(并且使非交互的):

·对象继承了可枚举属性;
·对象具有整数数组索引的属性;
·使用delete删除了对象已有的属性;
·使用Object.defineProperty()(6.7)或者类似的方法改变了对象的属性。

除了所有非继承的“自有”属性以外的继承属性(6.2.2)都往往(但并不是所有的JavaScript实现都是如此)都是可枚举的,而且可以按照他们定义的顺序进行枚举。如果对象属性继承自多个“原型”(6.1.3),也就是说它的原型链上有多个对象,那么链上面的每一个原型对象的属性也是依照特定顺序执行的。JavaScript的一些(但不是全部)实现依照数字顺序来枚举数组属性,而不是某种特定的顺序。但当数组元素的索引是非数字或数组是稀疏数组(数组索引是不连续的)时它们则按照特定顺序枚举。

5.6 跳转
JavaScript中另一类语句是跳转语句。从名称就可以看出,它使得JavaScript的执行可以从一个位置跳转到另一个位置。break语句是跳转到循环或者其他语句的结束。continue语句是终止本次循环的执行并开始下一次循环的执行。JavaScript中的语句可以命名或带有标签,break和continue可以标识目标循环或者其他语句标签。
return语句让解释器跳出函数体的执行,并提供本次调用的返回值。throw语句触发或者“抛出”一个异常,它是与try/catch/finnally语句一同使用的,这些语句指定了处理异常的代码逻辑。这是一种复杂的跳转语句,当抛出一个异常的时候,程序将跳转至最近的闭合异常处理程序,这个异常处理程序可以是在同一个函数中或则在高级层的调试栈中。

5.6.1 标签语句
语句是可以添加标签的,标签是有语句前的标识符和冒号组成:

identifier:statement

通过给语句定义标签,就可以在程序的任何地方通过标签名应用这条语句。也可以对多条语句定义标签,尽管只有在给语句块定义标签时它才更有用,比如循环和条件判断语句。通过给循环定义一个标签名,可以在循环体内部使用break和continue来退出循环或者直接跳转到下一个循环的开始。break和continue是JavaScript中唯一可以使用语句标签的语句。
用作标签的identifier必须是一个合法的JavaScript标识符,而不能是一个保留字。标签的命名空间和变量或函数的命名空间是不同的,因此可以使用同一个标识符作为语句标签和作为变量名或函数名。语句标签只有在它所起作用的语句(当然也可以在它的子句中)内是有定义的。一个语句标签不能和它内部的语句标签重名,但在两个代码段不相互嵌套的情况下是可以出现同名的语句标签的。带有标签的语句还可以带有标签,也就是说,任何语句可以有很多个补签。

5.6.2 break 语句
break能够使循环和switch语句退出;
JavaScript中同样允许break关键字后面跟随一个语句标签(只有标识符,没有冒号):break labelname;
当break和标签一块使用时,程序将跳转到这个标签所标示的语句块的结束,或者直接终止这个闭合语句块的执行。当没有任何闭合语句块指定了break所用的标签,这时会产生一个错误。当使用这种形式的break语句时,带标签的语句不应该是循环或者switch语句,因为break可以跳出任何闭合的语句块。这里的语句可以是有花括号括起来的一语句,使用同一标签来组织在这一组语句。
在break关键字和labelname之间不能换行。因为JavaScript可以给语句自动补全省略掉的分号,如果break关键字和标签按键之间有换行,JavaScript解释器会认为你再使用break不带标签的最简的形式,因此会在break后补充分号(2.5)。
当你希望通过break来跳出非就近的循环体或者switch鱼聚会,就会用到带标签的bresk语句。

        var matrix = getData();
        var sum = 0,success = false;  
        compute_sum:if (matrix) {
            for (var i = 0; i < matrix.length; i++) {
                var row = matrix[x];
                if (!row) break compute_sum;
                for (var j = 0; j < row.length; j++) {
                  var cell = row[y];
                  if (isNaN(cell)) break compute_sum;
                  sum += cell;
                }
            }
        success = true;
        }

PS:不管break语句带不带标签,它控制权都无法越过函数边界。比如,对于一条带标签的函数定义语句再来,不能从函数内部通过这个标签来跳转到函数外部。

5.6.3 continue语句

他不是退出循环,而是转而执行下一个循环。
不管continue语句带不带标签,他只能在循环体内使用。在其他地方使用会报语法错误。
当执行到continue语句的时候,当前的循环逻辑就终止了,随即执行下一次循环,在不同类型的循环中,continue的行为也有所区别:

·在while循环中,在循环开始处指定的expression会重复检测,如果检测结果为true,循环体会从头开始执行。
·在do/while循环中,程序的执行直接跳到循环结尾处,这时会重新判断循环条件,之后才会继续下一次循环。
·在for循环中,首先计算自增表达式,然后再次检测test表达式,用于判断是否执行循环体。
·在for/in循环中,循环开始遍历下一个属性名,这个属性名赋给了指定的变量。

PS:和break语句类似,带标签的continue语句可以用在嵌套的循环中,用于跳出多层次嵌套的循环体逻辑。同样和break语句类似,在continue语句和labelname之间不能有换行。

5.6.4 return语句
return语句只能再函数体内出现,如果不是的话会报语法错误。当执行到return语句的时候,函数终止执行,并返回expression的值给调用程序。
PS:由于JavaScript可以自动插入分号(2.5),因此在return关键字和它后面的表达式之间不能有换行。

5.6.5 throw 语句
所谓异常是当发生了某种异常情况或错误时产生的一个信号。抛出异常,就是用信号通知发生了错误或异常状况。捕获异常是指处理这个信号,即采取必要的手段从异常中恢复。在JavaScript中,当产生运行时错误或者程序使用throw语句是就会显式地抛出异常。使用try/catch/finally语句可以捕获异常。
throw语句的语法如下:throw expression;
expression的值可以是任意类型的。可以抛出一个代表错误码的数字,或者包含可读的错误消息的字符串。当JavaScript解释器抛出异常的时候通常采用Error类型和其子类型,当然也可以使用它们。一个Error对象有一个name属性表示错误类型,一个messaeg属性用来存放传递给构造函数的字符串(参照第三分部的Error类),在下面的例子中,当使用非法参数调用函数时就抛出一个Error对象:

  factorial:function name(x) {
        if (x<0)throw new Error("x不能是负数");       
        for (var f = 1; x>1; f*=x,x--) 
        return f;
    }
        console.log(this.factorial(10));
        console.log(this.factorial(-10));

当抛出异常时,JavaScript解释器会立即停止当前正在执行的逻辑,并跳转至就近的异常处理程序。异常处理程序是用try/catch/fianlly语句的catch从句编写的,下一节会介绍它。如果抛出异常的代码块没有一条相关联的catch从句,解释器会检查更高层的闭合代码块,看它是否有相关联的异常处理程序。以此类推,直到找到一个异常处理程序为止。如果抛出异常的函数没有出理它的try/catch/fianlly语句,异常将向上传播到调用该函数的代码。这样的话,异常就会沿着JavaScript方法的词法结构和调用栈向上传播。如果没有找到任何异常处理程序,JavaScript将把异常当成程序错误来处理,并报告给用户。

5.6.6 try/catch/fianlly语句
try/catch/fianlly语句是JavaScript的异常处理机制。其中try从句定义了需要处理的异常所在的代码块。catch从句跟随在try从句之后,当try块内某处发生了异常时,调用catch内的代码逻辑。catch从句后跟随finally块,后者中放置清理代码,不管try块中是否发生异常,finally块内的逻辑总是会执行。尽管catch和finally都是可选的,但try从句需要至少二者之一与之组成完整的语句。try、catch和finally语句块都需要使用花括号括起来,这里的花括号是必需的,即使从句中只有一条语句也不能省略花括号。
下面的代码说明了try/catch/finally的语法和使用目的:

       try {
            //需要检测的代码块        
        } catch (error) {
            //当且仅当try语句块抛出了异常,才会执行这里的代码
            //这里可以通过局部变量error来获得对Error对象或者抛出的其他值的引用
            //这里的代码块可以基于某种原因处理这个异常,也可以忽略这个异常,还可以通过throw语句重新抛出异常
        }
        finally{
            //不管try语句块是否抛出了异常,这里的逻辑总是会执行,终止try语句块的方式有:
            //1正常终止,执行完语句块的最后一条语句
            //2通过break、continue或return语句终止
            //3抛出一个异常,异常被catch从句捕获
            //4抛出一个异常,异常未被捕获,继续向上传播
        }

下面举个实际的例子,这里使用了前面提到的factorial()方法,并使用客户端JavaScript方法prompt()和alert()来输入和输出:

 try {
            var n = Number(prompt("请输入一个正整数",""));
            var f = this.factorial(n);
            alert(n + "| ="+f);
        } catch (error) {
            alert(error);        
        }

通常情况下,解释器执行到try块的尾部,然后开始执行finally中的逻辑,以便进行必要的清理工作。当由于return、continue或break语句使得解释器跳出try语句块时,解释器在执行新的目标代码之前先执行finally块中的逻辑。
如果try中产生了异常,而且存在一条与之相关的catch从句来处理这个异常,解释器会首先执行catch中的逻辑,然后执行finally中的逻辑。如果不存在处理异常的局部catch从句,解释器会首先执行finally中的逻辑,然后向上传播这个异常,直到找到能处理这个异常的catch从句。
如果finally块使用了return、continue、break或者throw语句使程序发生跳转,或者通过调用了抛出异常的方法改变了程序执行流程,不管这个跳转是程序挂起还是继续执行,解释器都会将其忽略。例如,如果finally从句抛出一个异常,这个异常将代替正在抛出的异常。如果finally从句运行到了return语句,尽管已经抛出了异常且这个抛出的异常还没有处理,这个方法依然会正常返回。

5.7 其他语句类型

5.7.1 with语句
3.10.3 讨论了作用域链,一个可以按序检索的对象列表,通过它可以进行变量名解析。with语句用于临时扩展作用域链,它有如下的语法:

withobject)
statement

这条语句将object添加到作用域链的头部,然后执行statement,最后把作用域链恢复到原始状态。
在严格模式中(5.7.3)是禁止使用with语句的,并且在非严格模式里也是不推荐使用with语句的,尽可能避免使用with语句。那些使用with语句的JavaScript代码非常难于优化,并且同没有使用with语句的代码相比,它运行得更慢。
在对象嵌套层次很深的时候通常会使用with语句来简化代码编写。例如,在客户端JavaScript中,可能会使用类似下面这种表达式来访问一个HTML表单中的元素:document.forms[0].address.value
如果这种表达式在代码中多次出现,则可以使用with语句将from对象添加至作用域链的顶层:

 with(document.forms[0]){//直接访问表单元素
            name.value = "";
            address.value ="";
            email.value ="";
        }

这种方法减少了大量的输入,不用再为每个属性名称添加
document.forms[0]前缀。这个对象临时挂载在作用域链上,当JavaScript需要解析诸如address的标识符是,就会自动在这个对象中查找。当然,不使用with语句的等价代码可以写成这样:

 var f = document.forms[0];
        f.name.value = "",
        f.address.value = "";
        f.email.value = "";

不要忘记,只有在查找标识符的时候才会用到作用域链,创建新的变量的时候不使用它,看一下下面这行代码:with(o) x = 1;
如果对象o有一个属性x,那么这行代码给这个属性赋值为1。但如果o中没有定义属性x,这段代码和不使用with语句的代码x=1是一模一样的。他给一个局部变量或者全局变量x赋值,或者创建全局对象的一个新属性。with语句提供了一种读取o的属性的快捷方式,但它并不能创建o的属性。
严格模式下禁止使用with语句:因为with语句无法在编译时就确定,属性到底归属那个对象。

5.7.2 debugger语句
debugger语句通常什么也不做。然而,当调试程序可用并运行的时候,JavaScript解释器将会(非必备)以调试模式运行。实际上,这条语句用来产生一个断点,JavaScript代码的执行会停止在断点的位置,这时可以使用调试器输出变量的值、检查调用栈等。例如,假设由于调用函数f()的时候使用了未定义的参数,因此f()抛出一个异常,但无法定位到底哪里抛出了异常。为了有助于调试这个问题,需要修改函数f():

    f:function (o) {
        if (o==undefined) {
            debugger; //这一行代码只是用于临时调试
        }
    },

这时,当调用f()的时候没有传入参数,程序将停止执行,这时可以通过调试器检测调用栈并找出错误产生的原因。
在ECMAScript 5中,debugger语句正式加入到这门语言里。但在相当长的一段时间里,主流浏览器厂商已经将其实现了。注意,可用的调试器是远远不够的,debugger语句不会启动调试器。但如果调试器已经在运行中,这条语句才会真正产生一个断点。例如,如果使用Firefox的调试扩展插件Firebug,则必须首先为待调试的网页启用Friebug,这样debugger语句才能正常工作。

5.7.3 “use strict”
“use strict”是ECMAScript5引入的一条指令。指令不是语句(但非常接近于语句)。
“use strict”指令和普通的语句之间有两个重要的区别:

·它不包含任何语言的关键字,指令仅仅是一个包含一个特殊字符串直接量的表达式(可以是使用单引号也可以使用双引号),对于那些没有实现ECMAScript5的JavaScript解释器来说。他只是一条没有副作用的表达式语句,他什么也没做。将来的ECMAScript标准希望将use用做关键字,这样就可以省略引号了。
·它只能出现在脚本代码的开始或者函数体的开始、任何实体语言之前。但他不必一定出现在脚本的首行或函数体内的首行,因为 "use strict"指令之后或之前都可能有其他字符串直接量表达式语句,并且JavaScript的具体实现可能将他们解析为解释器自有的指令。在脚本或者函数体内第一条常规语句之后字符串直接量表达式语句只当做普通的表达式语句对待;它们不会当做指令解析,他们也没有任何副作用。

使用”use strict”指令的目的是说明(脚本和函数中)后续的代码将会解析为严格代码。如果顶层(不在任何函数内的)代码使用了”use strict”指令,那么它们就是严格代码。如果函数体定义所处的代码是严格代码或者函数体使用了 “use strict”指令,那么函数体的代码也是严格代码。如果eval()调用时所处的代码是严格代码或者eval()要执行的字符串中是使用了 “scrict code”指令,则eval()内的代码是样代码。
严格代码以严格模式执行。ECMAScript5中的严格模式是该语言的一个受限制的子集,它修正了语言的重要缺陷,并提供健壮的查错功能和增强的安全机制。严格模式和非严格模式之间的区别如下(前三条尤为重要):

·在严格模式中禁止使用with语句。
·在严格模式中,所有的变量都要先声明,如果给一个未声明的变量、函数、函数参数、cantch从句参数或全局对象的属性赋值,将会抛出一个引用错误异常(在非严格模式中,这种隐式的全局变量的方法是给去全局对添加一个新属性)。
·在严格模式中,调用的函数(不是方法)中的一个this 值是undefined。(在非严格模式中,调用的函数中的this值总是全局对象)。可以利用这种特性来判断JavaScript实现是否支持严格模式:
var hasStrictMode = (function(){"use strict";return this == undefined});
·同样,在严格模式中,当通过call()或apply()来调用函数时,其中的this值就是通过call()或apply()传入的第一个参数(在非严格模式中,nullundefined值被全局对象和转换为对象的非对象值所替代)。
·在严格模式中,给只读属性赋值和给不可扩展的对象创建新成员都将抛出一个类型错误异常(在非严格模式中,这些操作只是简单的操作失败,不会报错)。
·在严格模式中,传入eval()的代码不能在调用程序所在的上下文中声明变量或定义函数,而在非严格模式中是可以这样做的。相反,变量变量和函数的定义在eval()穿创建的新作用域中,这个作用域在eval()返回时就启用了。
·在严格模式中,函数里的arguments对象(8.3.2)拥有传入函数值的静态副本。在非严格模式中,arguments对象具有“魔术般”的行为,arguments里的数组元素和函数参数都是指向同一个值的引用。
·在严格模式中,当delete运算符后跟随非法的标识符(比如变量、函数、函数参数)是,将会抛出一个语法错误异常(在非严格模式中,这种delete表达式什么也没做,并返回false)。
·在严格模式中,试图删除一个不可配置的属性将抛出一个类型错误异常(在非严格模式中,delete表达式操作失败,并返回false)。
·在严格模式中,在一个对象直接量中定义两个或多个同名属性将产生一个语法错误(在非严格模式中不会报错)。
·在严格模式中,函数声明中存在两个或多个同名的参数将产生一个语法错误(在非严格模式中不会报错)。
·在严格模式中是不允许使用八进制整数直接量(以0位前缀,而不是0x为前缀)的(在非严格模式中某些实现是允许八进制整数直接量的)。
·在严格模式中,标识符evalarguments当做关键字,他们的值是不能更改的。不能给这些标识符赋值,也不能把他们声明为变量、用做函数名、用做函数参数或用做catch块的标识符。
·在严格模式中限制了对调用栈的检测能力,在严格模式的函数中,arguments.caller和arguments.callee都会抛出一个类型错误异常。严格模式的函数同样具有caller和arguments属性,当访问这两个属性时将抛出类型错误异常(有一些JavaScript的实现在非严格模式里定义了这些非标准的属性)。

5.8 JavaScript语句小结
如上表所示,列出了每种语句的用法和用途。

猜你喜欢

转载自blog.csdn.net/qq_39710961/article/details/81097401