JS表达式运算

JS表达式运算

1. 表达式运算顺序

  • 运算顺序由运算符的优先级来定,通常情况下运算级越高,运算顺序越优先

  • 但是这样会导致一些错误:

    • let i = 0;
      console.log(i == 0 || i++ == 0);
      
    • ++优先级高于||,按照优先级运算则先计算i++,使i=2

    • 然后==优先级最高运算,左侧为false,不触发短路,计算右侧,返回flase,最后返回false

    • 但是结果为true

  • 正确的运算顺序:先将表达式根据优先级低的运算符进行拆分,再依次从左到右进行运算

    • let i = 0;
      console.log(i == 0 || i++ == 0);
      
      • ||优先级最低,通过||对表达式拆分为左侧i == 0和右侧i++ == 0
      • 计算左侧,成立返回true,触发短路,最后结果为true
    • console.log(([][[]] + [])[+!![]] + ([] + {})[+!![] + +!![]])
      
      • +优先级最低,拆分为左侧([][[]] + [])[+!![]]和右侧([] + {})[+!![] + +!![]]
      • 对左侧表达式继续拆分,分为[][[]][]!![]
        • 依次运算:
        • [][[]]表示数组[]下标为[]的值或者名字为[]的属性,为数字时表示下标,为字符串时表示属性,[]转换为原始类型为字符串''[]中不存在此属性,返回undefined
        • []进行+运算需要转换为原始类型,调用valueOf()仍为本身,调用toString()结果为''
        • !![]为二次取反,目的是把对应数据转换为布尔值,[]为对象类型,对应布尔值为true,取反为flase,再次取反为true数据前面使用!!,相当于将其转化为对应布尔值
        • 原表达式初步结果为(undefined + '')[+true]
        • undefined''进行运算,结果为'undefined'数据与''[]相加,相当于将其转换为对应字符串
        • true进行+运算先转换为number类型,为1原始类型数据前面使用+,相当于将其转化为对应number类型
        • 左侧表达式结果为'undefined'[1] = 'n'
      • 对右侧表达式继续拆分,分为[]{}+!![]
        • 依次运算:
        • []进行+运算需要转换为原始类型,调用valueOf()仍为本身,调用toString()结果为''
        • {}进行+运算需要转换为原始类型,调用valueOf()仍为本身,调用toString()结果为'[object Object]'
        • +!![]由上边逻辑可得1
        • 右侧表达式结果为('' + '[object object]')[1 + 1] = '[object object]'[2] = 'b'
      • 原表达式结果为'n' + 'b' = 'nb'

2. JS的数据存储

  • JS中number类型的存储方式为始终使用双精度浮点数,占8字节,64bit
  • 存储标准为国际IEEE 754标准
2.1 IEEE 754标准
  • 从逻辑上使用**三元组{S,E,M}**来表示一个数

    • S符号位,占1bit
    • E指数位,占11bit
    • M有效数字位,占52bit
  • S:符号位,位于存储单元首个bit位,1代表负数,0代表正数

  • E:指数位,位于存储单元第2~12个bit位

    • 指数位用于存储数据二进制的科学计数法所进位的指数数

    • 指数位存在正负

      • 当存储数据为小于1的小数时,指数进位为负

      • 存储方式:以11bit位存储指数,IEEE 754规定2的10次方-1(1024)对应为0此0-1-1此0+11

      • 1024 = 100 0000 0000,1023 = 011 1111 1111

      • 数据: 0.5 => 1 * 2(-1次方)
        指数: -1 => 1024-1-1 = 1022 = 011 1111 1110
        
      • 数据: 1 => 1 * 2(0次方)
        指数: 0 => 1024-1 = 1023 = 011 1111 1111
        
      • 数据: 2 => 1 * 2(1次方)
        指数: 1 => 1024-1+1 = 1024 = 100 0000 0000
        
  • M:数据位,位于存储单元第13~64位

    • 数据位采用科学计数法表示
  • 数据: 15.125 => (16-1).(1/8) => 1111.001 => 1.111001 * 2(3次幂)
    符号: S = 0
    指数: E = 3 => 1024-1+3 = 1026 = 100 0000 0010
    数据: M = 1110 0100 0000 0000 ~ 0000 (52)
    数据: 0100 0000 0010 1110 0100 0000 ~ 0000 (64)
    
