第五章:语句
2017.02.23
语句
语句(statement)是JavaScript整句或命令。
表达式计算值,语句用来执行以使某件事发生。
表达式语句
表达式语句(expression statement):具有副作用的表达式是JavaScript最简单的语句。
赋值语句
i*=3;
递增递减++
--
counter++;
delete运算符
delete o.x;
函数调用
alert(greeting);
window.close();
复合语句和空语句
用花括号将多条语句结合在一起,形成一条复合语句(compound statement)。希望多条语句被当做一条语句使用时,使用复合语句来替代。
{
x=Math.PI;
cs=Math.cos(x);
console.log("cos(π)="+cx);
}
语句块的结尾不需要分号。
空语句(empty statement)允许包含0条语句。
;
for(i=0;i<a.length;a[i++]=0);
声明语句
var和function都是声明语句,声明或定义变量或函数。这些语句定义标识符(变量名和函数名)并给其赋值。
var
var name_1 [=value_1] [,...,name_n[=value_n]]
function
var f=function(x){return x+1;} //①
function f(x){return x+1;} //②
function语句的花括号是必须的,即使只包含一条语句(不同于while if等语句)。
函数声明语句通常出现在JavaScript代码的最顶层,也可以潜逃在其他函数体内。但在嵌套时,函数声明只能出现在所潜逃函数的顶部。函数定义不能出现在if语句,while循环或其他任何语句中。(有些JS的实现允许出现语句的地方都可以进行函数声明,但是不同的实现在细节处理方式上有很大差别,因此将函数声明放在其他的语句内的做法并不具备可移植性。)
①中使用var,只有变量声明提前,初始化代码还在原位。②函数名称和函数体均提前。
条件语句
条件语句是通过判断指定表达式的值来决定执行还是跳过某些语句。
if/else if
if(expression){
//
}
else if{
//
}...{
//
}else{
//
}
没有花括号时,else总是和最近的if语句相匹配。所以建议使用花括号,即使分支只有一条语句。
switch
此处的case匹配使用===
恒等运算符比较。
switch(expression){
case expression1:
statements1;
[break;]
case expression2:
statements2;
[break;]
case expression3:
statements3;
[break;]
...
default:
statements;
}
循环
while
while(expression){
statements
}
do/while
do {
statements
}while(expression)
for
for(initialize;test;increment){
statements
}
相当于
initialize;
while(test){
statements
increment;
}
死循环
for(;;)
相当于
while(true)
for/in
for(varibale in object){
statement
}
用for in循环遍历对象属性成员:
for(var p in o){
console.log(p+":"+o[p]);//p是属性名,o[p]是属性值
}
注意:只要for/in 的variable值可以当做赋值表达式的左值,它可以是任意表达式。每次循环都会计算这个表达式。
将对象的所有属性名复制到一个数组中:
var o={x:1,y:2,z:3};
var a=[],i=0;
for(a[i++] in o);
枚举数组索引:
> var a=[1,2,,,,4]
undefined
> del
0
1
5
for/in循环并不会遍历对象的所有属性,只有"可枚举"(enumerable)的属性才会遍历到。JavaScript语言核心所定义的内置方法就不是"可枚举的"。对象可以继承其他对象的属性,集成的自定义属性也可以枚举出来。如果for/in循环体删除了还未枚举的属性,那么这个属性将不会再枚举到。如果循环体定义了新属性,这些属性通常不会被枚举到。
属性枚举的顺序
实际上,主流浏览器厂商的JavaScript实现是按照属性定义的先后顺序来枚举简单对象的属性,先定义的先枚举。如果使用对象直接量的形式创建对象,则将按照直接量中属性的出现顺序枚举。
跳转
break:跳转到循环或者其他语句的结束。
continue:终止本次循环的执行并开始下一次循环的执行。
return:让解释器跳出函数体的执行,并提供本次调用的返回值。
throw:触发或者抛出一个异常,与try、catch、finally语句一同使用。
标签语句
identifier:statement
通过break,continue跳转。break和continue是JavaScript中唯一可以使用语句标签的语句。
示例
mainloop:while(token!=null){
//...
continue mainloop;
}
用作标签的identifier必须是合法的标识符,不能使保留字。标签的命名空间与变量函数的命名空间不同,因此可以使用同一个额标识符作为语句标签和作为变量名或函数名。语句标签只有在它所起作用的语句内是有定义的。一个语句标签不能喝它内部的语句标签重名,但在两个代码段不相互嵌套的情况下可以出现同名的语句标签。带有标签的语句还可以带有标签。
outerloop:
for (var i = 0; i < 10; i++) {
innerloop:
for (var j = 0; j < 10; j++) {
if (j > 3){
break;
}
if (i == 2) {
break innerloop;
}
if (i == 4) {
break outerloop;
}
document.write("i=" + i + " j=" + j + "<br>");
}
}
break 语句
单独使用break语句的作用是立即退出最内层的循环或switch语句。
break;
break 关键字后面紧跟一个语句标签。
break labelname;
当break和标签一块使用时,程序将跳转到这个标签所标识的语句块的结束,或者直接终止这个闭合语句块的执行。当没有任何闭合语句块指定break所用的标签,这时会产生一个语法错误。当使用这种形式的break语句时,带标签的语句不应该是循环或者switch语句,因为break可以跳出任何闭合的语句块。这列的语句可以是花括号括起来的一组语句,使用同一个标签来标识这一组语句。
break和labelname之间不能换行。因为换行会在break后面补分号。
break无论带不带标签,都无法越过函数的便捷。在函数内部无法通过标签跳转到函数外部。
continue 语句
执行下一次循环。
continue;
continue后面紧跟一个语句标签。
continue labelname;
不管continue带不带标签,只能在循环体内使用,否则会报语法错误。
带标签的continue语句可以用在嵌套的循环中,用以跳出多层次嵌套的循环体逻辑。
continue和labelname之间不能换行。
return 语句
函数中的return语句是指定函数调用后的返回值。
return expression;
只能在函数体内出现,否则语法错误。
如果没有return语句。函数一次执行函数体内的每一条语句直到结束,表达式结果返回undefined。
return语句不必带有expression,这样函数也会返回undefined。
return和expression之间不能有换行。
throw 语句
异常(exception)是当发生了某种异常情况或错误时产生的一个信号。抛出异常,就是用信号通知发生了错误或异常装潢。捕获异常是指处理这个信号,即采取必要的手段从异常中恢复。在JavaScript中,当产生运行时错误或者程序使用throw语句就会显式地抛出异常。使用try/catch/finally语句可以捕获异常。
throw expression;
例如:
function f(x){
if(x<0) throw new Error("x 不能是负数");
return x;
}
f(-1);//Error: x 不能是负数
当抛出异常时,JavaScript解释器会当即停止当前正在执行的逻辑,并跳转至就近的异常处理程序(try/catch/finally语句段)。如果抛出异常的代码块没有相关联的catch一句,解释器会检查更高层的闭合代码块,以此类推,直到找到一个异常处理程序位置。
如果没有处理语句,异常将向上传播到调用该函数的代码。这样,异常就会沿着JavaScript方法的语法结构和调用栈向上传播。如果没有任何异常处理程序,JavaScript将把异常当做程序错误来处理,并报告给用户。
try/catch/finally 语句
各语句块都需要用花括号括起来。不能省略。
try{
//通常来讲,此处代码从头到尾执行不会有问题,有事会抛出异常,或由throw语句直接抛出或由方法间接抛出
}
catch(e){
//当try语句抛出异常时执行。通过局部变量e或得对Error对象或者抛出的其他值的引用。
//这里的代码块可以基于某种原因处理这个异常,也可以忽略这个异常或者通过throw语句重新抛出异常
}
finally{
//不管try语句块是否抛出异常,这里总会执行。终止try语句块的方式:
// 1)正常终止,执行完最后一条语句
// 2)通过break,continue,renturn终止
// 3)抛出异常,被catch捕获
// 4)抛出异常,异常未被捕获,继续向上传播
}
catch子句中的标识符具有块级作用域,只在catch内有定义。不管try代码执行了多少,只要try语句中有一部分代码执行了,finally从句就会执行。
其他语句
with 语句
域链(scope chain),一个可以按序检索的对象列表,通过它可以进行变量名解析。
with 可用于临时扩展作用域链。
with (object)
statement
这条语句将object添加到作用域头部,然后执行statement,最后把作用域链恢复到原始状态。
严格模式中禁止使用with。使用with语句的JavaScript代码难于优化,运行更慢。
例如:
document.forms[0].address.value
with(document.forms[0]){
name.value="";
address.value="";
email.value="";
}
等价于
var f=document.forms[0];
f.name.value="";
f.address.value="";
f.email.value="";
只有在查找标识符的时候才用到作用域链,创建新的变量的时候不适用。
with(o) x=1;//如果对象o有属性x,那么代码给属性赋值。如果没有,则等价于不含有with语句的情景。给局部变量或全局变量赋值或初始化全局变量并赋值。
debugger 语句
debugger语句通常什么也不做。调试程序可用并且运行时,JavaScript将会以调试模式运行。实际上,此语句用于产生断电,JavaScript代码执行将会停止在短点的位置。
function f(o){
if (o===undefined) debugger;
}
"use strict"
ES5引入的指令。指令不是语句(但非常接近于语句)。
- 不包含任何语言的关键字。指令仅仅是一个包含一个特殊字符串直接量的表达式。(单引号或双引号)
- 只能出现在脚本代码的开始或者函数体的开始、任何实体语句之前。
目的是说明(脚本或函数中)后续的代码将会解析为严格代码。如果顶层使用"use strict"
,那么它们就是严格代码。eval()内代码同样是严格代码。
- 严格模式禁止使用with语句
- 严格模式中,所有变量都要先声明。如果个未命名的变量,函数,函数参数,catch从句参数或全局对象的属性赋值,都会抛出引用错误异常。
- 严格模式中,调用的函数中的一个this值是undefined。非严格模式下this总是全局对象。可以用这种特性判断JavaScript是否支持严格模式。
var hasStrictMode=(function(){"use strict";return this===undefined}());
- 严格模式中,传入eval()的代码不能调用程序所在的上下文中声明变量或定义函数。变量和函数的定义是在eval()创建的新作用域中,这个作用域在eval()返回时就弃用了。
- 严格模式中,函数里的arguments对象拥有传入的函数值的静态副本。非严格模式中,arguments对象具有"魔术般"的行为,arguments里的数组元素和函数参数都是指向同一个值的引用。
- 严格模式中,delete跟随非法的标识符(变量,函数,函数参数)时,会抛出一个语法错误异常。(非严格模式下,delete什么也没做并返回false);试图删除一个不可配置的属性将抛出一个类型错误异常(非严格模式中不会报错)。两种模式下删除非左值都返回true。
- 严格模式中,函数声明中存在两个及以上同名的参数会产生语法错误(非严格模式中不会报错,后者覆盖前者):实际Chrome测试,严格模式下不会报错,后者覆盖前者。
- 严格模式中,函数声明中存在两个或多个同名参数会报错(非严格模式下,会覆盖)
- 严格模式中,不许晕使用八进制整数直接量。
- 严格模式中,eval和arguments当做关键字,值不可以更改。不能给其赋值,不能声明为变量、用做函数名或用作catch块标识符。
- 严格模式中限制了对调用栈的检测能力。 arguments.caller和arguments.callee都会抛出类型错误异常。严格模式函数同样具有calle和arguments属性,访问这两个属性时将抛出类型错误异常(有一些JavaScript的实现在费严格模式里定义了这些非标准的属性)
示例,chrome58 dev 下
【严格模式下】
<script>
"use strict";
function f(){
return this;
}
console.log(f());//输出 undefined
console.log(delete 1===true);// true
var o={x:1,x:2};
var o=123;
console.log(o);//123
function ff(x,x){
return (x+x);
} //Uncaught SyntaxError: Duplicate parameter name not allowed in this context
console.log(ff(1,2));
var m={x:2}
function c(o){
o.x=1;
};
c(m);
console.log(m);//Object {x:1}
</script>
【非严格模式下】
<script>
function f(){
return this;
}
console.log(f());//输出 window对象
console.log(delete 1===true);// true
var o={x:1,x:2};
var o=123;
console.log(o);//123
function ff(x,x){
return (x+x);
} //4
console.log(ff(1,2));
var m={x:2}
function c(o){
o.x=1;
};
c(m);
console.log(m);//Object {x:1}
</script>
chrome 58 dev下
> (function(){"use strict";return this===undefined}());
true
node 6.9.5下
> (function(){"use strict";return this===undefined}());
true