# 表达式&运算符

操作符用于数据值,操作符包括算术操作符(如加号和减号)、位操作符、关系操作符和相等操作符等。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);
Last Updated: 3/12/2023, 10:05:54 PM