实用的js运算符

实用的js运算符

一、可选链运算符?.

  • 可选链操作符( ?. )允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。
  • ?. 操作符的功能类似于 . 链式操作符,不同之处在于,在引用为空(null 或者 undefined) 的情况下不会引起错误,该表达式短路返回值是 undefined。
  • 与函数调用一起使用时,如果给定的函数不存在,则返回 undefined。
let obj = {
    user:{
        one:{
            firstName: 'Tom';
        },
    },
};
// 若存在obj.user.one.firstName该属性,那要安全的读取它就需要这样写:
let firstName =(obj 
        && obj.user 
        && obj.user.one
        && obj.user.one.firstName)|| 'default'
// 用可选链写做:
let firstName = obj?.user?.one?.firstName || 'default';

通过使用 ?. 操作符取代 . 操作符,JavaScript 会在尝试问 obj.user.one.firstName 之前,先隐式地检查并确定 obj.user 及obj.user.one既不是 null 也不是 undefined。如果是 null 或者 undefined,表达式将会短路计算直接返回 undefined。

语法:

  • obj?.prop
  • obj?.[expr]
  • arr?.[index]
  • func?.(args)

下面是?.运算符常见形式,以及不使用可选链时的等价形式。

    a?.b
    // 等同于
    a == null ? undefined : a.b

    a?.[x]
    // 等同于
    a == null ? undefined : a[x]

    a?.b()
    // 等同于
    a == null ? undefined : a.b()

    a?.()
    // 等同于
    a == null ? undefined : a()

要注意的是上面代码的后两种与函数的使用,如果a?.b()和a?.()。如果a?.b()里面的a.b有值,但不是函数,不可调用,那么a?.b()是会报错的。a?.()也是如此,如果a不是null或undefined,但也不是函数,那么a?.()会报错。

二 、空值合并运算符 ??

  • 如果第一个参数不是 null/undefined,将返回第一个参数,否则返回第二个参数。
  • 注意,虽然 JS 中的未定义 undefined、空对象 null、数值 0、空数字 NaN、布尔 false,空字符串’ ’ 都是假值,但??非空运算符只对 null/undefined 做处理。
 console.log( 1 ?? 'superJS' );       // 1
 console.log( false ?? 'superJS' );       // false
 console.log( null ?? 'superJS' );       // superJS
 console.log( undefined ?? 'superJS' );       // superJS

三、逻辑运算符

1.逻辑运算符与 && 和或 ||

  • 逻辑与(&&):看左边的值是真还是假,如果是真,返回的是右边的值,如果是假返回的是左边的值(只有false 、0、NaN、null、undefined、空字符串为假, 其余都是真)。

例:如果某个值为 true,则运行某个 function

// 第一个操作数求值为 true 就直接返回第一个数 **(短路操作)**
1alert(null && 'superJS')      // null
2alert(NaN && 'superJS')       // NaN
3alert(0 && 'superJS')         // 0
4alert(false && 'superJS')     // false
5alert(undefined && 'superJS') // undefined
6alert('' && 'superJS')        // '';

function say() {
  console.log("superJS");
}
let type = true;
type && say();     // superJS

  • 逻辑或(||): 看左边的值是真还是假,如果是真,返回的是左边的值,如果是假返回的是右边的值(只有 false 、0、NaN、null、undefined、空字符串为假, 其余都是真)。

例:给某个变量设置初始值

let student = {
  name: "shanguagua",
};
console.log(student.age || "superJS");    // superJS

注:(在js中&&运算符优先级大于||)

2.逻辑运算符非 !

非运算符!可将变量转换成 boolean 类型,null、undefined 和空字符串’'取反都为 true,其余都为 false。一般来说会有好几种用法,!,!!,!=,!==。

  1. 利用!取反

  2. 利用!!做类型判断
    判断变量 a 不等于 null,undefined 和’'才能执行的方法。

var a;
if (a != null && typeof a != undefined && a != "") {
  //balabala
}
//  等价于
if (!!a) {
  //balabala
}

四、位运算符

运算符名称描述
&如果两位都是1 则设置每位为1
|如果两位之一为1 则设置每位为1
^异或如果两位只有一位为1 则设置每位为1
~按位取反所有位
<<零填充左位移通过从右推入零向左位移,并使最左边的位脱落。
>>有符号右位移通过从左推入最左位的拷贝来向右位移,并使最右边的位脱落。
>>>零填充右位移通过从左推入零来向右位移,并使最右边的位脱落。

*位运算是低级的运算操作,所以速度往往也是最快的(相对其它运算如加减乘除来说),并且借助位运算的特性还能实现一些算法。恰当地使用运算有很多好处。下面举几个例子。

1.按位非~

先来个小例子:

~1 = -2

运算过程:

在 JavaScript 中,所有整数字变量默认都是有符号整数,一个整数是4个字节,32位,最高位为符号位,0代表正数,1代表负数。

1的二进制表示为:00000000 00000000 00000000 00000001
按位非(按位取反)后是:11111111 11111111 11111111 11111110

由于计算机中的数字都是以补码的形式存在以及运算的,这个按位非后的数由于最高位符号位为1,是负数,因此再结合上述机器存储的 编码方式 得:

反码 10000000 00000000 00000000 00000001
补码 10000000 00000000 00000000 00000010

根据机器存储一个具体数字的 编码方式 知:
对于正数:原码 = 反码 = 补码。
对于负数:反码为原码的最高位不变,其余位取反。补码为在反码的基础上加1。

由此可知1按位取反的结果就是-2。


