您的位置:首页 > 其它

第3章 基本概念(二)

2012-03-14 12:59 211 查看

3.5 操作符

ECMA-262描述了一组用于操作数据值的操作符,包括算术操作符 (如加号和减号) 、位操作符、关系操作符和相等操作符。ECMAScript 操作符的与众不同之处在于,它们能够适用于很多值,例如字符串、数字值、布尔值,甚至对象。不过,在应用于对象时,相应的操作符通常都会调用对象的 valueOf() 和(或) toString() 方法,以便取得可以操作的值。

3.5.1 一元操作符

1.递增和递减操作符
递增和递减操作符直接借鉴自 C ,而且各有两个版本:前置型和后置型。
执行前置递增和递减操作时,变量的值都是在语句被求值以前改变的 (在计算机科学领域,这种情况通常被称作副效应)。请看下面这个例子:
var age = 29;
var anotherAge = --age + 2;
alert(age); // 输出 28
alert(anotherAge); // 输出 30
这个例子中变量 anotherAge 的初始值等于变量 age 的值前置递减之后加 2 。 由于先执行了减法操作,age 的值变成了 28,所以再加上 2 的结果就是 30 。
由于前置递增和递减操作与执行语句的优先级相等,因此整个语句会从左至右被求值。再看一个例子:
var num1 = 2;
var num2 = 20;
var num3 = --num1 + num2; // 等于 21
var num4 = num1 + num2; // 等于 21
后置递增和递减与前置递增和递减有一个非常重要的区别,即递增和递减操作是在包含它们的语句被求值之后才执行的。请看下面的例子:
var num1 = 2;
var num2 = 20;
var num3 = num1-- + num2; // 等于 22
var num4 = num1 + num2; // 等于 21
所有这4个操作符对任何值都适用,也就是它们不仅适用于整数,还可以用于字符串、布尔值、浮点数值和对象。在应用于不同的值时,递增和递减操作符遵循下列规则。

在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减 1 的操作。字符串变量变成数值变量。
在应用于一个不包含有效数字字符的字符串时,将变量的值设置为 NaN 。字符串变量变成数值变量。
在应用于布尔值 false 时,先将其转换为 0 再执行加减 1 的操作。布尔值变量变成数值变量。
在应用于布尔值 true 时,先将其转换为 1 再执行加减 1 的操作。布尔值变量变成数值变量。
在应用与浮点数值时,执行加减 1 的操作。
在应用于对象时,先调用对象的 valueOf() 方法以取得一个可供操作的值。然后对该值应用前述规则。如果结果是 NaN ,则在调用 toString() 方法后再应用前述规则。对象变量变成数值变量。

以下示例展示了上面的一些规则:
var s1 = "2";
var s2 = "z";
var b = false;
var f= 1.1;
var o = {
valueOf: function(){
return -1;
}
};

s1++; // 值变成数值3
s2++; // 值变成 NaN
b++; // 值变成数值1

f--; // 值变成 0.100000000000000000000009 (由于浮点舍入错误所致)
o--;
// 值变成数值 -2
2.一元加和减操作符
一元加操作符以一个加号 (+) 表示,放在数值前面,对数值不会产生任何影响。
不过,在对非数值应用一元加操作符时,该操作符会像 Number() 转型函数一样对这个值执行转换。换句话说,布尔值 false 和 true 将被转换为 0 和 1,字符串会被按照一组特殊的规则进行解析,而对象是先调用它们的 valueOf() 和(或) toString() 方法,再转换得到的值。
下面的例子展示了对不同数据类型应用一元加操作符的结果:
var s1 = "01";
var s2 = "1.1";
var s3 = "z";
var b = false;
var f = 1.1;
var o = {
valueOf: function(){
return -1;
}
};
s1 = +s1; // 值变成数值 1
s2 = +s2; // 值变成数值 1.1
s3 = +s3; // 值变成 NaN
b = +b; // 值变成数值 0
f = +f; // 值未变,仍然是 1.1
o = +o; // 值变成数值 -1
在将一元减操作符应用于数值时,该值会变成负数。而当应用于非数值时,一元减操作符遵循与一元加操作符相同的规则,最后再将得到的数值转换为负数。
一元加和减操作符主要用于基本的算术运算,也可以像前面示例所展示的一样用于转换数据类型。