2.2 32位有符号整数
  • JS在进行位运算时,不能直接使用64位的存储形式进行运算,而是先将数据转换为32位有符号整数,再将运算结果转换为64位浮点数

  • 转换规则:首位为1表示为负,首位为0表示正(原码)

    • 0   => 0000 0000 0000 0000 0000 0000 0000 0000
      
    • 1   => 0000 0000 0000 0000 0000 0000 0000 0001
      
    • 111 => 0000 0000 0000 0000 0000 0000 0110 1111
      
    • -1  => 1000 0000 0000 0000 0000 0000 0000 0001
      
    • -11 => 1000 0000 0000 0000 0000 0000 0000 1011
      
  • 运算规则:先转换为补码进行运算,再将结果转换为原码

    • 正数:补码 = 反码 = 原码

      • 1
        原码 => 0000 0000 0000 0000 0000 0000 0000 0001
        反码 => 0000 0000 0000 0000 0000 0000 0000 0001
        补码 => 0000 0000 0000 0000 0000 0000 0000 0001
        
      • 1
        原码 => 0000 0000 0000 0000 0000 0000 0110 1111
        反码 => 0000 0000 0000 0000 0000 0000 0110 1111
        补码 => 0000 0000 0000 0000 0000 0000 0110 1111
        
    • 负数:补码 = 反码 + 1 = ~原码 +1,

      • -1
        原码 => 1000 0000 0000 0000 0000 0000 0000 0001
        反码 => 1111 1111 1111 1111 1111 1111 1111 1110
        补码 => 1111 1111 1111 1111 1111 1111 1111 1111
        
      • -11 => 
        原码 => 1000 0000 0000 0000 0000 0000 0000 1011
        反码 => 1111 1111 1111 1111 1111 1111 1111 0100
        补码 => 1111 1111 1111 1111 1111 1111 1111 0101
        

3. + , - , * , / , %

