本文深入解析 ECMAScript 中的核心语句。涵盖分号使用规范、变量声明提升及 jQuery 风格的最佳实践。重点讲解 for 循环的性能优化技巧,包括长度缓存与递减遍历。同时警示 with 语句的作用域同步问题及 return 语句自动分号插入导致的未定义返回陷阱,帮助开发者写出更健壮高效的代码。
JavaScript 语句基础与规范
掌握了基础语法元素后,接下来便是构建程序逻辑的主体——语句。在 ECMAScript 规范中,语句构成了代码执行的骨架。对于具备 C 语言背景的开发者而言,这些概念并不陌生。本文将梳理 ECMAScript 语句的关键知识点,特别聚焦于该语言中独特且易被忽视的细节,而基础部分则假设读者已具备相应知识。
语句概览
| 语句 | 语法 | 简要描述 |
| 简单语句 |
; |
语句以分号(;)结束,在不引起歧义的情况下也可以省略分号。 |
| 语句块 |
{} |
使用大括号({})将一组语句放一起组成一个语句块,在ECMAScript中,有语句块,但没有语句块作用域。 |
| if语句 |
if(condition){} if(ocndition){}else{} |
条件选择,在条件表达式中,会将结果隐式转换为Boolean类型。 建议每个分支都明确使用{},以避免维护时出错。 条件语句可以嵌套。 |
| switch语句 |
switch(expression) { case value1: statement1; break; case value2: statement2; break; default: statement; break; } |
switch语句语法和C语言一致,不同的是,switch中的expression不限于整型。 1、在switch语句中,表达式不限于整型,可以是任意表达式。 2、在case后面的value中,可以是整型,也可以是其它类型,甚至可以是一个表达式,但是在比较的时候不会进行类型转换,也即是使用全等(===)进行匹配。 3、case分支中的break表示不再继续后面的匹配,如果省略了会继续执行下面的case语句。建议每个case都加上break,如果是利用这种继续执行的特性,也加上相应注释说明。 4、最后一个分支的break加不加效果相同,我自己的个人风格是加上保持一致性。 |
| do-while语句 |
do{ statement; }while(expression); |
先执行循环体,再进行条件判断,这种格式至少会执行一次循环。 条件判断也会有隐式转换。 |
| while语句 |
while(expression) { statement; } |
满足条件才执行循环体。如果一开始就不满足条件,则根本不会执行循环体。 |
| for语句 |
for(initialization; expression; post-loop-expression){ statement; } |
for语句在功能上和while是等价的。 执行顺序是,先执行初始化initialization,然后进行条件比较expression,如果满足条件,就执行循环体,执行完一次循环后,执行post-loop-expression部分,然后循环比较条件直至跳出整个循环。 |
| for-in语句 |
for(property in expression){ statement; } |
for循环的另一种形式,可以使用这种循环遍历对象的属性和对象原型链上的属性。 |
| with语句 |
with(expression){ statement; } |
将代码的作用域设置到一个特定的对象中。 |
| label语句 | label:statement; | 给代码添加标签供其它语句使用。 |
| break语句 |
break; break label; |
1、用在switch语句中,在找到匹配的case分支后,不继续执行下面的case语句。 2、用在循环语句中中断整个循环。 |
| continue语句 |
continue; continue label; |
在循环语句中中断本次循环,执行下一次循环。 |
| try语句 |
try{ }catch(e){ }finally{ } |
将代码放在try块中,使得异常发生时能够做出相应的处理。 |
| throw语句 | throw e; | 抛出异常。 |
| debugger语句 | debugger; | 调试。 |
| return语句 |
return; return expression; |
返回语句。在return之后没有返回时,返回undefined。 |
以下是对各类语句的具体说明:
1、关于语句末尾是否必须添加分号(;),建议始终为每条语句显式添加分号,以避免 JavaScript 引擎进行自动分号插入(ASI)时的猜测行为。尽管存在反对观点认为省略分号亦能自圆其说,但这并未改变我的建议,仅拓宽了视野。
2、针对 var 语句,鉴于 ECMAScript 存在变量声明提升机制,推荐将作用域内所有变量定义集中置于代码顶部,并通过单个 var 语句声明多个变量。这种做法不仅提升代码可读性,还能减少潜在错误。目前主流 JS 库如 jQuery 均采纳此模式,示例代码如下:
代码如下:
var document = window.document,
navigator = window.navigator,
location = window.location; 3、语句块由大括号({})界定,该语法同样用于定义对象字面量。需注意,在 ECMAScript 中,语句块并不具备独立的作用域限制。
循环语句性能优化策略
4、四种循环语句(do-while、while、for、for-in)中,for-in 因每次迭代均需检索对象自身及原型链属性,效率相对较低。关于 for 循环的性能优化策略如下:
代码如下:
// 1. 标准for循环
for(var i=0; i < arr.length; i++){
}
// 2. 上述写法在每次迭代中重新计算arr.length,若arr为DOM集合,性能损耗显著,优化方案如下
for(var i=0,l=arr.length; i<l; i++){
}
// 3. 此方式仅计算一次长度,若考虑递减逻辑,可调整为
for(var i=arr.length; i>0; i--){
}
// 4. 无需中间变量且仅计算一次长度。鉴于JS中0的布尔值为false,可进一步简化为
for(var i=arr.length; i ; i--){
}
// 5. 为规避变量声明提升可能引发的隐患,最终优化版本如下
var i=arr.length;
for(; i ; i--){
} with 语句的风险与陷阱
5、with 语句虽能简化代码书写,但常导致不可预知的行为,建议尽量避免使用。示例如下:
代码如下:
//1. 使用with语句
with(obj){
a=b;
}
//2.不使用with语句,和1的情况等价
if(obj.a === undefined){
a = obj.b || b;
}else
{
obj.a = obj.b || b;
}
//3. 可能的运行结果
a = b;
a = obj.b;
obj.a = b;
obj.a = obj.b; 第1部分展示 with 用法,第2部分为等效无 with 写法,第3部分列举了最终可能的运行结果。仅从 with 语句本身难以直观判断实际执行路径。此外,涉及属性修改时易产生同步问题,代码如下:
代码如下:
var obj = {
person:{
name:'linjisong'
}
};
with(obj.person){
obj.person = {
name:'oulinhai'
};
console.info(obj.person.name); //oulinhai
console.info(name); //linjisong
} 此处无意间产生了对象属性与局部变量之间的不同步现象。
return 语句的自动分号陷阱
6、在 return 语句返回对象时需注意:
代码如下:
return
{
prop:'value';
//由于引擎会自动插入分号,此处实际返回undefined
return {
prop:'value';
//返回一个对象