3.5.2 位操作符

位操作符用于在最基本的层次上,即按内存中表示数值的位来操作数值。

3.5.3 布尔操作符

在一门编程语言中,布尔操作符的重要性堪比相等操作符。如果没有测试两个值关系的能力,那么诸如 if...else 和循环之类的语句就不会有用武之地了。布尔操作符一共有3个:非 (NOT) 、与 (AND) 和 或 (OR) 。
1.逻辑非
逻辑非操作符由一个叹号 (!) 表示,可以应用于 ECMAScript 中的任何值。无论这个值是什么数据类型,这个操作符都会返回一个布尔值。逻辑非操作符首先会将它的操作数转换为一个布尔值,然后再对其求反。也就是说,逻辑非操作符遵循下列规则:

如果操作数是一个对象,返回 false;
如果操作数是一个空字符串,返回 true;
如果操作数是一个非空字符串,返回 false;
如果操作数是数值 0,返回 true;
如果操作数是任意非0数值 (包括 Infinity) ,返回 false;
如果操作数是 null,返回 true;
如果操作数是 NaN,返回 true;
如果操作数是 undefined,返回 true。

2.逻辑与
逻辑与操作符由两个和号 ( && ) 表示,有两个操作数。
逻辑与操作可以应用于任何类型的操作数,而不仅仅是布尔值。在有一个操作数不是布尔值的情况下,逻辑与操作就不一定返回布尔值;此时,它遵循下列规则:

如果第一个操作数是对象,则返回第二个操作数;
如果第二个操作数是对象,则只有在第一个操作数的求值结果为 true 的情况下才会返回该对象;
如果两个操作数都是对象,则返回第二个操作数;
如果有一个操作数是 null,则返回 null;
如果有一个操作数是 NaN ,则返回 NaN;
如果有一个操作数是 undefined ,则返回 undefined 。

在使用逻辑与操作符时要始终铭记它是一个短路操作符。
3.逻辑或
逻辑或操作符由两个竖线符合 ( || ) 表示,有两个操作数。
与逻辑与操作相似,如果有一个操作数不是布尔值,逻辑或也不一定返回布尔值;此时,它遵循下列规则:

如果第一个操作数是对象,则返回第一个操作数;
如果第一个操作数的求值结果为 false ,则返回第二个操作数;
如果两个操作数都是对象,则返回第一个操作数;
如果两个操作数都是 null,则返回 null;
如果两个操作数都是 NaN,则返回 NaN;
如果两个操作数都是 undefined,则返回 undefined。

与逻辑与操作符相似,逻辑或操作符也是短路操作符。也就是说,如果第一个操作数的求值结果为 true,就不会对第二个操作数求值了。

我们可以利用逻辑或的这一行为来避免为变量赋 null 或 undefined 值。例如:
var myObject = preferredObject || backupObject;
在这个例子中,变量 myObject 将被赋予等号后面两个值中的一个。变量 preferredObject 中包含优先赋给变量 myObject 的值,变量 backupObject 负责在 preferredObject 中不包含有效值的情况下提供后备值。如果 preferredObject 的值不是 null ,那么它的值将被赋给 myObject ;如果是 null,则将 backupObject 的值赋给 myObject
。ECMAScript 程序的赋值语句经常会使用这种模式,本书也将采用这种模式。

3.5.4 乘性操作符

ECMAScript 定义了3个乘性操作符:乘法、除法和求模。这些操作符与 Java、C或者 Perl 中的相应操作符用途类似,只不过在操作数为非数值的情况下会执行自动的类型转换。如果参与乘法计算的某个操作数不是数值,后台会先使用 Number() 转型函数将其转换为数值。也就是说,空字符串将被当作 0 ,布尔值 true 将被当作 1 。

3.5.5 加性操作符

加法和减法在 ECMAScript 中,都有一系列的特殊行为。与乘性操作符类似,加性操作符也会在后台转换不同的数据类型。然而对于加性操作符而言,相应的转换规则还稍微额有点复杂。
1.加法
加法操作符 ( + ) 的用法如下所示:
如果两个操作数都是数值,执行常规的加法计算。
不过,如果有一个操作数是字符串,那么就要应用如下规则:

