# 运算符

运算符用于执行程序代码运算,会针对一个或以上操作数项目来进行运算。

JavaScript中的运算符主要分为:

  • 算术运算符
  • 关系(比较)运算符
  • 赋值运算符
  • 逻辑运算符
  • 一元运算符
  • 三元运算符
  • 特殊运算符
  • 指数运算符
  • 链判断运算符
  • Null 判断运算符
  • 逻辑赋值运算符

# 算数运算符

JavaScript中的算术运算符中有加(+)、减(-)、乘(*)、除(/)、取余(%)、自增(++)、自减(--)、求幂(**)

# 加法(+)

JavaScript中的+主要用于两个方面,一个是加法运算,另一个是用来进行字符串拼接,加法运算规则如下:

  1. 数字与除字符串以外类型相加: 将其他类型转化为数字后算术相加,无法转化则结果为NaN
    • true 转化为1
    • false、null转化为0
    • undefined 无法转化
  2. NaN与除字符串以外类型相加结果都为NaN
  3. 字符串与任意类型相加: 拼接
    1+false    //1
    '1'+1      //'11'
    NaN+'1'     //'NaN1'
    [1,2,3]+1    //"1,2,31"
    {a:2}+'1'    // 1
    

在开发中,我们经常需要将变量的值输出到某个字符串中,这时需要将变量与字符串进行拼接,字符串拼接有两种方式:

# 1. + 拼接

变量必须放到字符串引号外部,变量与字符串之间用+连接:

var name = "张三", age = 20, sex = "男";
alert( name + "的年龄是:" +age+ ",性别是:" +sex )
# 2. es6模板字符串 (ie不支持)

传统+拼接字符串相当繁琐不方便,ES6 引入了模板字符串解决这个问题