3.1 +
  • + 是最常用的运算符,由于js是弱类型语言,所以在运行+运算的时候需要进行隐式类型转换

  • +号运算规则:(本文规则说明顺序即为优先级)

    • 与字符串进行运算,结果总为字符串类型

      • console.log('' + 1); // '1' 
        console.log('1' + 1); // '11'
        console.log('' + NaN); // 'NaN' 
        console.log('1' + NaN); // '1NaN' 
        
      • console.log('' + Infinity); // 'Infinity' 
        
    • 与对象类型进行运算,需要将对象类型转化为原始类型 : 先查看valueOf(),转化失败再查看toString(),转化失败则报错

      • [] 转化时通常其valueOf()为本身,其toString()[]替换为''

        • console.log([] + ''); // '' 查看[].valueOf(),仍为[],再查看[].toString(),结果'','' + '' = ''
          console.log([1,2,3] + ''); // '1,2,3' 同理,最后为 '1' + ''
          
      • 函数转化时通常其valueOf()为函数类型,其toString()为函数形式的字符串形式

        • 例:find函数的valueOf()ƒ find() { [native code] }toString()'function find() { [native code] }'

        • 与函数进行运算时,如果函数带(),则为调用函数,根据函数的返回值进行运算,如果无返回值则为NaN

        • 与函数进行运算时,如果函数不带(),则为与函数相加,根据函数的原始类型格式toString()进行运算

        • function sum(x , y) {
           return x + y
          }
          function add() {
            console.log(1)
          }
          
        • console.log(sum + 1); 
          /* 
          'function sum(x , y) {
           return x + y
          }1' 
          不带()为函数与1相加,调用toString()转化为字符串,与1相加
          */
          
        • console.log(sum() + 1); // NaN
          // 带()为调用函数与1相加,但未传参,x,y均为NaN,返回值为NaN,结果为NaN
          
        • console.log(sum(1) + 1); // NaN
          // 带()为调用函数与1相加,但未传参y,y为NaN,返回值为NaN,结果为NaN
          
        • console.log(sum(1,2) + 1); // 4
          // 带()为调用函数与1相加,传参x,y,返回值为3,结果为4
          
        • console.log(add + 1); 
          /*
          'function add() {
            console.log(1)
          }1'
          */
          
        • console.log(add() + 1); // NaN
          // 带()为调用函数与1相加,无返回值,调用结果为NaN,结果为NaN
          
        • console.log(sum + [1]);
          /* 
          'function sum(x , y) {
           return x + y
          }1'
          不带()为函数与数组相加,先调用函数toString(),再调用数组toString(),再将结果相加
          */
          
        • console.log(sum(1,2) + [1]); // '31'
          // 先调用函数返回3,再调用数组toString()结果为'1',再将结果相加
          
        • console.log(sum + NaN);
          /* 
          'function sum(x , y) {
           return x + y
          }NaN'
          */
          
        • console.log(sum + sum() + 1);
          /* 
          'function sum(x , y) {
           return x + y
          }NaN1'
          */
          
      • 对象转化时通常其valueOf()为对象类型,其toString()总是为字符串'[object Object]'

        • function sum(x , y) {
           return x + y
          }
          
        • console.log({x: 1, y: 2} + 1); // '[object Object]1'
          
        • console.log({x: 1, y: 2} + NaN); // '[object Object]NaN'
          
        • console.log({x: 1, y: 2} + [1]); // '[object Object]1'
          
        • console.log({x: 1, y: 2} + sum);
          /*
          [object Object]function sum(x , y) {
           return x + y
          }
          */
          
        • console.log({x: 1, y: 2} + sum()); // '[object Object]NaN'
          
        • console.log({x: 1, y: 2} + sum(1,2)); // '[object Object]3'
          
    • 与非字符串类型的原始类型进行运算,需要转化为number类型 : 使用Number()函数

      • Number(undefined) = NaN
        Number(null) = 0
        Number(true) = 1
        Number(false) = 0
        
      • console.log(undefined + 1); // Number(undefined)为NaN 结果为NaN
        
      • console.log(null + 1); // Number(null)为0 结果为1
        
      • console.log(true + 1); // Number(true)为1 结果为2
        
      • console.log(false + 1); // Number(false)为0 结果为1
        
    • NaN进行运算,结果总是为NaN

      • 所有数据运算最后都会转化为三种数据:字符串numberNaN

      • 影响结果顺序:字符串NaNnumber

      • NaN只会影响结果,但并不会阻止表达式继续运行

      • let i = 10
        console.log(NaN + i++); // 结果为NaN i = 11
        
    • 新增的原始类型BigInt

      • BigInt为新增的原始类型,表示为大数类型,支持的运算符与number一致
      • BigInt不支持一元运算符中的正数表示符号+
      • BigInt不能与number直接运算,会报错,包括非字符串类型的原始类型转化的number类型
      • 其他加法规则均遵守
    • 新增的Symbol类型数据无法进行+运算

  • 单独使用+对后面数据进行运算,+表示为,作用为将后面数据转换为number类型

    • 原始数据类型直接使用Number()进行转换

      • console.log(+ undefined); // NaN
        
      • console.log(+ null); // 0
        
      • console.log(+ NaN); // NaN
        
      • console.log(+ ''); // 0
        
      • console.log(+ '123'); // 123
        
      • console.log(+ '1,2,3'); // NaN
        
      • console.log(+ false); // 0
        
      • console.log(+ 6); // 6
        
    • 对象类型先转换为原始类型,再转换为number类型

      • console.log(+ []); // 0      [] => '' => 0
        
      • console.log(+ [1,2]); // NaN      [1,2] => '1,2' => NaN
        
      • console.log(+ sum()); // NaN      sum() => 'function sum() {...}' => NaN
        
      • console.log(+ {}); // NaN      {} => '[object object]' => NaN
        
    • BigInt类型数据无法使用一元运算符+

      • console.log(+ 2n); // 报错
        
    • Symbol类型数据无法进行运算

      • console.log(+ Symbol(1)); // 报错
        