如果两个操作数都是字符串,则将第二个操作数与第一个操作数拼接起来;
如果只有一个操作数是字符串,则将另一个操作数转换为字符串,然后再将两个字符串拼接起来。

如果有一个操作数是对象、数值或布尔值,则调用它们的 toString() 方法取得相应的字符串值,然后再应用前面关于字符串的规则。对于 undefined 和 null ,则分别调用 String() 函数并取得字符串 "undefined" 和 "null" 。

3.5.6 关系操作符

小于 (<) 、大于 (>) 、小于等于 (<=) 和大于等于 (>=) 这几个关系操作符用于对两个值进行比较,比较的规则与我们在数学课上所学的一样。这几个操作数都返回一个布尔值。
与 ECMAScript 中的其他操作符一样,当关系操作符的操作数使用了非数值时,也要进行数据转换或完成某些奇怪的操作。以下就是相应的规则:

如果两个操作数都是数值,则执行数值比较;
如果两个操作数都是字符串,则比较两个字符串对应的字符编码值;
如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较;
如果一个操作数是对象,则调用这个对象的 valueOf() 方法,用得到的结果按照前面的规则执行比较。如果对象没有 valueOf() 方法,则调用 toString() 方法,并用得到的结果根据前面的规则执行比较;
如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较。

在使用关系操作符比较两个字符串时,会执行一种奇怪的操作。很多人都会认为,在比较字符串值时,小于的意思是 "在字母表中的位置靠前" ,而大于则意味着 “在字母表中的位置靠后”,但实际上完全不是那么回事。在比较字符串时,实际比较的是两个字符串中对应位置的每个字符的字符编码值。经过这么一番比较之后,再返回一个布尔值。由于大写字母的字符编码全部小于小写字母的字符编码,因此我们就会看到如下所示的奇怪现象:

var result = "Brick" < "alphabet" ; // true
另一种奇怪的现象发生在比较两个数字字符串的情况下,比如下面这个例子:
var result = "23" < "3" ; // true
这是因为两个操作数都是字符串,而字符串比较的是字符编码。不过,如果像下面例子中一样,将一个操作数改为数值,比较的结果就正常了:
var result = "23" < 3; // false
此时,字符串 “23” 会被转换成数值 23,然后再与 3 进行比较,因此就会得到合理的结果。在比较数值和字符串时,字符串都会被转换成数值,然后再以数值方式与另一个数值比较。当然,这个规则对前面的例子是适用的。可是,如果哦那个字符串不能被转换成一个合理的数值呢?比如:
var result = "a" < 3 ; // false , 因为 "a" 被转换成了 NaN
由于字母 "a" 不能转换成合理的数值,因此就被转换成了 NaN。根据规则,任何操作数与 NaN 进行关系比较,结果都是 false 。于是,就出现了下面这个有意思的现象:
var result1 = NaN < 3; // false
var result2 = NaN >= 3; // false

3.5.7 相等操作符

确定两个变量是否相等是编程中的一个非常重要的操作。在比较字符串、数值和布尔值的相等性时,问题还比较简单。但是涉及到对象的比较时,问题就变得复杂了。最早的ECMAScript 中的相等和不等操作符会在执行比较之前,先将对象转换成相似的类型。后来,有人提出了这种转换到底是否合理的质疑。最后,ECMAScript 的解决方案就是提供两组操作符:相等和不相等 --
先转换再比较,全等和不全等 -- 仅比较而不转换。
1.相等和不相等
ECMAScript 中的相等操作符由两个等于号 ( == ) 表示,如果两个操作数相等,则返回 true 。而不相等操作符由叹号后跟等于号 ( != ) 表示,如果两个操作数不相等,则返回 true 。这两个操作符都会先转换操作数 (通常称为强制转型) ,然后再比较它们的相等性。
在转换不同的数据类型时,相等和不相等操作符遵循下列基本规则:

如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值 -- false 转换为 0 ,而 true 转换为 1;
如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值;
如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf() 方法,用得到的原始值按照前面的规则进行比较;

