场景再现
代码审核中~~
同事代码:
a?.b.c();
我: 哥们你这代码不对呀,?.后面的引用都要用?.呀,不然a为undefined还是要报错吧。应该写成:
a?.b?.c();
同事:😳
我:还不服气?给你打开控制台试试:
我:😳
然后同事下班了,于是便有了这篇文章。
(?.)原理分析
a?.b.c
我直接打开babel,编译一波:
// 源码
a?.b.c
// babel后
var _a;
(_a = a) === null || _a === void 0 ? void 0 : _a.b.c;
这就看的很清晰了:
?.运算符在根对象为null或者undefined生效.- 只验证
?.的对象,该对象为空则直接返回undefined.
a?.b.c()
再来看看代码审核中的场景:
// 源码
a?.b.c()
// babel
var _a;
(_a = a) === null || _a === void 0 ? void 0 : _a.b.c();
- 从编码后的代码可以看出,就算是调用函数也无所谓,根对象为空直接返回了,并不会执行
- 这就涉及到了
短路计算的场景,例如:
let potentiallyNullObj = null;
let x = 0;
let prop = potentiallyNullObj?.[x++];
这种情况,x++并不会执行,x的值也不会变化。
a.b?.[0]
数组前也可以用?.运算符
// 源码
a.b?.[0]
// babel
var _a$b;
(_a$b = a.b) === null || _a$b === void 0 ? void 0 : _a$b[0];
a?.[0]逻辑其实和a?.b类似,只是换成了数组写法而已。
a.b?.()
函数调用前也可以用?.运算符
// 源码
a.b?.()
// babel
var _a$b, _a;
(_a$b = (_a = a).b) === null || _a$b === void 0 ? void 0 : _a$b.call(_a);
这种方法适用于a.b为undefined或null的情况。
a = { b: undefined }
// 报错 a?.b is not a function
a?.b()
// 返回undefined
a.b?.()
从bebel编译出的源码也很好解释,a?.b()只判断a,a不为空还是会执行a.b(),但是b也是空的,无法执行,遂报错。
反思
关于?.的原理好歹是理清了,但是为啥我之前会有?.后都需要用?.的错误印象呢?
后经过一番反思,之前应该是遇到了如下情况:
a: {
b: { c: any } | undefined
} | undefined
也就是说a和b都可能是undefined, 如果只写成a?.b.c这时候如果b为空,则依然会报错,所以必须写成a?.b?.c。只是之前不求甚解,故有此一劫。