3.2 -
  • -号在运算时也需要进行隐式转化

  • +号最终转化为字符串不同,-最终都需要转化为number类型

  • -号运算规则:

    • 与对象类型进行运算,需要将对象类型转化为原始类型,再将原始类型转化为number类型

      • [] 转化时通常其valueOf()为本身,其toString()[]替换为'',再将结果调用Number()来进行转化

      • console.log([] - 1); // [] => '' => 0 结果为-1
        console.log([1] - 1); // [1] => '1' => 1 结果为0
        console.log([1,2] - 1);// [1,2] => '1,2' => NaN 结果为NaN
        
    • 函数转化时通常其valueOf()为函数类型,其toString()为函数形式的字符串形式,再将结果调用Number()来进行转化

      • +相同,带()为调用函数,不带()为直接计算

      • 函数进行减法时,由于toString()后包含括号和函数体函数名,以及空格等无法转化为数字的字符,所以再转化为number类型会变成NaN

      • function sum(x , y) {
         return x - y
        }
        function add() {
          console.log(1)
        }
        
      • console.log(sum - 1); // NaN 转化为NaN,输出为NaN
        
      • console.log(sum() - 1); // NaN 函数结果为NaN,输出为NaN
        
      • console.log(sum(1) - 1); // NaN 函数结果为NaN,输出为NaN
        
      • console.log(sum(1,2) - 1); // -2 函数结果为-1,输出为-2
        
      • console.log(add - 1); // NaN 转化为为NaN,输出为NaN
        
      • console.log(add() - 1); // 1 NaN 函数结果为NaN,函数输出为1,输出为NaN
        
      • console.log(sum - []); // NaN 函数转化为NaN,数组转化为0,输出为NaN
        
      • console.log(sum - [1]); // NaN 函数转化为NaN,数组转化为1,输出为NaN
        
      • console.log(sum - [1,2]); // NaN 函数转化为NaN,数组转化为NaN,输出为NaN
        
      • console.log(sum(1,2) - [1]); // -2 函数结果为-1,数组转化为1,输出为-2
        
      • console.log(sum - sum() - 1); //NaN 函数转化为NaN,函数结果为NaN,输出为NaN
        
    • 对象转化时通常其valueOf()为对象类型,其toString()总是为字符串'[object Object]',再转化为number类型

      • 由于其toString()总是为字符串'[object Object]',再转化为number类型总是为NaN
    • 与原始类型进行运算,需要转化为number类型 : 使用Number()函数

      • Number(undefined) = NaN
        Number(null) = 0
        Number(true) = 1
        Number(false) = 0
        
      • 字符串中如果包含非数字字符,则转化为NaN

    • NaN进行运算,结果总是为NaN

    • 新增的原始类型BigInt,Symbol+规则一致

3.3 * / %
  • 这三个运算符用法与-相同,但需要注意:

  • 进行/运算时,当运算符之后的数字为0,则运算结果为Infinity,正负性根据运算符之前的数字确定

  • console.log(1 / 0); // Infinity
    
  • console.log(-1 / 0); // -Infinity
    
  • 进行%运算时,当运算符之后的数字为0,则运算结果为NaN

