操作符

168 阅读8分钟

关于操作符

  • 可用于操作数据值的操作符

一元操作符

  • 只操作一个值的操作符叫一元操作符

递增/递减操作符

  • ++
  • --
  • 分 前缀 和 后缀 - 前缀先递增/减再运算,后缀相反

一元加和减

  • 数学中的用途相同
  • +x 会隐式调用 Number 进行类型转换

注意点

  • 如作用于对象,则会调用其 valueOf 方法取得可以操作的值,如获取的值非原始类型,则调用其 toString 再进行操作
let o1 = {
  valueOf() {
    return 1
  }
}
let o2 = {
  valueOf() {
    return {}
  },
  toString() {
    return 2
  }
}

console.log(o1-- + 1); // 2
console.log(o2-- + 1); // 3

位操作符

  • 这个操作符用于数值的底层操作,也就是操作内存中代表数据的比特(位)(二进制)
  • 位操作符不直接应用于64位,而是先把值转换位32位
  • 第32位表示符号,0表示正,1表示负,这意味称为符号位,正值以真正的二进制格式储存,每一位都代表2的幂,第一位为2的0次幂
  • 负值以二补数(补码)储存
  • 在对 ECMA 中的数值应用位操作符时,后台会发生转换:64位数值转换为32位数值,操作后再将32位转换位64位储存,整个过程就像处理32位一样,但有一个副作用:NaNInfinity 在位操作中会被当成0处理
  • 如果应用于非数值时,会隐式使用 Number() 函数将该函数转换为数值
  • 好处:快

按位非(~)

  • 返回数值的一补数(反码)
const num1 = 25;    // 二进制 0000 0000 0000 0000 0000 0000 0001 1001
const num2 = ~num1; // 二进制 1111 1111 1111 1111 1111 1111 1110 0110
console.log(num2);  // -26

// 他的结果就像执行下面操作一样,反码的效果就是对数值取反减一
const num3 = 25;
const num4 = -num3 - 1;
console.log(num4);

按位与(&)

  • 将两个数的每一个对齐,然后按照下表规则,对每位执行对应的与操作
第一个数值的位第二个数值的位结 果
111
100
010
000
  • 在两个位都是 1 时返回 1,任何一位是 0 时返回 0
const res = 25 & 3;
console.log(res); // 1

/*
  025 = 0000 0000 0000 0000 0000 0000 0001 1001
  003 = 0000 0000 0000 0000 0000 0000 0000 0011
  ---------------------------------------------
  AND = 0000 0000 0000 0000 0000 0000 0000 0001
*/

按位或(|)

第一个数值的位第二个数值的位结 果
111
101
011
000
  • 至少一位是 1 时返回 1,两位都是 0 时返回 0
const res = 25 | 3;
console.log(res); // 27

/*
  25 = 0000 0000 0000 0000 0000 0000 0001 1001
  03 = 0000 0000 0000 0000 0000 0000 0000 0011
  ---------------------------------------------
  OR = 0000 0000 0000 0000 0000 0000 0001 1011
*/

按位异或(^)

第一个数值的位第二个数值的位结 果
110
101
011
000
  • 只在一位是 1 的时候返回1,两位相同时返回 0
const res = 25 ^ 3;
console.log(res); // 26

/*
  025 = 0000 0000 0000 0000 0000 0000 0001 1001
  003 = 0000 0000 0000 0000 0000 0000 0000 0011
  ---------------------------------------------
  XOR = 0000 0000 0000 0000 0000 0000 0001 1010
*/

左移(<<)

  • 按照指定的位数将数值的所在位向左移动

  • 注意:左移会保留所在操作数值的符号,比如 -2 左移 5 位,将得到 -64

const num1 = 2;
const num2 = num1 << 5; // 64
  
const num3 = -2;
const num4 = num3 << 5; // -64

/*
  02 = 0000 0000 0000 0000 0000 0000 0000 0010
  << = 0000 0000 0000 0000 0000 0000 0100 0000
                                        ------
                                        空位补0

  02 = 0000 0000 0000 0000 0000 0000 0000 0010 - 正码
  -2 = 1111 1111 1111 1111 1111 1111 1111 1110 - 2的补码
  << = 0000 0000 0000 0000 0000 0000 0100 0000 - 2左移5 - 64
  << = 1111 1111 1111 1111 1111 1111 1100 0000 - -2左移5 - 64的补码
*/

有符号右移(>>)

  • 按照指定的位数将数值的所在位向右移动,同时保留符号(正负)
  • 实际上是左移的逆运算
const num1 = 64;
const num2 = num1 >> 5; // 2
  
const num3 = -64;
const num4 = num3 >> 5; // -2

