初级
类型转换
string类型转换nubmer类型
数字转类型有些同学可能会这样写
const a = '123'
Number(a)
其实可以用*1 或者 + 来做隐式转换
const a = '123'
console.log(+a)
console.log(a * 1)
用+ 来隐式转换的其他示例
'123'
'ds'
''
null
undefined
{ valueOf: ()=>'3' }
number类型转string类型
const a = 123
String(a)
可以用 + ‘ ’ 来做隐式转换
const a = 123
console.log(a + '')
转Boolean类型
双!! 直接转成布尔值,真值会转成true,假值会转成false
Boolean(1)
!!123
使用 && 作逻辑判断
&& 除了用来做条件判断,因为其返回值特性(如果第一个为真值,返回后面那个值的结果,否则直接返回前值结果,后面的值不会继续求值),也可以运用在逻辑判断
if(a === 1 && b ===2 ) {
}
其实当if内的逻辑不复杂的时候,也可以用来做代替if
const arr - [1, 2, 3]
const index = arr.indexOf(1)
if(index === -1) {
arr.splice(index, 1)
}
index === -1 && arr.splice(index, 1)
如果是两个值中取一个值,而且也可以进行这样的简写
a &&= b
a = a && b
使用 || 做变量赋值
|| 除了用来做条件判断,因为其返回值特性(第一个值为真值,则返回第一个值的结果, 如果第一个为假值,则继续往后查结果,直到返回真值,返回真值后,如果后面还有值的话,不会求值),也可以运用在变量赋值判断
if(a === 1 || b === 2) {
}
const a = null || 1
console.log(a)
当用来给值当默认值的时候,也可以进行这样的简写
document.getElementById('box').innerHTML ||= '<div>我是默认文本</div>'
document.getElementById('box').innerHTML = document.getElementById('box').innerHTML || '<div>我是默认文本</div>'
可选链接运算符 (?.)
可选链接运算符 ?. 处于ES2020提案的第4阶段,要用的话要注意babel兼容
当我们使用了?. 时,如果这个值为假值,会停止执行,直接返回undefined
平时我们开发如果如果后端的数据有个字段是数组,我们要操作它,以防万一我们一般会进行数组判断,防止因为报错而影响后续执行
const data = []
if(data && data.length) {
}
if(data?.length) {
}
或者对象的值可能为空时,这样调用方法就会报错,也可以用这个判断
const res = obj?.name()
后面也可以跟动态变量名和数组取值
const res = datas?.data?.[arrName]?.[0]
空值合并运算符(??)
空值合并运算符(?? )是一个逻辑操作符,当左侧的操作数为null 或者undefined 时,才会返回右侧操作数结果,否则返回左侧结果
?? 和|| 有点像,但是|| 判断的是假值,?? 是判断null和undefined
const a = 1 ?? '默认值'
console.log(a)
const a = '' ?? '默认值'
console.log(a)
const a = null ?? '默认值'
console.log(a)
const a = undefined ?? '默认值'
console.log(a)
也可以进行简写
let a = 1
a ??= '默认值'
a = a ?? '默认值'
console.log(a)
中级
按位非运算符 (~)
简单说明下按位非运算符 ~
按位非运算符 ~ 是二进制的取反符号,取二进制的反码,对位为0则变1,1则变0,
有一个规律,就是~正数的话,结果等于-(x + 1)
下面32bit的二进制举例
1 = 0000 0000 0000 0000 0000 0000 0000 0001
~1 = 1111 1111 1111 1111 1111 1111 1111 1110
下面来说下这个符号用的场景
取整
可以使用双位操作符~~ 来替代正数的 Math.floor( ) ,替代负数的Math.ceil( )
当值为正数时
const num = 6.6
Math.floor(num)
~~num
当值为负数时
const num = -6.6
Math.ceil(num)
~~num
判断是否等于-1
当~-1 时候的值为0,可以用来判断indexOf的值
const arr = [1, 2, 3, 4, 5]
const index = arr.indexOf(1)
~index && arr.splice(index, 1)
console.log(arr);
按位或运算符 (|)
简单说明下按位或运算符 |
它需要至少需要两个操作数做对比,对位的数只有有一个是1,则返回1,否则返回0
我们用10 和 18 的 举例
10 = 0000 0000 0000 0000 0000 0000 0000 1010
18 = 0000 0000 0000 0000 0000 0000 0001 0010
---------------------------------------------
10 | 18 = 0000 0000 0000 0000 0000 0000 0001 1010 = 26
下面来说下按位异或运算符 ^ 可以使用的场景
取整
可以用 位或运算符 | 进行取整
6.66 | 0
-6.66 | 0
代替Math.round()
const a1 = 1.4
const a2 = 1.6
a1 + 0.5 | 0
a2 + 0.5 | 0
const b1 = -1.4
const b2 = -1.6
b1 + 0.5 | 0
b2 + 0.5 | 0
按位与运算符(&)
使用按位与运算符 & 实际上是操作数 同为比对的结果。
当对位的数有一个是0的时候,返回0,否则为1
10 = 0000 0000 0000 0000 0000 0000 0000 1010
1 = 0000 0000 0000 0000 0000 0000 0000 0001
---------------------------------------------
10 | 1 = 0000 0000 0000 0000 0000 0000 000 0000 = 0
下面来说下按位与运算符 & 可以使用的场景
判断奇偶数
8 & 1
7 & 1
if(8 & 1) {
}
判断是否为2的整数幂
const a = 20;
const b = 32;
a & (a - 1)
b & (b - 1)
按位异或运算符 (^)
使用按位异或符 ^ 实际上是操作数 同为比对的结果。 当比对位相加为1时返回1,否则返回0
下面拿32位(bit)举例
10 = 0000 0000 0000 0000 0000 0000 0000 1010
18 = 0000 0000 0000 0000 0000 0000 0001 0010
--------------------------------------------
10 ^ 18 = 0000 0000 0000 0000 0000 0000 0001 1000 = 24
下面来说下位异或运算符 ^ 可以使用的场景
切换0和1
在做开关类的功能时可能会用变量保存0和1来判断,其实可以用^ 切换
let toggle = 0
if(toggle) {
toggle = 1
} else {
toggle = 0
}
toggle = toggle ? 0 : 1
用^ 的方式看起来更简单
let toggle = 0
toggle ^= 1
console.log(toggle)
判断两数符号是否相同
const a = 1
const b = 2
const c = -1
const d = -2
(a ^ b) >= 0
(a ^ c) >= 0
(c ^ d) >= 0
判断整数部分是否相等
因为位运算符操作的是正数
const a = 1
const b = 1
a ^ b
const a = 1.1
const b = 1.6
a ^ b
完成值交换
我们也可以使用按位异或来进行两个变量的值交换
let a = 1
let b = 2
a ^= b
b ^= a
a ^= b
console.log(a)
console.log(b)
不过这种其实有点麻烦,其实用ES6的解构更方便
let a = 1;
let b = 2;
[a, b] = [b, a]
console.log(a)
console.log(b)
进阶
左移(<<)
左移用符号 << 来表示,正如它的名字,即将数值的二进制码按照指定的位数向左移动,然后空位填充0,符号位不变(最高位是符号位)
2 = 0000 0000 0000 0000 0000 0000 0000 0010
2 << 5 = 0000 0000 0000 0000 0000 0000 0100 0000 = 64
左移也可以用来取整
1.66 << 0
右移(>>)
有符号右移用符号 >> 来表示,即将数值的二进制码按照指定的位数向右移动,符号位不变,空位填充0,它和左移相反
2 = 0000 0000 0000 0000 0000 0000 0000 0010
2 >> 5 = 0000 0000 0000 0000 0000 0000 0000 0000 = 0
64 = 0000 0000 0000 0000 0000 0000 0100 0000
64 >> 5 = 0000 0000 0000 0000 0000 0000 0000 0010 = 2
无符号右移 (>>>)
“>>>”运算符执行无符号右移位运算。它把无符号的 32 位整数所有数位整体右移。对于无符号数或正数右移运算,无符号右移与有符号右移运算的结果是相同的。
10 >> 2
10 >>> 2
-1 >>> 0
16进制颜色值和RGB颜色值相互转换
前面我们提起过左移<< 和右移>> ,现在展示一下使用场景
16进制颜色值转RGB:
function hexToRGB(hex){
var hex = hex.replace("#","0x"),
r = hex >> 16,
g = hex >> 8 & 0xff,
b = hex & 0xff;
return "rgb("+r+","+g+","+b+")";
}
hexToRGB("#ffffff")
RGB转16进制颜色值:
function RGBToHex(rgb){
var rgbArr = rgb.split(/[^\d]+/),
color = rgbArr[1]<<16 | rgbArr[2]<<8 | rgbArr[3];
return "#"+color.toString(16);
}
RGBToHex("rgb(255,255,255)")
使用位运算符做权限控制
分配权限
上面我们说了左移<< 是把二进制码移动对应位数
1 << 0
1 << 1
1 << 2
1 << 3
1 << 4
1 << 5
...
大家会发现,每个数里都有且只有一个1,并且每个位置都不一样,这时如果把他们组合在一起的话,其实满足了组合的唯一性。
我们拿 vue-next 的patchFlags.ts文件中的这一段举个栗子,这段是 VisualDOM 中对 vnode 的类型标记,作用是在更新 DOM树 的时候会根据 vnode 的类型来使用不同的更新策略,我们看看类型的定义
* Patch flags can be combined using the | bitwise operator and can be checked
* using the & operator, e.g.
*
* ```js
* const flag = TEXT | CLASS
* if (flag & TEXT) { ... }
* ```
*
* Check the `patchElement` function in '../../runtime-core/src/renderer.ts' to see how the
* flags are handled during diff.
*/
export const enum PatchFlags {
TEXT = 1,
CLASS = 1 << 1,
STYLE = 1 << 2,
PROPS = 1 << 3,
FULL_PROPS = 1 << 4,
HYDRATE_EVENTS = 1 << 5,
STABLE_FRAGMENT = 1 << 6,
KEYED_FRAGMENT = 1 << 7,
UNKEYED_FRAGMENT = 1 << 8,
NEED_PATCH = 1 << 9,
DYNAMIC_SLOTS = 1 << 10,
HOISTED = -1,
BAIL = -2
}
这里作者用 << 定义了除了特性的类型外的 11 种类型,每一种都是通过左移得到的,作者在上面注释已经给了提示,使用 | 来权限赋予, 用& 来做权限校验
权限赋予
上面我们说过| 是对操作数们做比对,对位的位数只有有一个是1,则返回1,否则返回0
我们创建一个权限看看
const permission = 0
const permission1 = permission | TEXT | CLASS | PROPS
---------------------------------------------
TEXT = 0000 0000 0000 0000 0000 0000 0000 0001
CLASS = 0000 0000 0000 0000 0000 0000 0000 0010
PROPS = 0000 0000 0000 0000 0000 0000 0000 1000
permission1 = permission | TEXT | CLASS | PROPS = 0000 0000 0000 0000 0000 0000 0000 1011 = 8
这时赋予了一个permission1 的权限,可以操控TEXT , CLASS , PROPS
权限校验
上面我们说过& 是对操作数们做比对,当对位的数有一个是0的时候,返回0,否则返回为1。
现在进行权限校验
permission1 & TEXT
------------------------------------------------
permission1 = 0000 0000 0000 0000 0000 0000 0000 1011
TEXT = 0000 0000 0000 0000 0000 0000 0000 0001
permission1 & TEXT = 0000 0000 0000 0000 0000 0000 0000 0001 = 1 = true
permission1 & STYLE
------------------------------------------------
permission1 = 0000 0000 0000 0000 0000 0000 0000 1011
STYLE = 0000 0000 0000 0000 0000 0000 0000 0100
permission1 & STYLE = 0000 0000 0000 0000 0000 0000 0000 0000 = 0 = false
删除权限
删除权限的本质其实是将指定位置上的 1 重置为 0
我们可以使用^ , ^ 规则是当比对位相加为1时返回1,否则返回0
permission1 ^ TEXT
-------------------------------------------
permission1 = 0000 0000 0000 0000 0000 0000 0000 1011
TEXT = 0000 0000 0000 0000 0000 0000 0000 0001
permission1 ^ TEXT = 0000 0000 0000 0000 0000 0000 0000 1010
不过这种有缺点,就是如果没有这个权限的话,用^ 会增加权限
可以删除前进行判断,或者用&~ 来删除
permission1 & (~TEXT)
-------------------------------------------
permission1 = 0000 0000 0000 0000 0000 0000 0000 1011
~TEXT = 1111 1111 1111 1111 1111 1111 1111 1110
permission1 & (~TEXT) = 0000 0000 0000 0000 0000 0000 0000 1010
permission1 & (~STYLE)
--------------------------------------------
permission1 = 0000 0000 0000 0000 0000 0000 0000 1011
~STYLE = 1111 1111 1111 1111 1111 1111 1111 1011
permission1 & (~STYLE) = 0000 0000 0000 0000 0000 0000 0000 1011
参考
# 「硬核JS」令你迷惑的位运算
# js按位运算符及其妙用
|