模板字符串用 反单引号(`) 标识,可以当作普通字符串使用,或者在字符串中嵌入变量:

var name = "张三", age = 20, sex = "男";
alert( `${name}的年龄是:${age},性别是:${sex}` )

模板字符串特点:

1. 模板字符串中嵌入变量,需要将变量名写在`${}`之中,变量与字符串不需要 `+`连接
2. `${}` 中可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性: `${num + 1}`
3. 模板字符串中可以换行
4. 模板字符串中可以随意使用单双引号

# 减法(-)

  1. 数字和字符串相减 :
    • 字符串全部由数字组成,将字符串转化为数字,再相减
    • 字符串不能转化为数字,结束是 NaN
    • null转化为0 undefined转化为NaN true为1 false为0
  2. 字符串和字符串相减 : 和数字和字符串相减规则相同
'11a'-1    //NaN
'11'-1     //10

# 加减法做数字与字符串的转换

  • 数字转化为字符串: num + ""
  • 字符串转化为数字: num - 0

# 乘法运算

  1. 数字与字符串运算:
    • 将字符串尝试着转化为数字,进行运算
    • 字符串不能转化为数字,结果是 NaN
  2. null转化为0 true为1 false为0 空字符串转为0
  3. Undefined乘所有数据类型的都为NaN
  4. 字符串和字符串运算 : 和数字和字符串运算规则相同
true*null      //0
'11'*'11'    // 121
'1a'*22    //NaN

# 除法运算

  1. 字符串类型 数字类型与 null 相除 结果为 Infinity
  2. 字符串类型、数字类型 null与 Undefined 相除 结果为 NaN
  3. 字符串类型 数字类型 null与 字符串类型、数字类型 null相除进行相应转换做除法运算
    • 字符串不能转为数字则结果为NaN
    • 空字符串转为0
11/null              //Infinity
'11'/'11'            // 1
'11a'/'11'            // NaN

# 取余(%)

取余,即取余数,如 10 % 3 则指 10除以3的余数

用于取一段范围的值
一般不用于小数,因为结果不确定(不精确)。

  1. Undefined 与所有数据类型的 取余 都为NaN
  2. 其余数据类型先进行转换,能转为数字则进行取余运算,否则为NaN
''%1        // 0
11%0        // NaN
11%null     // NaN

判断数字num是否是偶数:

if(num%2==0){
    alert(num + "是偶数")
}

# ++ 自加 -- 自减

规则: i++ 相当于变量i自身值变大1,类似i+=1; i--同理 ++可写在变量前或后: i++是先访问i然后再自增,而++i则是先自增然后再访问i的值

var i = 10;
console.log(i++);  // 10   先输出,后自增。 console.log(i);  i++;
console.log(++i);  // 11   先自增,后输出。 i++;  console.log(i);

# 求幂运算符

x**y 取x的y次方,同Math.pow(x, y)

3 ** 2  //9
效果同
Math.pow(3, 2) //9

# 关系(比较)运算符

比较运算符得到的结果 都是 boolean

主要有以下几种比较运算符

  • >
  • <
  • >=
  • <=
  • == (相等)
  • != (不相等)
  • === (全等)
  • !== (不全等)

# 比较规则

  1. 将比较的对象尝试转化为数字,能转化则按照数字比较,不能转化则为false
  2. NaN与任意数据比较结果均为 false
  3. 字符串和字符串比较, 比较第一个字符的unicode编码值,第一个字符要是相同,就比较第二个,依次往下(ASCII码表在最末尾)
    '10000' < '2'   //1的unicode值比2的unicode值小  true       
    '10000' > 2    //转成数字比较  true
    
  4. ==和全等===的区别
    • ==是只比较两边的值(如果两边类型不相同,则先尝试着转化,然后再比较,所以耗时比===多)
      • 0==undefined (false)
      • 0==null (false)
      • NaN == NaN (false)
      • undefined==null (true)
    • ===比较两边的值和类型,都相等才返回true;
  5. ===object.js()的区别
Object.is(+0,-0);               //false
+0===-0                         //true
Object.is(NaN,NaN);             //true
NaN===NaN                       //false

# 赋值运算符

=、+=、-=、*=、/=、%=、**= 运算符右边的值赋给左边的变量

运算符 实例 等价于
= a=25 a=25
+= a+=25 a=a+25
-= a-=25 a=a-25
*= a*=25 a=a*25
/= a/=25 a=a/25
%= a%=25 a=a%25
**= a**=3 a=a^3

# 逻辑运算符

在JavaScript逻辑运算中,0、""、null、false、undefined、NaN都会判为false,其他都为true

  1. &&
    • 只要有一个是假,结果就是假
    • &&左侧是真,结果取右侧; 左侧假,结果取左侧值
  2. ||
    • 只要有一个是真,结果就是真
    • ||左侧是真,结果取左侧; 左侧假,结果取右侧值
  3. !
    • 取反
    • 结果是boolean

总结: 与或运算,哪一侧能决定表达式真假性,则结果取哪一侧。

例:

0 && 1    // 0     
0 || 1    // 1

1 && 2     // 2
1 || 2     // 1

!1     // false
!0     // true

# 逻辑运算符的应用:短路原则

与或运算左侧结果已经确定整个表达式真假性,则右侧不再计算

  • &&与运算:左侧是假,右侧不进行计算
  • ||或运算:左侧是真,右侧不进行计算

# 一元运算符

只能操作一个值的操作符就叫做一元操作符

  • new (创建对象) delete (删除对象上的内容)
  • typeof -(负号) +(正号)
  • instanceof (判断对象是否由一个构造函数实例化)

# 三元运算符

基于某些条件对变量进行赋值的条件运算符,

var iablename=(condition)?value1:value2   

condition为true则variablename取:前的值,也就是value1,否则取:后的值,也就是value2

# 三元运算符使用技巧

  1. 可以简化条件判断
var fee;
if(isMember){
    fee="¥2.00";
}else{
    fee="¥10.00";
}
fee=isMember ? "¥2.00" : "¥10.00";
  1. 三元运算符用来执行多个操作,用小括号包裹,再用逗号隔开语句。

# 特殊运算符

  • , 逗号 —— 分隔数据
var num1 = 1, num = 2, num3 = 3;
  • () 小括号运算符 —— 提升算术优先级
var i = 1 + 3 * 5;
console.log(i);   // 16
var l = (1 + 3) * 5;
console.log(l);   // 20

# 指数运算符 ES2016

ES2016 新增了一个指数运算符(**)。

2 ** 2 // 4
2 ** 3 // 8

这个运算符的一个特点是右结合,而不是常见的左结合。多个指数运算符连用时,是从最右边开始计算的。

// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512

上面代码中,首先计算的是第二个指数运算符,而不是第一个。

指数运算符可以与等号结合,形成一个新的赋值运算符(**=)。

let a = 1.5;
a **= 2;
// 等同于 a = a * a;

let b = 4;
b **= 3;
// 等同于 b = b * b * b;

# 链判断运算符 ?. ES2020

编程实务中,如果读取对象内部的某个属性,往往需要判断一下,属性的上层对象是否存在。比如,读取message.body.user.firstName这个属性,安全的写法是写成下面这样。

// 错误的写法
const  firstName = message.body.user.firstName || 'default';

// 正确的写法
const firstName = (message
  && message.body
  && message.body.user
  && message.body.user.firstName) || 'default';

简化的写法

const firstName = message?.body?.user?.firstName || 'default';
const fooValue = myForm.querySelector('input[name=foo]')?.value

?.运算符,直接在链式调用的时候判断,左侧的对象是否为nullundefined。如果是的,就不再往下运算,而是返回undefined

// 下面是判断对象方法是否存在,如果存在就立即执行的例子。
iterator.return?.()

# Null 判断运算符 ??ES2020

读取对象属性的时候,如果某个属性的值是null或undefined,有时候需要为它们指定默认值。常见做法是通过||运算符指定默认值。

只要属性的值为nullundefined,默认值就会生效,但是属性的值如果为空字符串或false或0,默认值也会生效

const headerText = response.settings.headerText || 'Hello, world!';
const animationDuration = response.settings.animationDuration || 300;
const showSplashScreen = response.settings.showSplashScreen || true;

Null 判断运算符??。它的行为类似||,但是只有运算符左侧的值为null或undefined时,才会返回右侧的值。

const headerText = response.settings.headerText ?? 'Hello, world!';
const animationDuration = response.settings.animationDuration ?? 300;
const showSplashScreen = response.settings.showSplashScreen ?? true;

跟链判断运算符?.配合使用,为nullundefined的值设置默认值。

const animationDuration = response.settings?.animationDuration ?? 300;

多个逻辑运算符一起使用,必须用括号表明优先级,否则会报错。

// 报错
lhs && middle ?? rhs
lhs ?? middle && rhs
lhs || middle ?? rhs
lhs ?? middle || rhs

# 逻辑赋值运算符 ES2021

ES2021 引入了三个新的逻辑赋值运算符(logical assignment operators),将逻辑运算符与赋值运算符进行结合。

// 或赋值运算符
x ||= y
// 等同于
x || (x = y)

// 与赋值运算符
x &&= y
// 等同于
x && (x = y)

// Null 赋值运算符
x ??= y
// 等同于
x ?? (x = y)
// 这三个运算符||=、&&=、??=相当于先进行逻辑运算,然后根据运算结果,再视情况进行赋值运算。

它们的一个用途是,为变量或属性设置默认值。

// 老的写法
user.id = user.id || 1;

// 新的写法
user.id ||= 1;

// 老的写法
function example(opts) {
  opts.foo = opts.foo ?? 'bar';
  opts.baz ?? (opts.baz = 'qux');
}

// 新的写法
function example(opts) {
  opts.foo ??= 'bar';
  opts.baz ??= 'qux';
}

# 运算符的优先级

下表中的运算符按从最高到最低的优先级列出。具有相同优先级的运算符按从左至右的顺序求值。

运算符 运算符类型
(…) 圆括号
….…, …[…] ,new…(…),…(…) 成员访问,需计算的成员访问,new(带参数列表),函数调用
new … new(无参数列表)
…++, …-- 后置递增(运算符在后) 后置递减(运算符在后)
!…,~…,+…,-…,++…,--…,typeof…,void…,delete…,await… 逻辑非 按位非 一元加法 一元减法 前置递增 前置递减 typeof void delete await
…**…
…*… …/… …%… 乘法 除法 取余
+ - 加法 减法
< <= > >= in instanceof 小于 小于等于 大于 大于等于 in 判断是否为这个构造函数实例化
== != === !== 等号 非等 全等 非全等
&& 逻辑与
¦¦ 逻辑或
… ? … : … 三元运算符
= += -= *= /= %= **= 赋值
, 逗号

ASCII码表