/*
      符号位
        -
  064 = 0000 0000 0000 0000 0000 0000 0100 0000
   >> = 0000 0000 0000 0000 0000 0000 0000 0010
         ------
         空位补0

  064 = 0000 0000 0000 0000 0000 0000 0100 0000 - 正码
  -64 = 1111 1111 1111 1111 1111 1111 1100 0000 - 64的补码
   >> = 0000 0000 0000 0000 0000 0000 0000 0010 - 64右移5 - 2
   >> = 1111 1111 1111 1111 1111 1111 1111 1110 - -64右移5 - 2的补码
*/

无符号右移(>>>)

  • 对于正数,无符号右移和有符号右移结果相同
  • 对于负数,他会将负数的二进制直接处理,并不会像上面这样转换成正数右移再转换为补码,所以右移之后结果相差很大
const num1 = 64;
const num2 = num1 >>> 5; // 2
  
const num3 = -64;
const num4 = num3 >>> 5; // 134,217,726

/*
  064 = 0000 0000 0000 0000 0000 0000 0100 0000
  >>> = 0000 0000 0000 0000 0000 0000 0000 0010
        ------
        空位补0

  -64 = 1111 1111 1111 1111 1111 1111 1100 0000 - 64的补码
                                         ------
                                         右移删
  >>> = 0000 01111 1111 1111 1111 1111 1111 110  - 134,217,726
        ------
        空位补0
*/

布尔操作符

逻辑非(!)

  • 应用于一个操作数,必定返回布尔值
  • 无论应用什么数据类型,都先将数值转换为布尔值,然后再取反
  • 可用 !! 将数值转换为布尔值,相当于调用了 Boolean()

逻辑与(&&)

  • 应用于两个操作数,不一定返回布尔值
  • 他是一种短路操作符,如果第一个操作数为 false,则永远不会对第二个操作数求值
  • 以下情况不会返回布尔值
    1. 第一个操作数是对象,返回第二个操作数
    2. 第二个操作数是对象,则只有第一个操作数求值为 true 才会返回对象
    3. 如果两个操作数都是对象,返回第二个操作数
    4. 只要有一个操作数是 null,则返回 null
    5. 只要有一个操作数是 NaN,则返回 NaN
    6. 只要有一个操作数是 undefined,则返回 undefined

逻辑或(||)

  • 应用于两个操作数,同样不一定返回布尔值
  • 同样是一种短路操作符,如果第一个操作数为 true,则永远不会对第二个操作数求值
  • 以下情况不会返回布尔值
    1. 第一个操作数是对象,返回第一个操作数
    2. 第一个操作数是 false,返回第二个操作数
    3. 如果两个操作数都是对象,返回第一个操作数 - 记住 1 即可
    4. 如果两个操作数都是 null,则返回 null - 记住 2 即可
    5. 如果两个操作数都是 NaN,则返回 NaN - 记住 2 即可
    6. 如果两个操作数都是 undefined,则返回 undefined - 记住 2 即可

乘性操作符

  • * / % 乘 除 取模
  • 跟数学运算没太大的区别
  • 都会隐形的用 Number() 转换类型
  • NaN 返回 NaN
  • 其他特殊的关于 NaNInfinity 使用时看一下,不赘述了

指数操作符

  • 好消息~ ES7 新增指数操作符
  • ** - Math.pow()
  • 还可以 **= 简写

加性操作符

  • 一般与数学运算没太大区别,其他与乘性操作符规则类似
  • 加法操作符有字符串拼接作用
  • 如有一位为对象,则先调用 valueOf(),如返回为引用类型,则调用 toString() 再进行规则,该规则为 Number() 的规则 - 5

关系操作符

  • < > <= >= - 用法与数学课上交的一样
  • 都返回布尔值
  • 如果比较两个字符串的对应字符编码,靠前原则,例如 'b' > 'abbb'
  • 有一位为 Number 时,另一位也会隐形调用 Number()
  • 其中一位为 NaN 时,返回 false

相等操作符

等于和不等于

  • 一般先进行类型转换,再确定操作数是否相等
  • true 转换为 1,false 转换为 0
  • 如果一位是字符串,另一位是数值,则尝试将字符串转换为数值
  • 一些特殊的
const obj = {
  valueOf() {
    return '123'
  }
}

console.log(obj == 123); // true 一位是对象,另一位不是时,隐式调用 valueOf

console.log(null == undefined); // true
console.log(NaN != NaN); // true
console.log(NaN == NaN); // false

全等和不全等

  • 与等于不等于不同的是,他们除了对比数值之外,还会进行类型比较

条件操作符

  • 三目运算符 - bol ? 执行true : 执行false
const str = true ? 'a' : 'b';

赋值与逗号操作符

const num = 1; // 右边赋值给左边

// 如果是对象,则把引用地址赋值
const obj1 = {}
const obj2 = obj1;
obj2.a = 1
console.log(obj1); // {a: 1}

const a = 1, b = 2, c = 3; // 逗号一般简写使用,虽说我本人不喜欢这样操作,看着难受
b = 4; // const报错

const num = (1, 2, 3, 4, 0); // 0 最后一项