# 表达式&运算符
操作符用于数据值,操作符包括算术操作符(如加号和减号)、位操作符、关系操作符和相等操作符等。JavaScript的操作符适用于很多值,例如字符串、数字值、布尔值,甚至对象。在应用于对象时,相应的操作符会调用对象的valueOf()和(或)toString()方法,来取得可以操作的值。
# 算数运算符
算术运算符以数值(字面量或变量)作为其操作数,并返回一个单个数值。
标准算术运算符是加法(+
),减法(-
),乘法(*
)和除法(/
)。
# 加法运算符(+)
加法运算符的作用是数值求和,或者字符串拼接。
// Number + Number -> 数字相加
1 + 2 // 3
// Number + +String -> 数字相加
1 + +"5" // 6 使用+将字符 “6” 转化成Number类型
// Boolean + Number -> 数字相加
true + 1 // 2
// Boolean + Boolean -> 数字相加
false + false // 0
// Number + String -> 字符串连接
5 + "foo" // "5foo"
// String + Boolean -> 字符串连接
"foo" + false // "foofalse"
// String + String -> 字符串连接
"foo" + "bar" // "foobar"
WARNING
[注意]在new Date()前面使用一元加符号,可以把日期字符串,转换为日期毫秒数
console.log(new Date()); //Wed May 27 2020 17:52:24 GMT+0800 (中国标准时间)
console.log(+new Date()); //1590573144095
# +转为数字
const toNumber = (val) => {
console.log(+val);
return +val;
}
// 传统数据类型
toNumber(null) // 0
toNumber(undefined) // NAN
toNumber(1) // 1
toNumber('123admin') // NAN
toNumber({}) // NAN
toNumber(true) // 1
// Es6的bigint symbol 【兼容性问题】
toNumber(10n) // TypeError: Cannot convert a BigInt value to a number
toNumber(Symbol.for(1)) // TypeError: Cannot convert a Symbol value to a number
// 有符号位移
const toNumber = (val) => {
return val >> 0;
}
// 无符号位移
const toNumber1 = (val) => {
return val >>> 0;
}
toNumber(null) // 0
toNumber({}) // 0
toNumber('10x') // 0
toNumber('10') // 10
// 当遇到超大的数时
toNumber(Number.MAX_SAFE_INTEGER) // -1
toNumber1(Number.MAX_SAFE_INTEGER) // 4294967295 高于32位不准确
// 转为二进制数
let max = Number.MAX_SAFE_INTEGER.toString(2);
console.log(max)
// 11111111111111111111111111111111111111111111111111111 // 53
let val = max.substring(0,32)
// 11111111111111111111111111111111 // 32
let num = parseInt(val,2) // 4294967295
// 十进制 => 二进制 :原码 => 反码+1 (补码)
// 二进制 => 十进制 :-1 => 反码 = 源码
// 最高位1是负数
# 减法(-)
减法运算符使两个操作数相减,结果是它们的差值。
5-3 //2
一元减运算符主要用于表示负数
当一元减运算符用于非数值时,会对该值使用Number()转型函数进行转换,再将得到的数值转换成负数
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
# 乘法 (*)
乘法运算符的结果是操作数的乘积。
2 * 2 // 4
-2 * 2 // -4
"foo" * 2 // NaN
Infinity * 0 // NaN 无穷*0
Infinity * Infinity // Infinity 无穷*无穷 = 无穷
# 除法 (/)
除法运算符的结果是操作数的商
1 / 2 // 在 JavaScript 中返回 0.5
1.0 / 2.0 // 在 JavaScript 或 Java 中都返回 0.5
2.0 / 0 // 在 JavaScript 中返回 Infinity
2.0 / 0.0 // 同样返回 Infinity
2.0 / -0.0 // 在 JavaScript 中返回 -Infinity
# 求余 (%)
求余运算符返回第一个操作数对第二个操作数的模
12 % 5 // 2
-1 % 2 // -1
NaN % 2 // NaN
1 % 2 // 1
2 % 3 // 2
-4 % 2 // -0
5.5 % 2 // 1.5
# 幂 (**)
幂运算符返回第一个操作数做底数,第二个操作数做指数的乘方
-4 ** 2;
// 其他语言中一般等于 -8
// 在 JavaScript 中是错误的,因为这会有歧义
// 正确写法
-(4 ** 2);
// -8
# 递增 (++)
递增运算符为其操作数增加1,返回一个数值。
- 如果使用后置(postfix),即运算符位于操作数的后面(如 x++),那么将会在递增前返回数值。
- 如果使用前置(prefix),即运算符位于操作数的前面(如 ++x),那么将会在递增
// 前置
var a = 2;
b = ++a;
// a = 3, b = 3
// 后置
var x = 3;
y = x++;
// y = 3, x = 4
# 递减 (--)
递减运算符将其操作数减去1,并返回一个数值。
- 如果后置使用(如 x--),则在递减前返回数值。
- 如果前置使用(如 --x),则在递减后返回数值。
// 前置
var a = 2;
b = --a; // a = 1, b = 1
// 后置
var x = 3;
y = x--; // y = 3, x = 2
# 赋值运算符
名称 | 简写形式 | 含义 |
---|---|---|
赋值 | x = y | x = y |
加赋值 | x += y | x = x + y |
减赋值 | x -= y | x = x - y |
乘赋值 | x *= y | x = x * y |
除赋值 | x /= y | x = x / y |
模赋值 | x %= y | x = x % y |
指数赋值 | x **= y | x = x ** y |
左移赋值 | x <<= y | x = x << y |
右移赋值 | x >>= y | x = x >> y |
无符号右移赋值 | x >>>= y | x = x >>> y |
按位与赋值 | x &= y | x = x & y |
按位异或赋值 | x ^= y | x = x ^ y |
按位或赋值 | x | = y | x = x | y |
# 比较(关系)操作符
运算符 | 说明 |
---|---|
> | 大于 |
< | 小于 |
>= | 大于或等于 |
<= | 小于等于 |
!= | 不等于 |
== | 强制类型转换比较 |
=== | 不强制类型转换比较 |
let a = 1,b = 2,c = '1';
console.log(a < b); //true
console.log(a == b); //false
console.log(a == c); //true
console.log(a === c); //false
console.log(a == true); //true
console.log(a === true); //false
# 条件运算符
这种类型的条件语句使用 ?
操作符来创建一个紧凑的if/else结构,
基本结构如下:
(name=="Tom") ? "hello Tom" : "hello unknow person";
这个语句可以理解为,如果名字是Tom,则”hello Tom” ,否则 “hello unknow person”
三元操作符是右结合的,也就是说可以把它链接起来, 和 if … else if… else if … else 链类似
function checkstate(val){
return val == 0?'使用中':(val == 1?'制作中':(val == 2?'已停用':''));
}
let result = checkstate(0);
result; // "使用中"
let result1 = checkstate(1);
result1; // "制作中"
let result2 = checkstate(2);
result2; // "已停用"
// 类似于下面代码
function checkstate(val) {
if(val == 0){
return "使用中";
}else if(val == 1){
return "制作中";
}else if(val == 2){
return "已停用中";
}else {
return ""
}
}
# 一元操作符
只能操作一个值的操作符叫做一元操作符。
++
--
-
+
~
!
delete
typeof
void
# 逗号操作符
逗号操作符分隔表达式并且按顺序执行表达式,通常逗号用于分隔变量声明,使得多个变量在一行之内声明。
var num1 = 1, num = 2, num3 = 3;
逗号运算符最常用的场景是在for循环中,这个for循环通常具有多个循环变量
// for循环中的第一个逗号是var语句的一部分
// 第二个逗号是逗号运算符
// 它将两个表达式(i++和j--)放在一条语句中
for(var i=0, j=10;i<j;i++,j--){console.log(i+j);}
逗号运算符还可以用于赋值,在用于赋值时,逗号运算符总是返回表达式中的最后一项
var num = (1,2,3,4,5);
console.log(num); //5 括号不能去掉 否则报错
返回右操作数的值
i = 0,j = 1,k = 2; // 返回2
//计算结果是2,它和下面的代码基本等价
i =0; j = 1; k = 2; // 返回2
# 逻辑运算符
TIP
逻辑运算符对操作数进行布尔运算,经常和关系运算符一样配合使用。逻辑运算符将多个关系表达式组合起来组成一个更复杂的表达式。逻辑运算符分为逻辑非 !
、逻辑与&&
、逻辑或 ||
3种
# 逻辑非
使用 !
符号表示逻辑非,即原来是true转变为false,反之亦然。
let a = true,b = false;
if (a && !b) {
console.log('表达式成立');
}
DANGER
可以应用于ECMAScript中的任何值。无论这个值是什么数据类型,这个操作符都会返回一个布尔值。逻辑非操作符首先会将它的操作数转换成一个布尔值,然后再对其求反。
TIP
逻辑非对操作数转为布尔类型的转换类型与Boolean()
转型函数相同,只不过最后再将其结果取反。而如果同时使用两个逻辑非操作符,实际上就会模拟Boolean()转型函数的行为
console.log(!!undefined);//false
console.log(!!null);//false
console.log(!!0);//false
console.log(!!-0);//false
console.log(!!NaN);//false
console.log(!!'');//false
console.log(!!false);//false
// bad
const bar = c ? true : false;
const baz = c ? false : true;
// ---避免不必要的三元表达式
// good
const bar = c ? true : false;
const baz = c ? false : true;
# 逻辑与(&&)
使用 &&
符号表示逻辑与,指符号两端都为 true
时表达式结果为 true。
# 逻辑与(&&)的真值表
第一个操作数 | 第二个操作数 | 结果 |
---|---|---|
true | true | true |
true | false | false |
false | true | false |
false | false | false |
TIP
逻辑与操作属于短路操作,如果第一个操作数能够决定结果,那么就不会再对第二个操作数求值。
对于逻辑与而言,如果第一个操作数是false,则无论第二个操作数是什么值,结果都是false,则返回第一个操作数;如果第一个操作数为true,则结果的真假和第二个操作数的真假相同,则返回第二个操作数
// 除了false、undefined、null、+0、-0、NaN、'' 这7个假值,其余都是真值
console.log('t' && ''); //因为't'是真值,所以返回''
console.log('t' && 'f'); //因为't'是真值,所以返回'f'
console.log('t' && 1 + 2); //因为't'是真值,所以返回3
console.log('' && 'f'); //因为''是假值,所以返回''
console.log('' && ''); //因为''是假值,所以返回''
逻辑与运算符可以多个连用,返回第一个布尔值为false的表达式的值
console.log(true && 'js' && '' && 4 && 'bool' && true); // ''
关系运算符的优先级比逻辑与(&&)和逻辑或(||)的优先级高,所以类似表达式可以直接书写,不用补充圆括号
if(a + 1 == 2 && b + 2 == 3){
//Todo
}
使用逻辑与运算符来取代 if
结构
if (a == b) {
doSomething();
}
// 等价于
(a == b) && doSomething();
逻辑与运算符常常用于回调函数使用中
//若没有给参数a传值,则a为默认的undefined,是假值,所以不执行a(),防止报错,如果给参数a传值,则执行函数a()
function fn(a){
if(a){ a(); }
}
//等价于
function fn(a){
a && a();
}
# 逻辑或(||)
使用 ||
符号表示逻辑或,符号左右两端有一方为true,表达式即成立。
let a = true,b = false;
if (a || b) {
console.log('表达式成立');
}
# 逻辑或(||)的真值表
第一个操作数 | 第二个操作数 | 结果 |
---|---|---|
true | true | true |
true | false | true |
false | true | true |
false | false | false |
TIP
逻辑或操作也属于短路操作,如果第一个操作数能够决定结果,那么就不会再对第二个操作数求值
对于逻辑或而言,如果第一个操作数是true,则无论第二个操作数是什么值,结果都是true,则返回第一个操作数;如果第一个操作数是false,则结果的真假和第二个操作数的真假相同,则返回第二个操作数
逻辑或运算符也可以多个连用,返回第一个布尔值为true的表达式的值
console.log(false || 0 || '' || 4 || 'jc' || true);// 4
逻辑或运算符常用于为变量设置默认值
//如果没有向参数p传入任何对象,则将该参数默认设置为空对象
function fn(p){
p = p || {};
}
# 优先级问题
下列中因为 && 的优先级高所以结果是 true。
console.log(true || false && false); // true
可以使用 () 来提高优先级
console.log((true || false) && false); // false
# 运算符的优先级
TIP
运算符优先级控制着运算符的执行顺序,优先级高的运算符的执行总是先于优先级运算符低的运算符
46个运算符总共分为14级的优先级,从高到低依次是:
++ -- - + ~ ! delete typeof void
* / %
+ -
<< >> >>>
< <= > >= instanceof in
== != === !==
&
^
|
&&
||
?:
= *= /= %= += -= &= ^= |= <<= >>= >>>=
,
由这14级的运算符优先级等级可以看出:
一元运算符 > 算术运算符 > 比较运算符 > 逻辑运算符 > 三元运算符 > 赋值运算符 > 逗号运算符
DANGER
[注意]逻辑取反运算符属于一元运算符,其优先级最高
示例
!2<1&&4*3+1;
- 先计算一元运算符!,!2;//false
//于是表达式变为
false < 1 && 4*3 + 1;
- 计算算术运算符4*3+1;//13
//于是表达式变为
false < 1 && 13;
- 计算比较运算符<,false<1;//true
//于是表达式变为:
true && 13; //13
# 短路
下例中 a
为真值,就已经知道结果了就不会再判断 b
的值了。
let a = true,b = false;
console.log(a || b); // true
同理当 a
值为假时,就已经可以判断 && 的结果了,就没有判断b
的必要了。
let a = false,b = true;
console.log(a && b); // false
使用短路特性赋值
let sex = prompt("你的性别是?") || "保密";
console.log(sex);