4. = , == , ===

  • =是赋值操作

  • ==是判断是否相等

    • ==两侧类型相同时:

      • NaN互不相等

        • console.log(NaN == NaN); // false
          
      • 原始类型直接比较值

        • console.log('' == '1'); // false
          
        • console.log(1 == 2); // false
          
        • console.log(true == false); // false
          
        • console.log(undefined == undefined); // true
          
        • console.log(null == null); // true
          
        • console.log(1n == 2n); // false
          
      • Symbol类型表示唯一标识符,具有唯一性,互不相等

        • console.log(Symbol(1) == Symbol(1)); // false
          
      • 对象类型比较:

        • 对象类型通常比较的为地址,指向的地址相同则返回true,否则返回false

        • const array1 = [];
          const array2 = [1];
          function sum(x , y) {
            return x + y
          }
          function sum2(x , y) {
            return x + y
          }
          const object1 = {x: 1, y: 2};
          const object2 = {x: 1, y: 2};
          
        • console.log([] == []); // 直接使用[]为定义数组,两个定义指向地址不同,返回false
          
        • console.log([1] == [1]); // 直接使用[]为定义数组,两个定义指向地址不同,返回false
          
        • console.log([1,2] == [1,2]); // 直接使用[]为定义数组,两个定义指向地址不同,返回false
          
        • console.log(array1 == array1); // 指向地址相同,返回true
          
        • console.log(array1 == array2); // 指向地址不同,返回false
          
        • console.log(sum == sum); // 指向地址相同,返回true
          
        • console.log(sum == sum2); // 指向地址不同,返回false
          
        • console.log(sum == sum()); // 左侧指向地址,右侧调用函数,返回false
          
        • console.log(sum() == sum()); // 调用函数根据返回值进行比较,此函数未传参返回值均为NaN,结果false
          
        • console.log({x: 1, y: 2} == {x: 1, y: 2});
          // 直接使用{}为定义数组,两个定义指向地址不同,返回false
          
        • console.log({x: 1, y: 2} == {x: 1, y: 2, z: 3});
          // 直接使用{}为定义数组,两个定义指向地址不同,返回false
          
        • console.log(object1 == object1); // 指向地址相同,返回true
          
        • console.log(object1 == object2); // 指向地址不同,返回false
          
    • ==两侧类型不同时:

      • 任何数据都与undefinednull不相等

        • console.log(undefined == 1); // false
          
        • console.log(null == 1); // false
          
      • undefinednull相等

        • console.log(null == undefined); // true
          
      • 两侧都为原始类型:转换为数字类型比较

        • console.log('' == 1); // '' => 0  false
          
        • console.log('1' == 1); // '1' => 1  true
          
        • console.log('fghjk' == 1); // 'fghjk' => NaN  false
          
        • console.log(true == 1); // true => 1  true
          
        • console.log(false == 1); // false => 0  false
          
      • 存在对象类型:转化为原始类型

        • console.log([] == 1); // [] => '' => 0 ,返回false
          
        • console.log([1] == 1); // [1] => '1' => 1 ,返回true
          
        • console.log([1,2] == 1); // [1,2] => '1,2' => NaN ,返回false
          
        • console.log(sum == 1);  // 左侧指向地址,右侧为1,返回false
          
        • console.log(sum() == 1); // 调用函数返回NaN,右侧为1,返回false
          
        • console.log(sum(1) == 1); // 调用函数返回NaN,右侧为1,返回false
          
        • console.log(sum(1,2) == 1); // 调用函数返回3,右侧为1,返回false
          
        • console.log(sum == []); // 左侧指向地址,右侧为0,返回false
          
        • console.log(sum == [1]); // 左侧指向地址,右侧为1,返回false
          
        • console.log(sum == [1,2]); // 左侧指向地址,右侧为NaN,返回false
          
        • console.log(sum(1,2) == [1]); // 调用函数返回3,右侧为1,返回false
          
        • console.log(sum == sum() == 1);
          // 先运算第一个==,左侧指向地址,右侧调用函数返回NaN,返回false
          // 再运算第二个==,左侧false转换为number类型为0,右侧为1,返回false
          
        • console.log({x: 1, y: 2} == 1); // 左侧指向地址,右侧为1,返回false
          
        • console.log({x: 1, y: 2} == NaN); // 左侧指向地址,右侧为NaN,返回false
          
        • console.log({x: 1, y: 2} == [1]); // 左侧指向地址,右侧为1,返回false
          
        • console.log({x: 1, y: 2} == sum); // 指向地址不同,返回false
          
        • console.log({x: 1, y: 2} == sum()); // 左侧指向地址,右侧调用函数返回NaN,返回false
          
        • console.log({x: 1, y: 2} == sum(1,2)); // 左侧指向地址,右侧调用函数返回3,返回false
          
  • ===是判断是否严格相等,不会进行类型转换,不同类型比较总会返回false

