《JavaScript高级程序设计》第三版 笔记
3.5 操作符
用于操作数据值的操作符:
算术操作符、位操作符、关系操作符和相等操作符。
字符串、数字值、布尔值、对象都可以应用操作符。应用操作符后都会变成数值变量。
在应用于对象时,相应的操作符通常都会调用对象的 valueOf() 和(或) toString() 方法,以便取得可以操作的值。
3.5.1 一元操作符
只能操作一个值的操作符叫做一元操作符。
一元操作符是 ECMAScript 中最简单的操作符。
1. 递增和递减操作符
递增和递减操作符各有两个版本-前置型 和 后置型。
这 4 个操作符对任何值都适用,除了整数还可以用于字符串、布尔值、浮点数值和对象。
前置型应该位于要操作的变量之前。(例如 ++i)
后置型则应该位于要操作的变量之后。(例如 i- -)
*前置型和后置型的区别:
执行前置递增和递减操作时,变量的值都是在求值以前改变的(通常称为副效应)。由于前置型与执行语句的优先级相等,因此整个语句会从左至右被求值。
后置型是在包含它们的语句被求值之后才执行的。
example:
<!DOCTYPE html>
<html lang="zh-cmn-hans">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
window.onload = function(){
var num1 = 0;
++num1;
console.log(num1); //1
var num2 = 0;
var num3 = ++num2;
console.log(num3); //1
var num4 = 0;
console.log(++num4); //1
var num5 = 0;
num5++;
console.log(num5); //1
var num6 = 0;
var num7 = num6++;
console.log(num7); //0
var num8 = 0;
console.log(num8++); //0
for(var i=0;i<2;++i){
console.log(i); //0 , 1
}
for(var j=0;j<2;j++){
console.log(j); //0 , 1
}
}
</script>
</body>
</html>
递增、递减操作符应用于不同的值时遵循下列规则:
字符串:
1.若字符串包含有效数字字符,先将其转换为数字值,再执行加减 1 的操作。
2.若字符串不包含有效数字字符,将变量的值设置为 NaN 。
布尔值:
1.布尔值 false ,先将其转换为 0 再执行加减 1 的操作。
2.布尔值 true ,先将其转换为 1 再执行加减 1 的操作。
浮点数值
执行加减 1 的操作。
对象
先调用对象的 valueOf() 方法以取得一个可供操作的值,然后对该值应用前述规则;
如果结果是 NaN ,则在调用 toString() 方法后再应用前述规则。
2. 一元加和减操作符
在对非数值应用一元加、减操作符时,该操作符会像 Number() 转型函数一样对这个值执行转换。
3.5.2 位操作符
ECMAScript 中所有的数值都以 IEEE-754 64位格式存储。
位操作符会先将 64 位的值转换成 32 位的整数,然后执行操作,最后再将结果转换回 64 位。
符号位:
对于有符号的整数, 32 位中的前 31 位用于表示整数的值;
第 32 位用来表示整数的符号:0 表示正数, 1 表示负数;
这个表示符号的位叫做符号位。
符号位的值决定了其它位数值的格式。
正数以纯二进制格式存储:
31位中的每一位都表示2 的幂;
第一位(叫做位0)表示 20 ,第二位表示 21,以此类推;
没有用到的位以 0 填充,即忽略不计。
example:
数值18的二进制表示是:
0000 0000 0000 0000 0000 0000 0001 0010
或者更简洁的:10010
这是 5 个有效位,这 5 位本身就决定了实际的值。
负数同样以二进制码存储,但使用的格式是二进制补码:
计算数值二进制补码的步骤:
求这个数值绝对值的二进制码;
求二进制反码,即将 0 替换为 1 ,将 1 替换为 0 ;
得到的二进制反码加 1 。
example:
求 -18 的二进制码:
1.求18的二进制码:
0000 0000 0000 0000 0000 0000 0001 0010
2.求其二进制反码,即 0 和 1 互换:
1111 1111 1111 1111 1111 1111 1110 1101
3.二进制反码加 1 :
1111 1111 1111 1111 1111 1111 1110 1101
+1
——————————————————————————————
1111 1111 1111 1111 1111 1111 1110 1110
有符号整数和无符号整数
处理有符号整数时,是不能访问 位31 的。
ECMAScript 会尽力向我们隐藏所有这些信息;
即在以二进制字符串形式输出一个负数时,我们看到的只是这个负数绝对值的二进制码前面加上了一个 负号 。
example:
var num = -18;
alert(toString(2)); //-10010
默认情况下, ECMAScript 中的所有整数都是有符号整数;
不过,当然也存在无符号整数;
对于无符号整数来说,第 32 位不再表示符号,因为无符号整数只能是正数;
无符号整数的值可以更大,因为多出的一位不再表示符号,可以用来表示数值。
对数值应用位操作符时的后台转换过程
64 位的数值被转换成 32 位数值,然后执行位操作;
最后再将 32 位的结果转换回 64 位数值。
这个转换过程导致在对特殊的 NaN 和 Infinity 值应用位操作时,这两个值都会被当成 0 来处理。
如果对非数值应用位操作符,会先使用 Number() 函数将该值转换为一个数值(自动完成),然后再应用位操作,得到的结果将是一个数值。
1. 按位非 (NOT)
按位非操作符由一个波浪线(~)表示。
执行按位非的结果就是返回数值的反码。
按位非操作的本质–操作数的负值减 1 。
*按位非是在数值表示的最底层执行操作,因此速度更快。
2. 按位与 (AND)
按位与操作符由一个和号字符(&)表示;
它有两个操作数。
从本质上讲,按位与就是将两个数值的每一位对齐,然后根据只要两个对应数值中的一个为 0 则为 0 ,否则为 1的规则,对相同位置上的两个数执行 AND 操作。
3. 按位或 (OR)
按位或操作符由一个竖线符号(丨)表示。
同样也有两个操作数。
按位或遵循只要两个对应数值中的一个为 1 则为 1 ,否则为 0 的规则。
4. 按位异或 (XOR)
按位异或操作符由一个插入符号 (^) 表示。
也有两个操作数。
按位异或遵循两个对应数值相同则为 0 ,不同则为 1 的规则。
5. 左移
左移操作符由两个小于号 (<<) 表示;
这个操作符会将数值的所有位向左移动指定的位数。
左移之后,右侧多出的空位会以 0 来填充,以便得到的结果是一个完整的 32 位二进制数。
左移不会影响操作数的符号位。
如果将 -2 向左移动 5 位,结果将是 -64 ,而非 64 。
6. 有符号的右移
有符号的右移操作符由两个大于号 (>>)表示。
这个操作符会将数值向右移动,但保留符号位(即正负号标记)。
有符号的右移操作与左移操作恰好相反,即如果将 64 向右移动 5 位,结果将变回 2 。
移位过程中出现的空位(原数值的左侧、符号位的右侧),将使用符号位的值来填充所有空位,以便得到一个完整的值。
7. 无符号右移
无符号右移操作符由3个大于号 (>>>)表示。
这个操作符会将数值的所有 32 位都向右移动。
无符号右移以 0 来填充空位。
对正数来说,无符号右移 = 有符号右移。
对负数来说,结果就不一样了;
无符号右移操作符会把负数的二进制码当成正数的二进制码;
由于负数以其绝对值的二进制补码形式表示,因此会导致无符号右移之后的结果非常大。
3.5.3 布尔操作符
布尔操作符一共有 3 个:
非 (NOT) 、与 (AND) 、或 (OR)。
1.逻辑非
逻辑非操作符由一个叹号 (!) 表示。
可以应用于任何值,且都会返回一个布尔值。
逻辑非操作符会先将它的操作数转换为一个布尔值;
然后再对其求反。
逻辑非操作符也可以用于将一个值转换为与其对应的布尔值;
同时使用两个逻辑非操作符,就会模拟 Boolean() 转型函数的行为;
结果与对该值使用 Boolean() 函数相同。
逻辑非操作符遵循规则:
对象,返回false。
null,返回true。
空字符串,返回true。
非空字符串,返回false。
数值 0 ,返回 true。
任意非零数值(包括 Infinity),返回false。
NaN ,返回true。
undefined ,返回true。
2. 逻辑与
逻辑与操作符由两个和号 (&&)表示。
有两个操作数。
属于短路操作,即如果第一个操作数能够决定结果,就不会再对第二个操作数求值。
不能使用未定义的值,否则会导致错误。
可以应用于任何类型的操作数,但不一定都返回布尔值。
遵循只要有一个为 false 则为 false ,否则为 true 的规则。
在有一个操作数不是布尔值的情况下,逻辑与就不一定返回布尔值,此时它遵循下列规则:
第一个操作数是对象,则返回第二个操作数。
第二个操作数是对象,则只有在第一个操作数求值结果为 true 的情况下才返回该对象。
两个操作数都是对象,则返回第二个对象。
有一个操作数为 null ,返回 null。
有一个操作数为 NaN ,返回 NaN。
有一个操作数为 undefined ,返回 undefined。
+ 第一个操作数不为 false ,第二个操作数为 null ,返回 null。
第一个操作数不为 false ,第二个操作数为 NaN ,返回 NaN。
第一个操作数不为 false ,第二个操作数为 undefined ,返回 undefined。
3. 逻辑或
逻辑或操作符由两个竖线符号 (丨丨) 表示。
有两个操作数。
属于短路操作符。
不能使用未定义的值,否则会导致错误。
遵循只要有一个为 true 则为 true ,否则为 false 。
如果有一个操作数不是布尔值,则不一定返回布尔值,此时它遵循下列规则:
第一个操作数是对象,返回第一个操作数。
第一个操作数求值结果为 false ,返回第二个操作数。
两个操作数都是对象, 返回第一个操作数。
两个操作数都为 null ,返回 null。
两个操作数都为 NaN ,返回 NaN。
两个操作数都为 undefined ,返回 undefined 。
运用逻辑或避免为变量赋 null 或 undefined 值。
example:
var myObject = preferredObject 丨丨 backupObject;
当 preferredObject 值不为 null 时,就会赋值给 myObject;
如果 preferredObject 值为 null ,则 backupObject 的值会赋给 myObject。
3.5.4 乘性操作符
乘性操作符有 3 个:乘法、除法、求模 。
操作数为非数值的情况下会执行自动的类型转换(后台使用 Number() 将其转换为数值)。
1. 乘法 (*)
处理特殊值遵循规则:
如果乘积超过了数值表示范围,则返回 Infinity 或 -Infinity 。
如果有一个操作数是 NaN ,则结果是 NaN 。
Infinity * 0 = NaN。
Infinity 乘以非 0 数值,结果是 Infinity 或 -Infinity ,取决于有符号操作数的符号。
Infinity * Infinity = Infinity。
example:
console.log(Number.MAX_VALUE * -2); //-Infinity
console.log(2 * NaN); //NaN
console.log(Infinity * 0); //NaN
console.log(Infinity * 2); //infinity
console.log(Infinity * Infinity); //infinity
console.log(true * "to"); //NaN
console.log(true * ""); //0
2. 除法 (/)
处理特殊值遵循规则:
如果商超过了数值表示范围,则返回 Infinity 或 -Infinity 。
console.log(Number.MIN_VALUE / -2); //-0
如果有一个操作数是 NaN ,则结果是 NaN 。
console.log(NaN / 2) /NaN
Infinity / Infinity = NaN 。
console.log(Infinity / Infinity); //NaN
0 / 0 = NaN 。
console.log(0 / 0); //NaN
非 0 的有限数被零除,结果是Infinity 或 -Infinity ,取决于有符号操作数的符号。
console.log(0 / -2); //-0
Infinity 被任何非零数值除,结果是Infinity 或 -Infinity ,取决于有符号操作数的符号。
console.log(2 / Infinity); //0
console.log(true / "2"); //0.5
3. 求模 (%)
处理特殊值遵循规则:
有限大的数值 % 无穷大值 = NaN 。
console.log(2 / Infinity); //0
0 % 有限大的数值 = NaN 。
console.log(0 / 2); //0
Infinity % Infinity = NaN 。
console.log(Infinity % Infinity); //NaN
无穷大数值 % 有限大的数值 = 被除数 。
console.log(Infinity % 2); //NaN
被除数是 0 ,结果是 0 。
console.log(2 % 0); //NaN
console.log(true % "2"); //1
3.5.5 加性操作符
1. 加法 (+)
规则:
有一个操作数为 NaN ,结果为 NaN 。
Infinity + Infinity = Infinity 。
-Infinity + -Infinity = -Infinity 。
Infinity + -Infinity = NaN 。
+0 + +0 = +0 。
-0 + -0 = -0 。
+0 + -0 = +0 。
如果有一个操作数为字符串:
如果两个操作数都是字符串,则将第二个操作数与第一个操作数拼接起来。
如果只有一个操作数为字符串,则将另一个操作数转换为字符串,然后拼接起来。
如果有一个操作数是对象、数值或布尔值,则调用它们的 toString() 方法取得相应的字符串值,再应用前面的规则。
对于 undefined 和 null ,调用 String() 函数取得字符串 “undefined” 和 “null” 。
example:
console.log(1 + NaN); //NaN
console.log(Infinity + Infinity); //Infinity
console.log(-Infinity + -Infinity); //-Infinity
console.log(Infinity + -Infinity); //NaN
console.log(+0 + +0); //0
console.log(-0 + -0); //-0
console.log(+0 + -0); //0
console.log("5" + "10"); //510
console.log("5" + 10); //510
console.log(1 + true); //2
console.log("1" + true); //1true
console.log(1 + {name:"edward"}); //1[object Object]
console.log(1 + undefined); //NaN
console.log("1" + undefined); //1undefined
console.log(1 + null); //1
console.log("1" + null); //1null
console.log(null + undefined); //NaN
console.log("num" + 5 +10); //num510
console.log("num" + (5 + 10)); //num15
2. 减法 (-)
规则:
有一个操作数为 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() 方法(如果对象没有 valueOf() 方法,则调用其 toString() 方法并将得到的字符串转换为数值)取得表示该对象的数值。如果得到的值是 NaN,则减法的结果就是 NaN 。
example:
console.log(1 - NaN); //NaN
console.log(Infinity - Infinity); //NaN
console.log(-Infinity - -Infinity); //NaN
console.log(Infinity - -Infinity); //Infinity
console.log(-Infinity - Infinity); //-Infinity
console.log(+0 - +0); //0
console.log(+0 - -0); //0
console.log(-0 - -0); //0
console.log(1 - "2"); //-1
console.log(1- true); //0
console.log(1 - null); //1
console.log(1 - undefined); //NaN
console.log(1 - {age:15}); //NaN
3.5.6 关系操作符
小于(<)、大于(>)、小于等于(<=)、大于等于(>=)。
这几个操作符都返回一个布尔值 。
当操作数使用了了非数值时,进行数据转换的规则:
两个操作数都是字符串,则比较两个字符串对应的字符编码,即两个字符串中对应位置的每个字符的字符编码值。
(大写字母的字符编码全部小于小写字母的字符编码。
如果要按字母表顺序比较字符串,就必须把两个操作数转换为相同的大小写形式【全部大写或全部小写】,然后执行比较。)
在比较数值和字符串时,字符串都会被转换成数值。
任何操作数与NaN进行关系比较,结果都是 false 。
如果一个操作数是数值,则将另一个操作数转换为一个数值,执行数值比较。
如果一个操作数是对象,则调用这个对象的 valueOf() 方法,用得到的结果按照前面的规则执行比较。如果对象没有 valueOf() 方法,则调用 toString() 方法,并用得到的结果按照前面的规则执行比较。
如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较。
example:
console.log("----------------------");
console.log("ab" > "ba"); //false
console.log("a" > "A"); //true
console.log(1 > true); //false
console.log(1 > "2"); //false
console.log(1 > {age:2}); //false
console.log("2" > "3"); //false
console.log(2 > "3"); //false
console.log("23" > "3"); //false
console.log(23 > "3"); //true
console.log("a" > 1); //false
console.log(NaN < 3); //false
console.log(NaN >= 3); //false
3.5.7 相等操作符
相等 (==)和不相等 (!=) – 先转换 (强制转换)再比较。
全等 (===)和不全等 (!==) – 仅比较而不转换。
1. 相等和不相等
转换不同数据类型遵循规则:
如果有一个操作数是布尔值,则在比较之前先将其转换为数值 – false 转换为 0 ,true 转换为 1 。
如果一个操作符是字符串,另一个操作数是数值,则先将字符串转换为数值。
如果操作数是对象,另一个操作数不是,则调用对象的 valueOf() 方法,用得到的基本类型按照前面的规则进行比较。
这两个操作符在进行比较时要遵循下列规则:
null == undefined 。
要比较相等性之前,不能将 null 和 undefined 转换成其它任何值。
如果有一个操作数是 NaN ,则相等操作符返回 false (即使两个操作数都是 NaN 也一样),不相等操作符返回 true 。
如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回 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 |
example:
console.log(true == 1); //true
console.log("2" == 2); //true
console.log(2 == {age :2}); //false
console.log(null == undefined); //true
console.log(NaN == 2); //false
console.log(NaN == NaN); //false
console.log({name:"edward"} == {name:"edward"}); //false
var obj = {class:"3",age:9};
var obj2 = obj;
console.log(obj == obj2); //true
console.log("NaN" == NaN); //false
console.log(false == 0); //true
console.log(true == 2); //false
console.log(undefined == 0); //false
console.log(null == 0); //false
console.log("5" == 5); //true
2. 全等 (===) 和 不全等 (!==)
只在操作数未经转换就相等的情况下返回 true 。
null === undefined ; //false
3.5.8 条件操作符
variable = (num1 > num2) ? true_value : false_value;
问号前的表达式结果为 true ,则赋冒号前面的值,为 false 则赋冒号后面的值。
3.5.9 赋值操作符 (=)
var num = 10;
把右侧的值赋给左侧的变量。
在等号前面添加乘性、加性、位操作符,可以完成复合赋值操作。
num = num + 10;
可以用 num += 10;
代替,相当于常规表达式的简写形式。
复合赋值操作符:
乘/赋值(*=)
除/赋值(/=)
模/赋值(%=)
加/赋值(+=)
减/赋值(-=)
左移/赋值(<<=)
有符号右移/赋值(>>=)
无符号右移/赋值(>>>=)
可以简化赋值操作,但不会带来性能提升。
3.5.10 逗号操作符
逗号操作符多用于声明多个变量:
var num1=1, num2=2, num3=3;
除此之外,还可以用于赋值。
在用于赋值时,逗号操作符总会返回表达式中的最后一项:
var num = (1,2,3,4,5); //num值为5