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
:符号位,占1bitE
:指数位,占11bitM
:有效数字位,占52bit
-
S
:符号位,位于存储单元首个bit位,1代表负数,0代表正数 -
E
:指数位,位于存储单元第2~12个bit位-
指数位用于存储数据二进制的科学计数法所进位的指数数
-
指数位存在正负:
-
当存储数据为小于1的小数时,指数进位为负
-
存储方式:以11bit位存储指数,IEEE 754规定2的10次方-1(1024)对应为0,此0-1为
-1
,此0+1为1
-
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
-
所有数据运算最后都会转化为三种数据:字符串,number,NaN
-
影响结果顺序:字符串,NaN,number
-
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
-
-
-
==
两侧类型不同时:-
任何数据都与
undefined
和null
不相等-
console.log(undefined == 1); // false
-
console.log(null == 1); // false
-
-
undefined
和null
相等-
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
-
-
非
0
的number
和非0n
的大数类型总是返回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
-
-
任何正数进行
>>>
无符号右移运算,规则都与>>
右移运算相同 -
任何负数进行
>>>
无符号右移运算,总是会得到一个正数