3.5.4 乘性操作符
ECMAScript定义了3个操作符:乘法、除法和求模。这些操作符与Java、C或者Perl中应用操作符用途类似,只不过在操作非数值的情况下会执行自动的类型转换。如果参与乘性就算的某个操作数不是数值,后台会先使用Number()转型函数将其转换为数值。也就是说,空字符串会被当做0,布尔值true会被当做1。
1.乘法
乘法操作符有一个*表示,用于计算两个数值的积。其语法类似于C,如下面的例子所示:
var result = 34 * 56;
在处理特殊值的情况下,乘法操作符遵循下列特殊的规则:
如果操作数都是数值,执行常规的乘法运算,即两个正数或者两个负数相乘的结果还是正数,而只有一个操作数有符号,那么结果就是负数。如果乘积超过了JavaScript数值的表示范围,则返回Infinity和-Infinity;
如果有一个操作数是NaN,则结果是NaN;
如果是Infinity和0相乘,则结果是NaN;
如果是Infinity和非0数值相乘,则结果是Infinity或者Infinity,取决于有符号操作数的符号;
如果是Infinity和Infinity相乘,则结果是Infinity;
如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后在加上上面的规则。
2.除法
触发操作符是由一个斜线/表示,执行第二个操作数除第一个操作数的计算,如下面的例子所示:
var result = 66 /11;
与乘法操作符相似,除法操作符对特殊的值也有特殊的处理规则,这些规则如下:
如果操作符是数值,执行常规的除法计算,即两个正数或者两个负数相除的结果还是负数,如果只有一个操作数有符号,那么结果就是负数。如果商超过了ECMAScript数值的表示范围,则返回Infinity或者-Infinity;
如果一个操作数是NaN,则结果是NaN;
如果Infinity和Infinity相除,则结果是NaN;
如果是零被零相除,则结果是NaN;
如果非零的有限数被零除,则结果是Infinity或者-Infinity,取决于有符号操作数的符号。
如果是Infinity被任何非零数整除,则结果是Infinity或者-Infinity,取决于有符号操作数的符号。
如果一个操作数不是数值,则在后台调用Number()将其转换为数值,然后在应用上面的规则。
3.求模
求模(余数)操作符有一个百分号%表示,用法如下:
var result = 26 % 5; // 等于1
与另外两个乘性操作符相类似,求模操作符遵循下列特殊规则来处理特殊的值:
如果操作数是数值,执行常规的除法运算,返回除的余数;
如果被除数是无穷大的值而除数是有限大的值,则结果返回NaN;
如果被除数是无穷大的值而除数是零,则结果是NaN;
如果Infinity被Infinity除,则结果是NaN;
如果被除数是有限大的值而除数是无穷大的值,则结果是被除数;
如果除数是零,则结果是零;
如果有一个操作数不是数值,则在后台调用Number()将其装换为数值,然后在应用上面的规则。
3.5.5 加性操作符
加法和减法这两个加性操作符应该说是编程语言中最常用的操作符了。但是在ECMAScript中,这两个操作符却都有一系列特殊行为。与乘性操作符类似,加性操作符也会在后台转换不同的数据类型。然而,对于加性操作符而言,相应的转换规则还稍微有一些复杂。
1.加法
加法操作符(+)的用法如下所示:
var result = 1 + 2;
如果两个数都是数值,执行常规的加法计算,然后根据下列规则返回结果:
如果一个操作数是NaN,则结果是NaN;
如果Infinity加Infinity,则结果是Infinity;
如果-Infinity加-Infinity,则结果是-Infinity;
如果Ifinity加-Infinity,则结果是NaN;
如果+0加+0,则结果是+0;
如果-0加-0,则结果是-0;
如果+0加-0,则结果是+0;
不过,有一个操作数是字符串,那么就应该应用如下的规则:
如果两个操作数都是字符串,则将第二个操作数与第一个操作数拼接起来;
如果只有一个操作数是字符串,则将另外一个操作数转换为字符串,然后再将两个字符串拼接起来。
如果一个操作数是对象、数值或者布尔值,则调用它们的toString()方法取得相应的字符串值,然后在应用前面关于字符串的规则。对于undefined和null,则分别调用String()函数并取得字符串”undefined”和”null”。
下面举几个例子:
var result = 5 + 5;
alert(result); // 10
var result2 = 5 + "5";
alert(result2); // "55"
以上代码演示了加法操作符在两种不同模式下的差别。第一行代码演示了正常的情况,即5+5=10(数值)。但是如果将一个操作数当成字符串“5”,结果就变成了”55”。因为第一个操作数也被转换为了字符串”5”。
忽视加法操作中的数据类型是ECMAScript编程中常见的错误。再来看一个例子。
var num1 = 5;
var num2 = 10;
var message = "The sum of 5 and 10 is " + num1 + num2;
alert(message);
在这个例子中,变量message的值是执行两个加法操作之后的结果。有人可能会为最后得到的字符串”The sum of 5 and 10 is 15”,但是实际结果却是“The sum of 5 of 10 is 510”。之所以会这样,就是因为每个加法操作是独立执行的。第一个加法操作将一个数值和字符串拼接了起来,结果是一个字符串。而第二个加法操作又用这个字符串去加另外一个数值,当然会得到一个字符串。如果先对这个数值执行算数计算,然后再将结果与字符串拼接起来,应该向下面这样使用圆括号:
var num1 = 5;
var num2 = 10;
var message = "The sum of 5 and 10 is" + (5 + 10);
alert(message);
在这个例子中,一对圆括号把两个数值括在了一起,这样就告诉了解析器先计算器结果,然后在将字符串和结果拼接起来。
2. 减法
减法操作符(-)是另外一个常用的操作符,器用法如下所示:
var result = 2 -1;
与加法操作符类似,ECMAScript中减法操作符在处理各种数据类型转换的时候,同样需要遵循一些特殊的规则,如下所示:
如果两个操作数都是数值,则执行常规的算术减法操作并返回结果;
如果一个操作数是NaN,则结果是NaN;
如果Infinity减Infinity,则结果是NaN;
如果-Infinity减-Infinity,则结果是NaN;
如果Infinity减-Infinity,则结果是Infinity;
如果-Infinity减Infinity,则结果是-Infinity;
如果+0减+0,则结果是+0;
如果+0减-0,则结果是-0;
如果-0减-0,则结果是+0;
如果有一个操作数是字符串、布尔值、null或者undefined,则现在后台调用Number()函数将其转换为数值,然后在更具前面的规则进行减法运算。如果转换的结果是NaN,则减法的结果是NaN;
如果有一个操作数是对象,则调用对象的valueOf()方法以取得表示该对象的数值。如果得到的值是NaN,则减法的结果是NaN。如果对象没有valueOf()方法,则调用toString()方法并将得到的字符串转换为数值。
var result1 = 5 - true; // 4,因为true转换成了1
var result2 = NaN - 1; // NaN
var result3 = 5 -3; // 2
var result4 = 5 - ""; // 5,因为""被转换成了0
var result5 = 5 - "2"; // 3,因为"2"倍被转换了了2
var result6 = 5 -null; // 5,因为null被转换成了0
3.5.6 关系运算符
小于(<)、大于(>)、小于等于(<=)和大于等于(>=)这几个关系操作符用于对两个数值进行比较,比较的规则与我们数学课上学的一样。这几个操作符都返回一个布尔值。如下面的例子所示:
var result1 = 5 > 3; // true
var result2 = 5 < 3; // false
与ECMAScript中的其他操作符一样,当关系操作符的操作数是非数值的时候,也要进行数据类型转换完成某些奇怪的操作。一下就是相应的规则:
如果两个操作数都是数值,则执行数值比较;
如果两个操作数都是字符串,则比较两个字符串对应的字符编码的值;
如果一个操作数是数值,则将另外一个操作数转换为数值,然后执行数值比较;
如果操作数是对象,则调用对象的valueOf()方法,用得到的结果按照前面的规则进行比较。如果对象没有valueOf()方法,则调用toString()方法,并用得到的结果根据前面的规则进行比较;
如果一个操作数是布尔值,则先将其转换为数值,然后在执行比较;
在使用关系操作符比较两个字符串的时候,会执行一种奇怪的操作。很多人都会认为,在比较字符串的时候,小于的意思是“在字母表的位置靠前”,而大于则意味着“在字母表的位置靠后”,但是实际上完全不是这么一回事。在比较字符串的时候,实际上比较的是两个字符串中对应位置的字符编码值。经过这一番的讨论之后,再返回一个布尔值。由于大写字母的字符编码全部小于小写字母的字符编码,因此,我们可以看到如下的奇怪现象。
var result = "Brick" < "alphabet"; // true
在这个例子中,字符串“Brick”被认为小于字符串“alphabet”。原因是字母B的字符编码为66,而字母a的编码是97。如果要正真的按照字母顺序比较字符串,就必须把两个字符串转换为相同的大小写形式,然后在执行比较,如下所示:
var result = "Brick".toLowerCase() < "alphabet".toLowerCase();
通过两个操作数都转换为小写形式,就可以得出”alphabet”按字母表顺序排在”Brick”之前的正确判断了。
另外一种奇怪的现象是发生在两个数字字符串的比较情况下,比如下面这个例子:
var result = "23" < "3"; // true
确实,当比较字符串“23”是否小于字符串“3”时,结果居然是true。这是因为两个操作数都是字符串,而字符串比较是比较字符编码。不过,向下面的例子中一样,将一个操作数改为数值,然后在进行比较,如下所示:
var result = "23" < 3;
此时字符串“23”会被转换成23,然后在与3进行比较,因此就会得到合理的结果。在比较数值和字符串的时候,字符串会被转换成数值,然后数值的方式和另一个数值进行比较。当然这个规则对前面的例子是适用的。可是,如果那么字符串不能被转换为合理的数值呢。比如:
var result = "a" < 3;
由于字母”a”不能转换为合理的数值,因此就被转换成了NaN。根据规则,任何操作数与NaN进行关系比较,结果都是false。于是就出现了下面这个有意思的现象。
var result1 = NaN < 3; // false
var result2 = NaN >= 3; // false
按照常理,如果一个值不小于另一个值,则一定大于或者等于那个值。然而,在与NaN进行比较的时候,这两个操作的结果都返回了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。
下表列出了一些特殊的情况以及比较结果:
表达式 | 值 | 表达式 | 值 |
---|---|---|---|
null == undefined | true | true == 1 | true |
“NaN” == NaN | false | true == 2 | false |
5 == NaN | false | undefined == 0 | false |
NaN == NaN | false | null == 0 | false |
NaN != NaN | true | “5” == 5 | true |
false == 0 | true |
2.全等和全不等
除了在比较之前不转换操作数之外,去等和全不等与相等和不相等操作符没有什么区别。全等操作符由3个等号”===”表示,它只在两个操作数未经转换就相等的情况下才返回true,如下面的例子所示:
var result1 = ("55" == 55); // true
var result2 = ("55" === 55); // false
在这个例子中,第一个比较使用的是相等操作符比较字符串”55”和数值55,结果返回了true,然后再与另一个数值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;
本质上,这行代码的含义就是基于对boolean_expression求值的结果,决定给变量vaiable赋什么值。如果求值结果为true,则给变量variable赋值true_value值;如果求值结果为false,则给变量variable符false_value值。
3.5.9 赋值操作符
简单的赋值操作符由等号(=)表示,其作用就是把右侧的值赋个左边的变量。如下面的例子所示:
var num = 10;
如果在等号(=)前面再添加乘性操作符、加性操作符或者位操作符,就可以完成复合赋值操作。这种复合赋值操作相当于对下面常规表达式的简写形式:
var num = 10;
var num = num + 10;
其中的第二行可以用一个复合赋值来代替:
var num = 10;
num += 10;
每个主要算数运算符都有对应的复合赋值操作符。这些操作符如下所示:
乘/赋值(*=);
除/赋值(/=);
模/赋值(%=);
加/赋值(+=);
减/赋值(-=);
左移/赋值(<<=);
…
设计这些操作符的主要目的是简化赋值操作。使用它们不会代码任何性能上的提升。
3.5.10 逗号操作符
使用逗号操作符可以在一条语句中执行多个操作,如下面的例子所示:
var num1 = 1,num2 = 2,num3 = 3;
逗号操作符多用于声明多个变量;但是除此之外,逗号操作符还可以用作赋值。在用于赋值的时候,逗号操作符总会返回表达式中的最后一项,如下面的例子所示:
var num = (5,1,4,8,0);
由于0是表达式的最后一项,因此num的值就是0。虽然逗号的这种使用方式不常见。但是这个例子可以帮助我们理解逗号这种行为。