这两个操作符在进行比较时则要遵循下列规则。

null 和 undefined 是相等的。
要比较相等性之前,不能将 null 和 undefined 转换成其他任何值。
如果有一个操作数是 NaN ,则相等操作符返回 false ,而不相等操作符返回 true 。重要提示:即使两个操作数都是 NaN ,相等操作符也返回 false ;因为按照规则, NaN 不等于 NaN。
如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回 true ;否则,返回 false 。

下表列出了一些特殊情况及比较结果:



2.全等和不全等
除了在比较之前不转换操作数之外,全等和不全等操作符与相等和不相等操作符没有什么区别。全等操作符由3个等于号 ( === ) 表示,它只在两个操作数未经转换就相等的情况下返回 true,如下面的例子所示:
var result1 = ( "55" == 55); // true ,因为转换后相等
var result2 = ( "55" === 55); // false ,因为不同的数据类型不相等
不全等操作符由一个叹号后跟两个等于号 ( !== ) 表示,它在两个操作数未经转换就不相等的情况下返回 true 。
var result1 = ( "55" != 55); // false ,因为转换后相等
var result2 = ( "55" !== 55); // true ,因为不同的数据类型不相等

3.5.8 条件操作符

条件操作符应该算是 ECMAScript 中最灵活的一种操作符了,而且它遵循与 Java 中的条件操作符相同的语法形式,如下面的例子所示:
variable = boolean_expression ? true_value : false_value ;

3.5.9 赋值操作符

每个主要算术操作符 (以及个别的其他操作符) 都有对应的复合赋值操作符。
设计这些操作符的主要目的就是简化赋值操作。使用它们不会带来任何性能的提升。

3.6 语句

ECMA-262规定了一组语句 (也称为流控制语句)。

3.6.1 if 语句

以下是 if 语句的语法:
if (condition) statement1 else statement2
其中 condition (条件) 可以是任意表达式;而且对这个表达式求值的结果不一定是布尔值。ECMAScript 会自动调用 Boolean() 转换函数将这个表达式的结果转换为一个布尔值。而且这两个语句既可以是一行代码,也可以是一个代码块 (以一对花括号括起来的多行代码)。请看下面的例子
var i = 24;
if (i < 25)
alert("Greater than 25."); // 单行语句
else {
alert("Less than or equal to 25."); // 代码块中的语句
}
不过,业界普遍推崇的最佳实践是始终使用代码块,即使要执行的只有一行代码。因为这样可以消除人们的误解,否则可能让人分不清在不同条件下要执行哪些语句。

3.6.2 do-while 语句

do-while 语句是一种后测试循环语句,即只有在循环体中的代码执行之后,才会测试出口条件。换句话说,在对条件表达式求值之前,循环体内的代码至少会被执行一次。以下是 do-while 语句的语法:
do {
statement
} while(expression);
像 do-while 这种后测试循环语句最常用于循环体中的代码至少要被执行一次的情形。

3.6.3 while 语句

while 语句属于前测试循环语句,也就是说,在循环体内的代码被执行之前,就会对出口条件求值。因此,循环体内的代码有可能永远不会被执行。以下是 while 语句的语法:
while (expression) statement

3.6.4 for 语句

for 语句也是一种前测试循环语句,但它具有在执行循环之前初始化变量和定义循环后要执行的代码的能力。以下是 for 语句的语法:
for (initialization; expression; post-loop-expression) statement
使用 while 循环做不到的,使用 for 循环同样也做不到。也就说, for 循环只是把与循环有关的代码集中在了一个位置。
由于 ECMAScript 中不存在块级作用域,因此在循环内部定义的变量也可以在外部访问到。例如:
var count = 10;
for (var i = 0;i<count;i++){
alert(i);
}
alert(i); // 10
在这个例子中,会有一个警告框显示循环完成后变量 i 的值,这个值是 10 。这是因为,即使 i 是在循环内部定义的一个变量,但在循环外部仍然可以访问到它。
此外,for 语句中的初始化表达式、控制表达式和循环后表达式都是可选的。将这两个表达式全部省略,就会创建一个无限循环,例如:
for( ; ; ) { // 无限循环
doSomething();
}

