IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> javaScript 你不知道的运算符技巧 -> 正文阅读

[JavaScript知识库]javaScript 你不知道的运算符技巧

初级

类型转换

string类型转换nubmer类型

数字转类型有些同学可能会这样写

//before
const a = '123'

Number(a)	// 123

其实可以用*1 或者 + 来做隐式转换

//after
const a = '123'

console.log(+a)    // 123
console.log(a * 1)    //123

用+ 来隐式转换的其他示例

'123'        // 123
'ds'         // NaN
''           // 0
null         // 0
undefined    // NaN
{ valueOf: ()=>'3' }    // 3

number类型转string类型

//before
const a = 123
String(a)    //'123'

可以用 + ‘ ’ 来做隐式转换

//after
const a = 123
console.log(a + '')    // '123'

转Boolean类型

!!直接转成布尔值,真值会转成true,假值会转成false

//before
Boolean(1)    //true

//after
!!123    //true

使用 && 作逻辑判断

&& 除了用来做条件判断,因为其返回值特性(如果第一个为真值,返回后面那个值的结果,否则直接返回前值结果,后面的值不会继续求值),也可以运用在逻辑判断

//条件判断
if(a === 1 && b ===2 ) {
    //todo
}

其实当if内的逻辑不复杂的时候,也可以用来做代替if

const arr - [1, 2, 3]
const index = arr.indexOf(1)
//before
if(index === -1) {
    arr.splice(index, 1)
}

//after 
index === -1 && arr.splice(index, 1)
//其实这个还可以再用位运算符 `~ `简化一点,继续向下看到位运算符就能看到。

如果是两个值中取一个值,而且也可以进行这样的简写

//当a是真值则返回b,否则返回a
a &&= b

//等同于
a = a && b

使用 || 做变量赋值

|| 除了用来做条件判断,因为其返回值特性(第一个值为真值,则返回第一个值的结果, 如果第一个为假值,则继续往后查结果,直到返回真值,返回真值后,如果后面还有值的话,不会求值),也可以运用在变量赋值判断

//条件判断
if(a === 1 || b === 2) { 
    //todo
}

//变量赋值
const a = null || 1
console.log(a)  // 1

当用来给值当默认值的时候,也可以进行这样的简写

//当box标签有值则使用这个值,没值使用默认值
document.getElementById('box').innerHTML ||= '<div>我是默认文本</div>'

//等同于
document.getElementById('box').innerHTML = document.getElementById('box').innerHTML || '<div>我是默认文本</div>'

可选链接运算符 (?.)

可选链接运算符 ?.处于ES2020提案的第4阶段,要用的话要注意babel兼容

当我们使用了?.时,如果这个值为假值,会停止执行,直接返回undefined

平时我们开发如果如果后端的数据有个字段是数组,我们要操作它,以防万一我们一般会进行数组判断,防止因为报错而影响后续执行

//before
const data = []

if(data && data.length) {
    //todo
}

//after
if(data?.length) {
    //todo
}

或者对象的值可能为空时,这样调用方法就会报错,也可以用这个判断

const res = obj?.name()

//这样不会执行,也不会报错,直接返回undefined

后面也可以跟动态变量名和数组取值

const res = datas?.data?.[arrName]?.[0]

空值合并运算符(??)

空值合并运算符(??)是一个逻辑操作符,当左侧的操作数为null或者undefined 时,才会返回右侧操作数结果,否则返回左侧结果

??||有点像,但是||判断的是假值,?? 是判断null和undefined

//真值
const a = 1 ?? '默认值'
console.log(a)    // 1

//假值
const a = '' ?? '默认值'
console.log(a)    // ''

//null
const a = null ?? '默认值'
console.log(a)    // '默认值'

//undefined
const a = undefined ?? '默认值'
console.log(a)    // '默认值'

也可以进行简写

let a = 1
a ??= '默认值'

//等同于
a = a ?? '默认值'

console.log(a)    // 1

中级

按位非运算符 (~)

简单说明下按位非运算符 ~

按位非运算符 ~是二进制的取反符号,取二进制的反码,对位为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

//before
Math.floor(num)  // 6

//after 
~~num   // 6

当值为负数时

const num = -6.6

//before 
Math.ceil(num)      // -6

//after 
~~num   // -6

判断是否等于-1

~-1时候的值为0,可以用来判断indexOf的值

const arr = [1, 2, 3, 4, 5]
const index = arr.indexOf(1)

//这里index值为0,~index的值为-1,-1是真值
~index && arr.splice(index, 1)
console.log(arr);   //[2, 3, 4, 5]

按位或运算符 (|)

简单说明下按位或运算符 |

它需要至少需要两个操作数做对比,对位的数只有有一个是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

-6.66 | 0    //-6

