使用 Javascript 可选链让代码更优雅
ECMAScript 11 中加入了可选链?.
和空值合并运算符??
,这两个符号的应用可以让代码更简洁和优雅。本文用几个例子说明这两个符号的使用,所有例子都来自于 root.js 标签库。
可选链?.
表示当前面的值不为null
或undefined
时才继续向下执行,否则整个表达式返回undefined
。空值合并运算符??
表示运算符左面的值为null
或undefined
时返回右面的值。下面看例子。
例一
v = tag[attr]?.toString() ?? t.getAttribute?.(attr);
可表示为当元素tag
包含attr
原生属性时,那么将属性值转成字符串,否则从标签属性列表中获取值。使用老方法,可以写成:
if (tag[attr] != undefined) {
v = tag[attr].toString();
}
else if (tag.getAttribute != undefined) {
v = tag.getAttribute(attr);
}
两个新符号将 6 行代码转成了 1 行代码。
例二
$('#PromptPopup')?.close?.();
可以表述为:当选择器$('#PromptPopup')
选择的元素存在且有close
方法时,则执行。老办法是这样写的:
let popup = $s('#PromptPopup');
if (popup != null && popup.close != undefined) {
popup.close();
}
例三
window[component.class]?.[component.method]?.(container);
可以表述为当定义了component.class
时,且包含方法component.method
时,那么就执行这个方法,传入参数container
。其中component.class
和component.method
均为字符串。
例四
tag.events?.get(eventName)?.forEach(func => {
//...
});
例子,events
是一个 Map 对象。老办法可以写成:
if (tag.events != undefined && tag.events.has(eventName)) {
tag.events.get(eventName).forEach(func => {
...
})
}
例五
这个例子例子比较复杂。
<button id="CreateButton" class="normal-button green-button"
onclick+="post:/api/user/team?name=$(#Name)&leader=$(#Leader) -> not-zero"
failure-text="# duplicate-team-tip #" hint="#SomethingWrong"
onclick+success="parent.$('#Catalog')?.selectedNode?.if(node => node.name ==
'teams')?.increase().reload()" href="/user/teams"># create-team #</button>
核心代码是onclick+success
事件,主要逻辑为:这个按钮在一个 iframe 页面中,当onclick+
服务器端事件请求的接口执行完成且返回值不为0
时,会触发onclick+success
事件。事件的主要逻辑为:
- 查找父级页面上有没有
#Catalog
,这一个是 TreeView 树形目录。 - 如果找到,则定位树形目录的选中的节点。
- 如果树形目录有节点被选中,则判断选中的节点是否满足
if
方法给定的条件。if
方法当条件满足时返回当前节点,不满足时返回null
。 - 如果以上条件都满足,那么执行节点的
increase
方法,即让树形节点的 tip 中的数字加 1。 - 最后执行
reload
方法,显示通过按钮刚刚添加的子节点。
如果没有可选链,实在没有办法将这么长的一个逻辑写成一句话放到属性值中。