5. || , && , !

  • ||,&&,!属于逻辑运算符

  • ||逻辑或,至少一个条件成立返回true

    • 具有短路机制,第一个条件成立时,不进行第二个条件的判断,直接返回true

    • let i = 0;
      console.log(i == 0 || i++ == 0); // 第一个条件成立,返回true
      console.log(i); // 未进行第二个条件,i仍为0
      
  • &&逻辑与,至少两个条件成立返回true

  • !逻辑非,右侧表达式结果取反

    • 当右侧表达式不为布尔类型时,需要进行隐式类型转换:

    • 对象类型总是返回true

      • console.log(![1,2]); // false
        
      • console.log(!sum); // false
        
      • console.log(!{x: 1, y: 2}); // false
        
    • 非空字符串总是返回true

      • console.log(!'0'); // false
        
      • console.log(!'1'); // false
        
      • console.log(!'sfdg'); // false
        
      • console.log(!''); // true
        
    • 0number0n的大数类型总是返回true

      • console.log(!1); // false
        
      • console.log(!-1); // false
        
      • console.log(!10086); // false
        
      • console.log(!0); // true
        
    • Infinity-Infinity总是返回true

      • console.log(!Infinity); // false
        
      • console.log(!-Infinity); // false
        
    • Symbol类型总是返回true

      • console.log(!Symbol(1)); // false
        
    • 仅以下数值会返回false

      • console.log(!undefined); // true
        
      • console.log(!null); // true
        
      • console.log(!NaN); // true
        
      • console.log(!0); // true
        
      • console.log(!''); // true
        

6. | , & , ~ , ^

  • 这三个运算符属于位运算符,在运算时直接对二进制的数据进行运算
  • 由于计算机中数据是由二进制存储的,所以在进行位运算时效率极高
  • 进行位运算时,任何非number类型数据都将转换为number类型的有效数据,无法转换则置0
  • JS进行按位运算时需要先将数据转换为32位有符号整数(超出部分截断,小数部分舍弃),再将结果转换为64位浮点数
6.1 |
  • |或运算符表示进行运算的数据的二进制相同位置,只要有一个值为1则结果为1

    • console.log(3 | 9); // 11
      // 3 => 0000 0000 0000 0000 0000 0000 0000 0011
      // 9 => 0000 0000 0000 0000 0000 0000 0000 1001
      // ? => 0000 0000 0000 0000 0000 0000 0000 1011 => 11
      
    • console.log(3 | 6); // 7
      // 3 => 0000 0000 0000 0000 0000 0000 0000 0011
      // 9 => 0000 0000 0000 0000 0000 0000 0000 0101
      // ? => 0000 0000 0000 0000 0000 0000 0000 0111 => 7
      
    • console.log(3.1 | 0.2); // 3
      // 3.1 => 3 => 0000 0000 0000 0000 0000 0000 0000 0011
      // 0.2 => 0 => 0000 0000 0000 0000 0000 0000 0000 0000
      // ?        => 0000 0000 0000 0000 0000 0000 0000 0011 => 3
      
    • console.log(3.1 | 'lxl'); // 3
      // 3.1   => 3 => 0000 0000 0000 0000 0000 0000 0000 0011
      // 'lxl' => 0 => 0000 0000 0000 0000 0000 0000 0000 0000
      // ?          => 0000 0000 0000 0000 0000 0000 0000 0011 => 3
      
    • console.log(-1 | 9); // -1
      // -1 => 1000 0000 0000 0000 0000 0000 0000 0001 原码
      //    => 1111 1111 1111 1111 1111 1111 1111 1110 反码
      //    => 1111 1111 1111 1111 1111 1111 1111 1111 补码
      // 9  => 0000 0000 0000 0000 0000 0000 0000 1001 原反补相等
      // ?  => 1111 1111 1111 1111 1111 1111 1111 1111 补码
      //    => 1111 1111 1111 1111 1111 1111 1111 1110 反码
      //    => 1000 0000 0000 0000 0000 0000 0000 0001 原码 => -1
      
  • -1与任何数进行|或运算都得-1

  • 0与任何数进行|或运算都得本身

  • 1与奇数进行|或运算结果为本身,与偶数进行|或运算结果为本身+1

