JavaScript技巧篇 之 如何巧妙安全取值 & 安全设置兜底值

758 阅读3分钟

运算符扩展

可选链操作符 (?.)

我们来看一个项目中常见的场景:

 let xuan = {
     xuan2: {
 ​
     }
 };
 ​
 console.log(xuan.xuan2.xuan3);  // undefined
 ​
 console.log(xuan.xuan2.xuan3.xuan4);    
 // Uncaught TypeError: Cannot read properties of undefined (reading 'xuan4')

可能你会问为什么第一条输出不会报错,但第二条输出会报错。我的理解是 从已定义的对象身上取未定义的值是可以的,但是从undefined身上取值会报错,因为xuan3未定义值为undefined所以才会报错。

可以看出来以上取值方式是危险的,搞不好中间哪一步就为undefined。那么有什么办法可以安全的取值呢?有,那就是&&短路运算符

 let i = 1
 console.log(i++ && false && i++);   // false
 console.log(i); // 2
 ​
 console.log(true && 2 && 0 && 4);   // 0

所谓&&逻辑运算符就是当有一个条件不满足,那么就不会继续判断后面的条件,并且输出最后一个导致终止的值

总所周知,在JavaScript中null和undefined的布尔值是为false,那么我们是否可以利用这个小特性实现安全取值

 let xuan = {
     xuan2: {
     },
 };
 ​
 console.log(xuan && xuan.xuan2 && xuan.xuan2.xuan3 && xuan.xuan2.xuan3.xuan4);
 // undefined

可以看到,当进行到了xuan.xuan2.xuan3这一步时值为undefined,就会终止后续判断。

我的天啊,安全是安全了,可是多了这么多代码,那有没有什么办法既能安全又能简洁呢,当然有,此时我们的主角?.可选链操作符出场了

 let xuan = {
     xuan2: {
     },
 };
 ​
 console.log(xuan?.xuan2?.xuan3?.xuan4);    // undefined

?. 操作符的功能类似于 . 链式操作符,不同之处在于,在引用为空(nullish ) (null 或者 undefined) 的情况下不会引起错误,该表达式短路返回值





空值合并操作符 (??)

我们来看这么一个场景:

 let xuan = {
     xuan2: {
     },
 };
 ​
 let flag = xuan.xuan2.xuan3
 console.log('我的名字叫' + flag);    // 我的名字叫undefined

没有从业计算机行业的朋友看到这一幕会觉得好有个性,名字居然叫未定义。但是我们看到这一幕就会知道是取值出问题了。此时我们希望如果用户没设置名字,那么就给他设置一个兜底值要怎么做?

||短路运算符

 console.log(false || false || '轩小浅');   // 轩小浅
 ​
 console.log(false || '轩小浅' || '喜洋洋');   // 轩小浅
 ​
 console.log(false || false || 0);   // 0

所谓||逻辑运算符就是当有一个表达式布尔值为true,那么就不会继续后面的判断,并且输出第一个导致终止的值,如果布尔值都不为true那么就输出最后一个值。

因为undefined在布尔值里面为false,那我们可以用||实现设置兜底值

 let xuan = {
     xuan2: {
     },
 };
 ​
 let flag = xuan.xuan2.xuan3 || '轩小浅'    // 如果第一个表达式不为true,那么将后面的值赋值给flag
 ​
 console.log('我的名字叫' + flag);    // 我的名字叫轩小浅

可以看到成功了,但,真的没有一点问题吗?我们来看下面一段代码

 let xuan = {
     xuan2: {
         xuan3:0,
         xuan4:'',
     },
 };
 ​
 let flag = xuan.xuan2.xuan3 || '轩小浅'
 ​
 let flag2 = xuan.xuan2.xuan4 || '喜洋洋'
 ​
 console.log('我的名字叫' + flag);    // 我的名字叫轩小浅
 ​
 console.log('我的名字叫' + flag2);   // 我的名字叫喜洋洋

可以看到,明明用户设置了名字,为什么还是动用了兜底值?因为在JavaScript中空字符串0的布尔值都为false

聪明的你一拍脑门就写出了下面的代码:

 let xuan = {
     xuan2: {
         xuan3: 0,
         xuan4: '',
     },
 };
 ​
 let flag = xuan.xuan2.xuan3;
 let flag2 = xuan.xuan2.xuan4;
 ​
 if (flag === (undefined || null)) {
     flag = '轩小浅'
 }
 ​
 if (flag2 === (undefined || null)) {
     flag2 = '喜洋洋'
 }
 ​
 console.log('我的名字叫' + flag);    // 我的名字叫0
 ​
 console.log('我的名字叫' + flag2);   // 我的名字叫

是的,判断一下不就行了吗?但是你看了一眼身边的大佬是这样写的:

 let xuan = {
     xuan2: {
         xuan3: 0,
         xuan4: '',
     },
 };
 ​
 let flag = xuan.xuan2.xuan3 ?? '轩小浅';
 let flag2 = xuan.xuan2.xuan4 ?? '喜洋洋';
 let flag3 = xuan.xuan2.xuan5 ?? '灰太狼';
 ​
 console.log('我的名字叫' + flag);    // 我的名字叫0
 ​
 console.log('我的名字叫' + flag2);   // 我的名字叫
 ​
 console.log('我的名字叫' + flag3);   // 我的名字叫灰太狼

看完后你含着泪花默默改掉自己刚刚写的代码。

??是一个逻辑操作符,当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。



两者配合使用

 let xuan = {
     xuan2: {
 ​
     },
 };
 ​
 let flag = xuan?.xuan2?.xuan3?.xuan4 ?? '轩小浅';
 ​
 console.log(flag);

\