代替Math.round()

//正数
const a1 = 1.4
const a2 = 1.6
a1 + 0.5 | 0   // 1
a2 + 0.5 | 0   // 2

//负数
const b1 = -1.4
const b2 = -1.6
b1 + 0.5 | 0   // -1
b2 + 0.5 | 0   // -2

按位与运算符(&)

使用按位与运算符 &实际上是操作数同为比对的结果。

当对位的数有一个是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    // 0
7 & 1    // 1

// 0是假值,8 & 1 直接用来判断是奇数,如果是想直接判断偶数,则用num & 2
if(8 & 1) {
    //如果是奇数执行的逻辑
}

判断是否为2的整数幂

const a = 20;
const b = 32;

a & (a - 1) // 16  a不是2的整数幂
b & (b - 1) // 0 	b是2的整数幂

按位异或运算符 (^)

使用按位异或符 ^实际上是操作数同为比对的结果。
当比对位相加为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来判断,其实可以用^切换

//before
let toggle = 0

if(toggle) {
    toggle = 1
} else {
    toggle = 0
}

//或者用三元运算符
toggle = toggle ? 0 : 1

^的方式看起来更简单

let toggle = 0

toggle ^= 1  
console.log(toggle)    // 1

判断两数符号是否相同

const a = 1
const b = 2
const c = -1
const d = -2

(a ^ b) >= 0   // true
(a ^ c) >= 0   // false
(c ^ d) >= 0   // true

判断整数部分是否相等

因为位运算符操作的是正数

//都是正数
const a = 1
const b = 1

a ^ b    // 0

//小数点
const a = 1.1
const b = 1.6

a ^ b    // 0

完成值交换

我们也可以使用按位异或来进行两个变量的值交换

let a = 1
let b = 2

a ^= b
b ^= a
a ^= b
console.log(a)   // 2
console.log(b)   // 1

不过这种其实有点麻烦,其实用ES6的解构更方便

let a = 1;
let b = 2;

[a, b] = [b, a]
console.log(a)   // 2
console.log(b)   // 1

进阶

左移(<<)

左移用符号 << 来表示,正如它的名字,即将数值的二进制码按照指定的位数向左移动,然后空位填充0,符号位不变(最高位是符号位)

2 = 0000 0000 0000 0000 0000 0000 0000 0010

//我们把2 左移 5位
2 << 5 = 0000 0000 0000 0000 0000 0000 0100 0000 = 64

左移也可以用来取整

1.66 << 0   // 1

右移(>>)

有符号右移用符号 >> 来表示,即将数值的二进制码按照指定的位数向右移动,符号位不变,空位填充0,它和左移相反

2 = 0000 0000 0000 0000 0000 0000 0000 0010

//我们把2 右移 2位
2 >> 5 = 0000 0000 0000 0000 0000 0000 0000 0000 = 0


//再举个例子,我们把 64 右移5位
64 = 0000 0000 0000 0000 0000 0000 0100 0000
64 >> 5 = 0000 0000 0000 0000 0000 0000 0000 0010 = 2

无符号右移 (>>>)

“>>>”运算符执行无符号右移位运算。它把无符号的 32 位整数所有数位整体右移。对于无符号数或正数右移运算,无符号右移与有符号右移运算的结果是相同的。

//正数和>> 结果一样
10 >> 2   // 2
10 >>> 2   // 2

//对于负数来说,无符号右移将使用 0 来填充所有的空位,同时会把负数作为正数来处理,所得结果会非常大所以,使用无符号右移运算符时要特别小心,避免意外错误。
-1 >>> 0    // 4294967295

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(255,255,255)

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)")    // #ffffff

使用位运算符做权限控制

分配权限

上面我们说了左移<<是把二进制码移动对应位数

// 1 的二进制为 00000001

1 << 0  // 00000001
1 << 1  // 00000010
1 << 2  // 00000100
1 << 3  // 00001000
1 << 4  // 00010000
1 << 5  // 00100000

...

大家会发现,每个数里都有且只有一个1,并且每个位置都不一样,这时如果把他们组合在一起的话,其实满足了组合的唯一性。

我们拿 vue-nextpatchFlags.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, // 1 << 0
  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,

  // SPECIAL FLAGS -------------------------------------------------------------

  // Special flags are negative integers. They are never matched against using
  // bitwise operators (bitwise matching should only happen in branches where
  // patchFlag > 0), and are mutually exclusive. When checking for a special
  // flag, simply check patchFlag === FLAG.
  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 的权限,可以操控TEXTCLASSPROPS

权限校验

上面我们说过&是对操作数们做比对,当对位的数有一个是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按位运算符及其妙用

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-29 23:25:14  更:2021-07-29 23:25:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/7 5:06:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码