6.2 &
  • &与运算符表示进行运算的数据的二进制相同位置,只要有一个值为0则结果为0

    • console.log(3 & 9); // 1
      // 3 => 0000 0000 0000 0000 0000 0000 0000 0011
      // 9 => 0000 0000 0000 0000 0000 0000 0000 1001
      // ? => 0000 0000 0000 0000 0000 0000 0000 0001 => 1
      
    • console.log(3.1 & 0.2); // 0
      // 3.1 => 3 => 0000 0000 0000 0000 0000 0000 0000 0011
      // 0.2 => 0 => 0000 0000 0000 0000 0000 0000 0000 0000
      // ?        => 0000 0000 0000 0000 0000 0000 0000 0000 => 0
      
    • console.log(3.1 & 'lxl'); // 0
      // 3.1   => 3 => 0000 0000 0000 0000 0000 0000 0000 0011
      // 'lxl' => 0 => 0000 0000 0000 0000 0000 0000 0000 0000
      // ?          => 0000 0000 0000 0000 0000 0000 0000 0000 => 0
      
    • console.log(-1 & 9); // -1
      // -1 => 1000 0000 0000 0000 0000 0000 0000 0001 原码
      //    => 1111 1111 1111 1111 1111 1111 1111 1110 反码
      //    => 1111 1111 1111 1111 1111 1111 1111 1111 补码
      // 9  => 0000 0000 0000 0000 0000 0000 0000 1001 原反补相等
      // ?  => 0000 0000 0000 0000 0000 0000 0000 1001 补反原相等(符号变为0,正数) => 9
      
  • 任何数与0进行&与运算结果都为0

  • 任何数与-1进行&与运算结果都为本身

  • 1与奇数进行&与运算结果为0,与偶数进行&与运算结果为1

6.3 ~
  • ~非运算符表示对进行运算的数据各位取反

    • console.log(~1); // -2
      // 1 => 0000 0000 0000 0000 0000 0000 0000 0001 原反补相等
      // ? => 1111 1111 1111 1111 1111 1111 1111 1110 补码
      //   => 1111 1111 1111 1111 1111 1111 1111 1101 反码
      //   => 1000 0000 0000 0000 0000 0000 0000 0010 原码 => -2
      
    • console.log(~0); // -1
      // 0 => 0000 0000 0000 0000 0000 0000 0000 0000 原反补相等
      // ? => 1111 1111 1111 1111 1111 1111 1111 1111 补码
      //   => 1111 1111 1111 1111 1111 1111 1111 1110 反码
      //   => 1000 0000 0000 0000 0000 0000 0000 0001 原码 => -1
      
    • console.log(~-1); // -1
      // -1 => 1000 0000 0000 0000 0000 0000 0000 0001 原码
      //    => 1111 1111 1111 1111 1111 1111 1111 1110 反码
      //    => 1111 1111 1111 1111 1111 1111 1111 1111 补码
      // ?  => 0000 0000 0000 0000 0000 0000 0000 0000 补反原相等(符号变为0,正数) => 0
      
  • 任何数进行~非运算结果都为变号-1

6.4 ^
  • ^异或运算符表示进行运算的数据的二进制相同位置,值相同则结果为0,值不同则结果为1

    • console.log(3 ^ 9); // 10
      // 3 => 0000 0000 0000 0000 0000 0000 0000 0011
      // 9 => 0000 0000 0000 0000 0000 0000 0000 1001
      // ? => 0000 0000 0000 0000 0000 0000 0000 1010 => 10
      
    • console.log(3.1 ^ 0.2); // 3
      // 3.1 => 3 => 0000 0000 0000 0000 0000 0000 0000 0011
      // 0.2 => 0 => 0000 0000 0000 0000 0000 0000 0000 0000
      // ?        => 0000 0000 0000 0000 0000 0000 0000 0011 => 3
      
    • console.log(3.1 & 'lxl'); // 3
      // 3.1   => 3 => 0000 0000 0000 0000 0000 0000 0000 0011
      // 'lxl' => 0 => 0000 0000 0000 0000 0000 0000 0000 0000
      // ?          => 0000 0000 0000 0000 0000 0000 0000 0011 => 3
      
    • console.log(-1 ^ 9); // -1
      // -1 => 1000 0000 0000 0000 0000 0000 0000 0001 原码
      //    => 1111 1111 1111 1111 1111 1111 1111 1110 反码
      //    => 1111 1111 1111 1111 1111 1111 1111 1111 补码
      // 9  => 0000 0000 0000 0000 0000 0000 0000 1001 原反补相等
      // ?  => 1111 1111 1111 1111 1111 1111 1111 0110 补码
      //    => 1111 1111 1111 1111 1111 1111 1111 0101 反码
      //    => 1000 0000 0000 0000 0000 0000 0000 1010 原码 => -10
      
  • 任何数与-1进行^异或运算结果都为变号-1

  • 任何数与0进行^异或运算结果都为本身

  • 任何奇数与1进行^异或运算结果都为本身-1,任何偶数与1进行^异或运算结果都为本身+1