3.6.5 for-in 语句

for-in 语句是一种精准的迭代语句,可以用来枚举对象的属性。以下是 for-in 语句的语法:
for (property in expression) statement
下面是一个示例:
for (var propName in window) {
document.write(propName);
document.write("<br/>");
}
在这个例子中,我们使用 for-in 循环来显示了 BOM 中 window 对象的所有属性。每次执行循环时,都会将 window 对象中存在的一个属性名赋值给变量 propName 。这个过程会一直持续到对象中的所有属性都被枚举一遍为止。与 for 语句类似,这里控制语句中的 var 操作符也不是必需的。但是,为了保证使用局部变量,我们推荐上面例子中的这种做法。
ECMAScript 对象的属性都没有顺序。因此,通过 for-in 循环输出的属性名的顺序是不可预测的。具体来讲,所有属性都会被返回一次,但返回的先后次序可能会因浏览器而异。
Safari 3 以前版本的 for-in 语句中存在一个 bug ,该 bug 会导致某些属性被返回两次。

3.6.6 label 语句

使用 label 语句可以在代码中添加标签,以便将来使用。以下是 label 语句的语法:
label : statement
下面是一个示例:
start: for(var i=0; i<count; i++) {
alert(i);

}
这个例子中定义的 start 标签可以在将来由 break 或 continue 语句引用。加标签的语句一般都要与 for 语句等循环语句配合使用。

3.6.7 break 和 continue 语句

break 和 continue 语句用于在循环中精确地控制代码的执行。其中,break 语句会立即退出循环,强制继续执行循环后面的语句。而 continue 语句虽然也是立即退出循环,但退出循环后会从循环的顶部继续执行。请看下面的例子:
var num = 0;
for (var i = 1; i < 10 ; i++){
if (i % 5 == 0) {
break;
}
num++;
}
alert (num); // 4
如果在这里把 break 替换为 continue 的话,则可以看到另一种结果:
var num = 0;
for (var i = 1; i < 10; i++) {
if (i % 5 == 0) {
continue;
}
num++;
}
alert(num); // 8
break 和 continue 语句都可以与 label 语句联合使用,从而返回代码中特定的位置。这种联合使用的情况多发生在循环嵌套的情况下,如下面的例子所示:
var num = 0;
outermost:
for (var i = 0; i < 10; i++) {
for (var j=0; j < 10; j++) {
if ( i == 5 && j == 5) {
break outermost;
}
num++;
}
}
alert(num); // 55
在这个例子中,outermost 标签表示外部的 for 语句。如果每个循环正常执行 10 次,则 num++ 语句就会正常执行 100 次。换句话说,如果两个循环都自然结束, num 的值应该是 100 。但内部循环中的 break 语句带了一个参数 : 要返回到的标签。添加这个标签的结果将导致 break 语句不仅会退出内部的 for 语句 (即使用变量 j 的循环),而且也会退出外部的
for 语句 (即使用变量 i 的循环)。为此,当变量 i 和 j 都等于5时, num 的值正好是 55 。同样,continue 语句也可以像这样与 label 语句联用,如下面的例子所示:
var num = 0;
outermost:
for (var i=0; i<10; i++) {
for (var j=0; j<10; j++){
if(i == 5 && j == 5){
continue outermost;
}
num++;
}
}
alert(num); // 95
在这种情况下,continue 语句会强制继续执行循环 -- 退出内部循环,执行外部循环。当 j 是5时, continue 语句执行,而这也就意味着内部循环少执行了 5 次,因此 num 的结果是 95 。
虽然联用 break 、continue 和 label 语句能够执行复杂的操作,但如果使用过度,也会给调试带来麻烦。在此,我们建议如果使用 label 语句,一定要使用描述性的标签,同时不要嵌套过多的循环。

3.6.8 with 语句