下面是~的两个小巧用:

1) 使用~~ 操作符来替代正数的 Math.floor( ),替代负数的 Math.ceil( )
Math.floor()的作用是丢弃小数部分,获得整数。
因为只有整型可以进行位运算,小数是没有位运算的,所以对小数进行位运算会直接把小数给舍去。因 此~1.111111取反后的结果就是-2,再把-2取反,还是回到1,因此用这种方式实现了比Math.floor更快的运算。

Math.floor(1.111111) === 1; // true
~~1.111111 === 1; // true
Math.ceil(-6.6) === -6; // true
~~-6.6 === -6; // true

2)使用按位非~判断引索存在
也是一个非常实用的小技巧,如判断一个字符是否存在

// 如果url含有?号,则后面拼上&符号,否则加上?号
url += ~url.indexOf("?") ? "&" : "?";
// 不用~的写法
url += url.indexOf("?") === -1 ? "&" : "?";

这是因为:

~-1 === 0

-1原码 10000000 00000000 00000000 00000001
-1反码 11111111 11111111 11111111 11111110
-1补码 11111111 11111111 11111111 11111111
 ~-1   00000000 00000000 00000000 00000000

2.异或^

0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
异或的运算规律顾名思义就是0和1异或的时候相同的异或结果为0,不同的异或结果为1。

1)使用异或交换两个数

let a = 5,
    b = 6;
//  使用零食变量c交换a, b的值 
let c = a;
a = b;
b = c;
//  使用异或交换a,b的值
a = a ^ b;
b = a ^ b; // b 等于 5
a = a ^ b; // a 等于 6

这是因为:
把1式带入2式可得:b = (a ^ b) ^ b = a ^ (b ^ b) = a ^ 0 = a;
而当计算3式时,b已经变为a了,即: a = a ^ (a ^ b) = 0 ^ b = b;

这也是两个变量交换值的最快方法,不需要任何额外的空间。

2) 找出只出现了一次的元素
设计一个算法要求不占用额外的空间。
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
    let result = 0;
    for(let i=0; i< nums.length; i++){
        result = nums[i] ^ result;
    }
    return result
};
  • 使用异或运算,将所有值进行异或
  • 不同为真,相同为假,所以 a^a = 0 ;0^a = a
  • 因为异或运算 满足交换律 a^b^a = a^a^b = b 所以数组经过异或运算,单独的值就剩下了

3)加密
异或还经常被用于加密,比如在前面分享过的DES加密中就多次用到了异或的位运算。

第一步,明文(text)与密钥(key)进行异或运算,可以得到密文(cipherText)。
text ^ key = cipherText
第二步,密文与密钥再次进行异或运算,就可以还原成明文。
cipherText ^ key = text
原理很简单,如果明文是 x,密钥是 y,那么 x 连续与 y 进行两次异或运算,得到自身。
(x ^ y) ^ y
= x ^ (y ^ y)
= x ^ 0
= x
4)数据备份

异或运算可以用于数据备份。文件 x 和文件 y 进行异或运算,产生一个备份文件 z。
x ^ y = z
以后,无论是文件 x 或文件 y 损坏,只要不是两个原始文件同时损坏,就能根据另一个文件和备份文件,进行还原。
x ^ z
= x ^ (x ^ y)
= (x ^ x) ^ y
= 0 ^ y
= y
上面的例子是 y 损坏,x 和 z 进行异或运算,就能得到 y。

3.按位与&和按位或|

&: 0与 0跟1都为0,全1才为1;
|:1或 0跟1都为1,全0才为0。

使用位运算符时会抛弃小数位,我们可以利用|0来给数字取整。也可以使用&1来判断奇偶数。

  1. 通过|运算符取整
10.6 | 0         // 10
     1010
     0000
    ——————
     1010     ->  10    
  1. 判断奇偶数
// 跟1进行与运算,偶数结果为0,奇数结果为1
let num = 5;
!!(num & 1); // true
!!(num % 2); // true
//   0101
  &  0001
    ——————
     0001

4.左移<< 、右移>>

  • 左移<<:表示将一个数的二进制值向左移动指定的位数,符号位始终保持不变。如果右侧空出位置,则自动填充为 0。超出 32 位的值,则自动丢弃。

console.log(5 << 2); //返回值20

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8D5U6cxb-1662693923271)(Image.png)]

如果要求2的n次方:

function power(n) { 
    return 1 << n; 
} 
power(5);       // 32
  • 右移>>:表示将一个数的二进制值向右移动指定的位数,它把 32 位数字中的所有有效位整体右移,再使用符号位的值填充空位,即正数补0,负数补1。移动过程中超出的值将被丢弃。

console.log(1000 >> 8); //返回值3

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UvbyuRsW-1662693923273)(Image%20%5B1%5D.png)]

求一个数的二分之一:

var num = 64 >> 1; // 32

有符号左移与右移不会影响符号位。

  • 零填充(无符号)右移>>>:正数的无符号右移与有符号右移结果是一样的。负数的无符号右移会把符号位也一起移动,而且无符号右移会把负数的二进制码当成正数的二进制码。

console.log(-1000 >> 8); //返回值 -4
console.log(-1000 >>> 8); //返回值 16777212

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BPDeoli5-1662693923274)(Image%20%5B2%5D.png)]

所以可以利用无符号右移来判断一个数的正负:

function isPos(n) { 
    return (n === (n >>> 0)) ? true : false; 
} 
isPos(-1);    // false 
isPos(1);     // true

注:js及其他语言中都没有无符号左移运算。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值