javascript 操作符类型隐性转换
(一).一元操作符
只能操作一个值的操作符叫做一元操作符
1.递增和递减操作符
a. 在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减1的操作。字
符串变量变成数值变量。
eg: var a=‘1‘; ++a; //a 变成2
b.在应用于一个不包含有效数字字符的字符串时,将变量的值设置为NaN,字符串变量变成数值变量。
eg:var a=‘1aa‘;++a //a 变成NaN
c:在应用于布尔值false时,先将其转换为0再执行加减1的操作。布尔值变量变成数值变量。
eg:var a=false;++a //a 变成1
d:在应用于布尔值true时,先将其转换为1再执行加减1的操作。布尔值变量变成数值变量。
eg:var a=true;++a //a 变成2
e:在应用于浮点数值时,执行加减1的操作。
eg:var a=1.1;++a //a 变成2.1
f:在应用于对象时,先调用对象的valueOf()方法(第5章将详细讨论)以取得一个可供操作的
值。然后对该值应用前述规则。如果结果是NaN,则在调用toString()方法后再应用前述规
则。对象变量变成数值变量。
eg:var a=[];++a; //a变成1;内部操作原则:先把a调用valueOf()如果返回的是对象则再调用toString(),再对结果转换为
Number按照一元操作符计算
由此可见对于一元操作符来说都是先把变量转换为Number类型 ,再进行计算。先Number(变量),再计算。
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
一元减操作符主要用于表示负数,例如将1转换成-1
在将一元减操作符应用于数值时,该值会变成负数。而当应用于非数值时,
一元减操作符遵循与一元加操作符相同的规则,最后再将得到的数值转换为负数,如下面的例子所示:
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
一元加和减操作符主要用于基本的算术运算,也可以像前面示例所展示的一样用于转换数据类型。
(二)位操作符
ECMAScript中的所有数值都以IEEE-754 64位格式存储,但位操作符并不直接操作64位的值。而是先将64位的值转换成32位
的整数,然后执行操作,最后再将结果转换回64位。
对于有符号的整数,32位中的前31位用于表示整数的值(最后一位索引为0)。第32位用于表示数值的符号:0表示正数,1表示
负数。这个表示符号的位叫做符号位,符号位的值决定了其他位数值的格式。正数以纯二进制格式存储,31位中的每一位都
表示2的幂.没有用到的位以0 填充,即忽略不计。
如果对非数值应用位操作符,会先使用Number()函数将该值转换为一个数值(自动完成),然后再应用位操作。得到的结果
将是一个数值。
在对特殊的NaN和Infinity值应用位操作时,这两个值都会被当成0来处理
十进制转二进制算法为分步除2取余数,然后余数倒过来得到二进制结果。
例如:
2|__3__ 1 //余数为1
2|_1_ 1 //余数为1
0 //余数为0
余数倒过来为:011。即十进制3转为二进制为011
_
例如,数值18 的二进制表示是
00000000000000000000000000010010,或者更简洁的10010。
负数同样以二进制码存储,但使用的格式是二进制补码。计算一个数值的二进制补码,需要经过下列3个步骤:
(i) 求这个数值绝对值的二进制码(例如,要求?18的二进制补码,先求18的二进制码);
(ii) 求二进制反码,即将0替换为1,将1替换为0;
(iii) 得到的二进制反码加1。
要根据这3个步骤求得?18的二进制码,首先就要求得18的二进制码,即:
0000 0000 0000 0000 0000 0000 0001 0010
然后,求其二进制反码,即0和1互换:
1111 1111 1111 1111 1111 1111 1110 1101
最后,二进制反码加1:
1111 1111 1111 1111 1111 1111 1110 1101
1
---------------------------------------
1111 1111 1111 1111 1111 1111 1110 1110
这样,就求得了?18的二进制表示,即11111111111111111111111111101110。要注意的是,在处理有符号整数时,是不能访
问位31的。
ECMAScript会尽力向我们隐藏所有这些信息。换句话说,在以二进制字符串形式输出一个负数时,
我们看到的只是这个负数绝对值的二进制码前面加上了一个负号。如下面的例子所示:
var num = -18;
alert(num.toString(2)); // "-10010"
要把数值?18转换成二进制字符串时,得到的结果是"-10010"。这说明转换过程理解了二进制补码并将其以更合乎逻辑的形
式展示了出来。
1. 按位非(NOT)由一个波浪线(~)表示
执行按位非的结果就是返回数值的反码
var num1 = 25; // 二进制00000000000000000000000000011001
var num2 = ~num1; // 二进制11111111111111111111111111100110
alert(num2); // -26
这里,对25执行按位非操作,结果得到了?26。这也验证了按位非操作的本质:操作数的负值减1。
相当于num2 = -num1 -1
然以上代码也能返回同样的结果,但由于按位非是在数值表示的最底层执行操作,因此速度更快。
2. 按位与(AND)
按位与操作符由一个和号字符(&)表示,它有两个操作符数。从本质上讲,按位与操作就是将两
个数值的每一位对齐,然后根据下表中的规则,对相同位置上的两个数执行AND操作:
------------------------------------------
第一个数值的位 第二个数值的位 结 果
1 1 1
1 0 0
0 1 0
0 0 0
------------------------------------------
简而言之,按位与操作只在两个数值的对应位都是1时才返回1,任何一位是0,结果都是0。
下面看一个对25和3执行按位与操作的例子:
var result = 25 & 3;
alert(result); //1
可见,对25和3执行按位与操作的结果是1。为什么呢?请看其底层操作:
25 = 0000 0000 0000 0000 0000 0000 0001 1001
3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
AND = 0000 0000 0000 0000 0000 0000 0000 0001
原来,25和3的二进制码对应位上只有一位同时是1,而其他位的结果自然都是0,因此最终结果
等于1。
3. 按位或(OR)
按位或操作符由一个竖线符号(|)表示,同样也有两个操作数。按位或操作遵循下面这个真值表
------------------------------------------
第一个数值的位 第二个数值的位 结 果
1 1 1
1 0 1
0 1 1
0 0 0
------------------------------------------
按位或操作在有一个位是1的情况下就返回1,而只有在两个位都是0的情况下才返回0。
var result = 25 | 3;
alert(result); //27
25与3按位或的结果是27:
25 = 0000 0000 0000 0000 0000 0000 0001 1001
3 = 0000 0000 0000 0000 0000 0000 0000 0011
--------------------------------------------
OR = 0000 0000 0000 0000 0000 0000 0001 1011
这两个数值的都包含4个1,因此可以把每个1直接放到结果中。二进制码11011等于十进制值27。
4. 按位异或(XOR)
按位异或操作符由一个插入符号(^)表示,也有两个操作数。以下是按位异或的真值表。
------------------------------------------
第一个数值的位 第二个数值的位 结 果
1 1 0
1 0 1
0 1 1
0 0 0
------------------------------------------
按位异或与按位或的不同之处在于,这个操作在两个数值对应位上只有一个1时才返回1,如果对
应的两位都是1或都是0,则返回0。
对25和3执行按位异或操作的代码如下所示:
var result = 25 ^ 3;
alert(result); //26
25与3按位异或的结果是26,其底层操作如下所示:
25 = 0000 0000 0000 0000 0000 0000 0001 1001
3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
XOR = 0000 0000 0000 0000 0000 0000 0001 1010
5. 左移
移操作符由两个小于号(<<)表示,这个操作符会将数值的所有位向左移动指定的位数。例如,
如果将数值2(二进制码为10)向左移动5位,结果就是64(二进制码为1000000),代码如下所示:
var oldValue = 2; // 等于二进制的10
var newValue = oldValue << 5; // 等于二进制的1000000,十进制的64
在向左移位后,原数值的右侧多出了5个空位。左移操作会以0来填充这些空位,以便得到
的结果是一个完整的32位二进制数
左移不会影响操作数的符号位。换句话说,如果将?2向左移动5位,结果将是?64,而非64
6. 有符号的右移
有符号的右移操作符由两个大于号(>>)表示,这个操作符会将数值向右移动,但保留符号位(即
正负号标记)。有符号的右移操作与左移操作恰好相反,即如果将64向右移动5位,结果将变回2:
var oldValue = 64; // 等于二进制的1000000
var newValue = oldValue >> 5; // 等于二进制的10 ,即十进制的2
同样,在移位过程中,原数值中也会出现空位。只不过这次的空位出现在原数值的左侧、符号位的
右侧,而此时ECMAScript会用符号位的值来填充所有空位,以便得到一个完整的值
7. 无符号右移
无符号右移操作符由3个大于号(>>>)表示,这个操作符会将数值的所有32位都向右移动。对正
数来说,无符号右移的结果与有符号右移相同。仍以前面有符号右移的代码为例,如果将64无符号右
移5位,结果仍然还是2:
ar oldValue = 64; // 等于二进制的1000000
var newValue = oldValue >>> 5; // 等于二进制的10 ,即十进制的2
但是对负数来说,情况就不一样了。首先,无符号右移是以0来填充空位,而不是像有符号右移那
样以符号位的值来填充空位。所以,对正数的无符号右移与有符号右移结果相同,但对负数的结果就不
一样了。其次,无符号右移操作符会把负数的二进制码当成正数的二进制码。而且,由于负数以其绝对
值的二进制补码形式表示,因此就会导致无符号右移后的结果非常之大,如下面的例子所示:
var oldValue = -64; // 等于二进制的11111111111111111111111111000000
var newValue = oldValue >>> 5; // 等于十进制的134217726
这里,当对?64执行无符号右移5位的操作后,得到的结果是134217726。之所以结果如此之大,
是因为?64的二进制码为11111111111111111111111111000000,而且无符号右移操作会把这个二进制码当
成正数的二进制码,换算成十进制就是4294967232。如果把这个值右移5 位,结果就变成了
00000111111111111111111111111110,即十进制的134217726。
(三)布尔操作符
布尔操作符一共有3个:非(NOT)!、与(AND)&& 和或(OR)
1.逻辑非
可以应用于ECMAScript中的任何值,无论这个值是什么数据类型,这个操作符都会返回一个布尔值
逻辑非操作符首先会将它的操作数转换为一个布尔值,然后再对其求反。
逻辑非操作符遵循下列规则:
a.如果操作数是一个对象,返回false;
b.如果操作数是一个空字符串,返回true;
c.如果操作数是一个非空字符串,返回false;
d.如果操作数是数值0,返回true
e.如果操作数是任意非0数值(包括Infinity),返回false;
f.如果操作数是null、NaN、undefined,返回true;
而同时使用两个逻辑非操作符,实际上就会模拟Boolean()转型函数的行为
第一个逻辑非操作会基于无论什么操作数返回一个布尔值,而第二个逻辑非操作则对该布尔值求反,于是就得到了这个值真
正对应的布尔值,最终结果与对这个值使用Boolean()函数相同
2. 逻辑与( && )
逻辑与操作可以应用于任何类型的操作数,而不仅仅是布尔值。在有一个操作数不是布尔值的情况下,逻辑与操作就不一定
返回布尔值
逻辑与操作属于短路操作,即如果第一个操作数能够决定结果,那么就不会再对第二个操作数求值。对于逻辑与操作而言,
如果第一个操作数是false,则直接返回第一个操作数不执行第二个操作数。
eg: true && dd //报错dd is not defined
false && dd //不会报错 ,因为false直接返回了,不会对与操作符右侧操作数求值(取值)了
3. 逻辑或( || )
逻辑或操作符也是短路操作符。也就是说,如果第一个操作数的求值结果为true,就不会对第二个操作数求值了
eg: true || dd //不会报错 ,true 直接返回了dd不会被求值
(四). 乘性操作符
ECMAScript定义了3个乘性操作符:乘法、除法和求模。在操作数为非数值的情况下会执行自动的类型转换
如果参与乘性计算的某个操作数不是数值,后台会先使用Number()转型函数将其转换为数值
Infinity 无穷,-Infinity 负无穷 。Number(Infinity) 结果还是Infinity
a . 乘法
i:如果乘积超过了ECMAScript数值的表示范围,则返回Infinity或-Infinity;
ii:如果有一个操作数是NaN,则结果是NaN
iii:如果是Infinity与0相乘,则结果是NaN;
iv:如果是Infinity与非0数值相乘,则结果是Infinity或-Infinity,取决于有符号操作数的符号;
v:如果是Infinity与Infinity相乘,则结果是Infinity
vi:如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后再应用上面的规则
b. 除法
i:如果操作数都是数值,执行常规的除法计算,即两个正数或两个负数相除的结果还是正数,而如果只有一个操作数有符号
,那么结果就是负数。如果商超过了ECMAScript数值的表示范围,则返回Infinity或-Infinity
ii:如果有一个操作数是NaN,则结果是NaN;
iii:如果是Infinity被Infinity除,则结果是NaN;
iv:如果是零被零除,则结果是NaN;
v:如果是非零的有限数被零除,则结果是Infinity或-Infinity,取决于有符号操作数的符号;
vi:如果是Infinity被任何非零数值除,则结果是Infinity或-Infinity
c. 求模
求模(余数)操作符由一个百分号(%)表示
i:如果被除数是无穷大值而除数是有限大的数值,则结果是NaN
ii:如果被除数是有限大的数值而除数是零,则结果是NaN
iii:如果是Infinity被Infinity除,则结果是NaN
iv:如果被除数是有限大的数值而除数是无穷大的数值,则结果是被除数
v:如果被除数是零,则结果是零
(五)加性操作符
加法和减法都属于加性操作符,与乘性操作符类似,加性操作符也会在后台转换不同的数据类型
a. 加法
如果两个操作符都是数值,执行常规的加法计算,然后根据下列规则返回结果
i:如果有一个操作数是NaN,则结果是NaN;
ii:如果是Infinity加Infinity,则结果是Infinity;
iii:如果是-Infinity加-Infinity,则结果是-Infinity
iv:如果是Infinity加-Infinity,则结果是NaN;
v:如果是+0加+0,则结果是+0,如果是-0加-0,则结果是-0,如果是+0加-0,则结果是+0。
不过,如果有一个操作数是字符串,那么就要应用如下规则
i:如果两个操作数都是字符串,则将第二个操作数与第一个操作数拼接起来
ii:如果只有一个操作数是字符串,则将另一个操作数转换为字符串,然后再将两个字符串拼接起来。
如果有一个操作数是对象、数值或布尔值,则调用它们的toString()方法取得相应的字符串值,然后再应用前面关于字符串
的规则。对于undefined和null,则分别调用String()函数并取得字符串"undefined"和"null"。
(true + 1 ==2)
b.减法
与加法操作符类似,ECMAScript中的减法操作符在处理各种数据类型转换时,同样需要遵循一些特殊规则,如下所示:
i:如果两个操作符都是数值,则执行常规的算术减法操作并返回结果;
ii:如果有一个操作数是NaN,则结果是NaN;
iii:如果是Infinity减Infinity,则结果是NaN;
iv:如果是-Infinity减-Infinity,则结果是NaN;
v:如果是Infinity减-Infinity,则结果是Infinity;
vi:如果是-Infinity减Infinity,则结果是-Infinity
vii:如果是+0减+0,则结果是+0;如果是+0减-0,则结果是-0;如果是-0减-0,则结果是+0
viii:如果有一个操作数是字符串、布尔值、null或undefined,则先在后台调用Number()函数将其转换为数值,然后再根据
前面的规则执行减法计算。如果转换的结果是NaN,则减法的结果就是NaN;
ivv:如果有一个操作数是对象,则调用对象的valueOf()方法以取得表示该对象的数值。如果得到的值是NaN,则减法的结果
就是NaN。如果对象没有valueOf()方法,则调用其toString()方法并将得到的字符串转换为数值。
(六) 关系操作符
小于(<)、大于(>)、小于等于(<=)和大于等于(>=)这几个关系操作符用于对两个值进行比较,这几个操作符都返回
一个布尔值
与ECMAScript中的其他操作符一样,当关系操作符的操作数使用了非数值时,也要进行数据转换或完成某些奇怪的操作。以
下就是相应的规则。
i.如果两个操作数都是数值,则执行数值比较。
ii.如果两个操作数都是字符串,则比较两个字符串对应的字符编码值
iii.如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较。
iv.如果一个操作数是对象,则调用这个对象的valueOf()方法,用得到的结果按照前面的规则执行比较。如果对象没有
valueOf()方法,则调用toString()方法,并用得到的结果根据前面的规则执行比较。
v.如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较。
eg:var result = "23" < "3"; //true
var result = "23" < 3; //false
字符串"23"会被转换成数值23,然后再与3进行比较,因此就会得到合理的结果。
var result = "a" < 3; // false,因为"a"被转换成了NaN
( 七 ) 相等操作符
相等和不相等——先转换再比较,全等和不全等——仅比较而不转换。
a. 相等(==)、不相等(!=)这两个操作符都会先转换操作数(通常称为强制转型),然后再比较它们的相等性。
在转换不同的数据类型时,相等和不相等操作符遵循下列基本规则:
i:如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false转换为0,而true转换为1;
ii:如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值;
iii:如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比
较;
iv: null和undefined是相等的
v:要比较相等性之前,不能将null和undefined转换成其他任何值。//undefined == 0 false;null == 0 false
vi:如果有一个操作数是NaN,则相等操作符返回false,而不相等操作符返回true
vii:如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回true
;否则,返回false。
b.全等(===)和不全等(!==)
在比较之前不转换操作数,不同数据类型的操作数都不全等
(八)条件操作符
variable = boolean_expression ? true_value : false_value;
基于对boolean_expression求值的结果,决定给变量variable赋什么值
(九)赋值操作符
简单的赋值操作符由等于号(=)表示,其作用就是把右侧的值赋给左侧的变量
如果在等于号(=)前面再添加乘性操作符、加性操作符或位操作符,就可以完成复合赋值操作
每个主要算术操作符(以及个别的其他操作符)都有对应的复合赋值操作符
乘/赋值(*=);
除/赋值(/=);
模/赋值(%=);
加/赋值(+=);
减/赋值(?=);
左移/赋值(<<=);
有符号右移/赋值(>>=);
无符号右移/赋值(>>>=)。
计这些操作符的主要目的就是简化赋值操作。使用它们不会带来任何性能的提升
(十)逗号操作符
使用逗号操作符可以在一条语句中执行多个操作:
var num1=1, num2=2, num3=3;
逗号操作符还可以用于赋值。在用于赋值时,逗号操作符总会返回表达式中的最后一项:
var num = (5, 1, 4, 8, 0); // num的值为0
参考资料:javascript高级程序设计(第三版)