with 语句的作用是将代码的作用域设置到一个特定的对象中,with 语句的语法如下:
with (expression) statement
定义 with 语句的目的主要是为了简化多次编写同一个对象的工作,如下面的例子所示:
var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;
上面几行代码都包含 location 对象。如果使用 with 语句,可以把上面的代码改写成如下所示:
with (location) {
var qs = search.substring(1);
var hostName = hostname;
var url = href;
}
这个重写后的例子中,使用 with 语句关联了 location 对象。这意味着在 with 语句的代码块内部,每个变量首先被认为是一个局部变量,而如果在局部环境中找不到该变量的定义,就会查询 location 对象中是否有同名的属性。如果发现了同名属性,则以 location 对象属性的值作为变量的值。
由于大量使用 with 语句会导致性能下降,同时也会给调试代码造成困难,因此在开发大型应用程序时不建议使用 with 语句。

3.6.9 switch 语句

switch 语句与 if 语句的关系最为密切,而且也是在其他语言中普遍使用的一种流控制语句。ECMAScript 中 switch 语句的语法与其他基于 C 的语言非常接近,如下所示:
switch (expression) {
case value: statement
break;
case value: statement
break;
case value: statement
break;
case value: statement
break;
default: statement
}
switch 语句中的每一种情形 (case) 的含义是:“如果表达式等于这个值 (value) ,则执行后面的语句 (statement)” 。而 break 关键字会导致代码执行流跳出 switch 语句。如果省略 break 关键字,就会导致执行完当前 case 后,继续执行下一个 case 。最后的 default 关键字则用于在表达式不匹配前面任何一种情形的时候,执行机动代码 (因此,也相当于一个 else 语句)。
从根本上讲,switch 语句就是为了让开发人员免于编写像下面这样的代码:
var i = 25;
if (i == 25) {
alert("25");
} else if (i == 35){
alert("35");
} else if (i == 45){
alert("45");
} else {
alert("Other");
}
而与此等价的 switch 语句如下所示:
var i = 25;
switch (i) {
case 25:
alert("25");
break;
case 35:
alert("35");
break;
case 45:
alert("45");
break;
default:
alert("Other");
}
通过为每个 case 后面都添加一个 break 语句,就可以避免同时执行多个 case 代码的情况。假如确实需要混合几种情形,不要忘记在代码中添加注释,说明你是有意省略了 break 关键字,如下所示:
var i = 25;
switch (i){
case 25:
/* 合并两种情形 */
case 35:
alert("25 or 35");
break;
case 45:
alert("45");
break;
default:
alert("Other");
}
虽然 ECMAScript 中的 switch 语句借鉴自其他语言,但这个语句也有自己的特色。首先,可以在 switch 语句中使用任何数据类型 (在很多其他语言中只能使用数值) ,无论是字符串,还是对象都没有问题。其次,每个 case 的值不一定是常量,可以是变量,甚至是表达式。请看下面这个例子:
switch ("hello world"){
case "hello" + " world":
alert("Greeting was found.");
break;
case "goodbye":
alert("Closing was found.");
break;
default:
alert("Unexpected message was found.");
}
在这个例子中, switch 语句使用的就是字符串。其中,第一种情形实际上是一个对字符串拼接操作求值的表达式。由于这个字符串拼接表达式的结果与 switch 的参数相等,因此结果就会显示 "Greeting was found." 。而且,使用表达式作为 case 值还可以实现下列操作:
var num = 25;
switch (true) {
case num < 0:
alert(“Less than 0.”);
break;
case num >=0 && num <= 10:
alert("Between 0 and 10.");
break;
case num > 10 && num <= 20:
alert("Between 10 and 20.");
break;
default:
alert("More than 20.");
}
这个例子首先在 switch 语句外面声明了变量 num 。而之所以给 switch 语句传递表达式 true ,是因为每个 case 值都可以返回一个布尔值。这样,每个 case 按照顺序被求值,直到找到匹配的值或者遇到 default 语句为止 (这正是这个例子的最终结果) 。
switch 语句在比较值时使用的是全等操作符,因此不会发生类型转换 ( 例如, 字符串 “10” 不等于数值 10) 。

3.7 函数