7. << , >> , >>>

  • 这三个运算符也是位运算符,但是与上四个不同,这三个主要用于位移动
7.1 <<
  • <<左移运算符用于将对象数据的32位整数形式向左移动,右侧补0

    • console.log(5 << 2); // 20
      // 5 => 0000 0000 0000 0000 0000 0000 0000 0101 原反补相等
      // ? => 0000 0000 0000 0000 0000 0000 0001 0100 补反原相等 => 20 => 5 * 2(2次方)
      
    • console.log(-5 << 2); // 20
      // -5 => 1000 0000 0000 0000 0000 0000 0000 0101 原码
      //    => 1111 1111 1111 1111 1111 1111 1111 1010 反码
      //    => 1111 1111 1111 1111 1111 1111 1111 1011 补码
      // ?  => 1111 1111 1111 1111 1111 1111 1110 1100 补码
      //    => 1111 1111 1111 1111 1111 1111 1110 1011 反码
      //    => 1000 0000 0000 0000 0000 0000 0001 0100 原码 => -20 => -5 * 2(2次方)
      
    • console.log(1025 << 22); // 20
      // 5 => 0000 0000 0000 0000 0000 0100 0000 0001 原反补相等 => 舍弃左侧一个1
      // ? => 0000 0000 0100 0000 0000 0000 0000 0000 补反原相等 => 4194304 => 2(22次方)
      
  • 任何数进行<<左移运算,左侧舍弃的值不包含1,结果都为本身 \* 2(移位次幂)

7.2 >>
  • >>右移运算符用于将对象数据的32位整数形式向右移动,正数左侧补0,负数左侧补1

    • console.log(5 >> 2); // 1
      // 5 => 0000 0000 0000 0000 0000 0000 0000 0101 原反补相等
      // ? => 0000 0000 0000 0000 0000 0000 0000 0001 补反原相等 => 1 => 5 / 2(2次方)
      
    • console.log(-5 >> 2); // 1
      // -5 => 1000 0000 0000 0000 0000 0000 0000 0101 原码
      //    => 1111 1111 1111 1111 1111 1111 1111 1010 反码
      //    => 1111 1111 1111 1111 1111 1111 1111 1011 补码
      // ?  => 1111 1111 1111 1111 1111 1111 1111 1110 补码 => 舍弃尾部两个1
      //    => 1111 1111 1111 1111 1111 1111 1111 1101 反码
      //    => 1000 0000 0000 0000 0000 0000 0000 0010 原码 => -2
      
  • 任何数进行>>右移运算,右侧舍弃的值不包含1,结果都为本身 / 2(移位次幂)

7.3 >>>
  • >>>无符号右移运算符用于将对象数据的32位整数形式向右移动,无视符号位扩展,左侧全都补0

    • console.log(5 >>> 2); // 1
      // 5 => 0000 0000 0000 0000 0000 0000 0000 0101 原反补相等
      // ? => 0000 0000 0000 0000 0000 0000 0000 0001 补反原相等 => 1 => 5 / 2(2次方)
      
    • console.log(-5 >> 2); // 1
      // -5 => 1000 0000 0000 0000 0000 0000 0000 0101 原码
      //    => 1111 1111 1111 1111 1111 1111 1111 1010 反码
      //    => 1111 1111 1111 1111 1111 1111 1111 1011 补码
      // ?  => 0011 1111 1111 1111 1111 1111 1111 1110 补反原相等 => 舍弃尾部两个1,首位补两个0 => 1073741822
      
  • 任何正数进行>>>无符号右移运算,规则都与>>右移运算相同

  • 任何负数进行>>>无符号右移运算,总是会得到一个正数

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

傲才.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值