函数对任何语言来说都是一个核心的概念。通过函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行。ECMAScript 中的函数使用 function 关键字来声明,后跟一组参数以及函数体。
ECMAScript 中的函数在定义时不必指定是否返回值。实际上,任何函数在任何时候都可以通过 return 语句后跟要返回的值来实现返回值。
函数会在执行完 return 语句之后停止并立即退出。因此,位于 return 语句之后的任何代码都永远不会执行。例如:
function sum (num1, num2) {
return num1 + num2;
alert("Hello world"); // 永远不会执行
}
另外,return 语句也可以不带有任何返回值。在这种情况下,函数在停止执行后将返回 undefined 值。这种用法一般用在需要提前停止函数执行而又不需要返回值的情况下。比如在下面这个例子中,就不会显示警告框:
function sayHi (name, message) {
return;
alert("Hello " + name + "," + message); // 永远不会调用
}
推荐的做法是要么让函数始终都返回一个值,要么永远都不要返回值。否则,如果函数有时候返回值,有时候又不返回值,会给调试代码带来不便。

3.7.1 理解参数

ECMAScript 函数的参数与大多数其他语言中函数的参数有所不同。ECMAScript 函数不介意传递进来多少个参数,也不在乎传进来参数是什么数据类型。也就是说,即便你定义的函数只接收两个参数,在调用这个函数时也未必一定要传递两个参数。可以传一个、三个甚至不传递参数,而解析器永远不会有什么怨言。之所以会这样,原因是 ECMAScript 中的参数在内部是用一个数组来表示的。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数
(如果有参数的话) 。如果这个数组中不包含任何元素,无所谓;如果包含多个元素,也没有问题。实际上,在函数体内可以通过 arguments 对象来访问这个参数数组,从而获取传递给函数的每一个参数。
其实,arguments 对象只是与数组类似 (它并不是 Array 的实例),因为可以使用方括号语法访问它的每一个元素 (即第一个元素是 arguments[0],第二个元素是 arguments[1] ,以此类推) ,使用 length 属性来确定传递进来多少个参数。
不显式地使用命名参数:
function sayHi () {
alert("Hello " + arguments[0] + "," + arguments[1]);
}

sayHi("Nicholas", "how are you today?");
这个重写后的函数中不包含命名的参数。虽然没有使用 name 和 message 标识符,但函数的功能依旧。这个事实说明了 ECMAScript 函数的一个重要特点:命名的参数只提供便利,但不是必需的。另外,在命名参数方面,其他语言可能需要事先创建一个函数签名,而将来的调用必须与该签名一致。但在 ECMAScript 中,没有这些条条框框,解析器不会验证命名参数。
另一个与参数相关的重要方面,就是 arguments 对象可以与命名参数一起使用,如下面的例子所示:
function doAdd(num1, num2) {
if(arguments.length == 1) {
alert(num1 + 10);
} else if (arguments.length == 2){
alert(arguments[0] + num2);
}
}
doAdd(10); // 20
doAdd(30, 20); // 50
在重写后的这个 doAdd() 函数中,两个命名参数都与 arguments 对象一起使用。由于 num1 的值与 arguments[0] 的值相同,因此它们可以互换使用。
关于参数还要记住最后一点:没有传递值的命名参数将自动被赋予 undefined 值。这就跟定义了变量但又没有初始化一样。例如,如果只给 doAdd() 函数传递了一个参数,则 num2 中就会保存 undefined 值。
ECMAScript 中的所有参数传递的都是值,不可能通过引用传递参数。

3.7.2 没有重载

ECMAScript 函数不能像传统意义上那样实现重载。而在其他语言 (如 Java) 中,可以为一个函数编写两个定义,只要这两个定义的签名 (接受的参数的类型和数量)不同即可。如前所述,ECMAScript 函数没有签名,因为其参数是由包含零或多个值的数组来表示的。而没有函数签名,真正的重载是不可能做到的。
如果在 ECMAScript 中定义了两个名字相同的函数,则该名字只属于后定义的函数。请看下面的例子:
function addSomeNumber (num) {
return num + 100;
}
function addSomeNumber (num) {
return num + 200;
}
var result = addSomeNumber (100) ; // 300
alert(result);
在此,函数 addSomeNumber () 被定义了两次。由于后定义的函数覆盖了先定义的函数,因此当在最后一行代码中调用这个函数时,返回的结果就是 300 。
如前所述,通过检查传入函数中参数的类型和数量并作出不同的反应,可以模仿方法的重载。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: