在 JavaScript 中是可选的(空安全)。
由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!
昨天我偶然看到了StackOverflow 上的这个问题,它让我开始思考如何null在undefinedJavaScript 中处理这个问题。
简要背景
什么是“ undefined?”? undefined这是一个原始值,它仅用于已声明的变量、不存在的属性或函数参数。
“ null?” null是另一个表示无值的原始值。
那么,当我们执行以下操作时会发生什么呢?
let obj;
console.log(obj.someProp);
我们收到以下错误
TypeError:无法读取未定义对象的属性“someProp”。
同样的情况也发生在……null
空值检查
那么,我们该如何避免这种情况呢?幸运的是,JavaScript 有short-circuit求值机制,这意味着为了避免之前的问题,TypeError我们可以这样写。
let obj;
console.log(obj && obj.someProp); // Prints undefined
但如果我们想更深入地了解情况呢?比如…… obj.prop1.prop2.prop3?我们最终会做很多检查,例如:
console.log( obj && obj.prop1 && obj.prop1.prop2 && obj.prop1.prop2.prop3 );
感觉很讨厌,不是吗?
如果我们想在链式调用中存在“undefined或”运算符时打印默认值呢null?那么表达式就会变得更大:
const evaluation = obj && obj.prop1 && obj.prop1.prop2 && obj.prop1.prop2.prop3;
console.log( evaluation != null ? evaluation : "SomeDefaultValue" );
其他语言是如何实现的?
这个问题并非 JavaScript 独有,大多数编程语言都存在这个问题,所以让我们来看看如何在一些编程语言中进行空值检查。
Java
Java 中有以下OptionalAPI:
SomeClass object;
Optional.ofNullable(object)
.map(obj -> obj.prop1)
.map(obj -> obj.prop2)
.map(obj -> obj.prop3)
.orElse("SomeDefaultValue");
Kotlin
在 Kotlin(另一种 JVM 语言)中,有Elvis ( ?:) 和安全调用( ?.) 运算符。
val object: SomeClass?
object?.prop1?.prop2?.prop3 ?: "SomeDefaultValue";
C#
最后,在 C# 中,我们还有空条件运算符( ?.) 和空合并运算符( ??)。
SomeClass object;
object?.prop1?.prop2?.prop3 ?? "SomeDefaultValue";
JS选项
看完这些之后,我就在想,有没有办法避免写那么多 JavaScript 代码呢?于是我开始尝试用正则表达式编写一个函数,让我可以安全地访问对象的属性。
function optionalAccess(obj, path, def) {
const propNames = path.replace(/\]|\)/, "").split(/\.|\[|\(/);
return propNames.reduce((acc, prop) => acc[prop] || def, obj);
}
const obj = {
items: [{ hello: "Hello" }]
};
console.log(optionalAccess(obj, "items[0].hello", "def")); // Prints Hello
console.log(optionalAccess(obj, "items[0].he", "def")); // Prints def
之后,我发现了lodash._get具有相同签名的[此处应填写签名]:
_.get(object, path, [defaultValue])
但说实话,我不太喜欢字符串路径,所以我开始寻找避免使用它们的方法,然后我想到了一种使用代理的解决方案:
// Here is where the magic happens
function optional(obj, evalFunc, def) {
// Our proxy handler
const handler = {
// Intercept all property access
get: function(target, prop, receiver) {
const res = Reflect.get(...arguments);
// If our response is an object then wrap it in a proxy else just return
return typeof res === "object" ? proxify(res) : res != null ? res : def;
}
};
const proxify = target => {
return new Proxy(target, handler);
};
// Call function with our proxified object
return evalFunc(proxify(obj, handler));
}
const obj = {
items: [{ hello: "Hello" }]
};
console.log(optional(obj, target => target.items[0].hello, "def")); // => Hello
console.log(optional(obj, target => target.items[0].hell, { a: 1 })); // => { a: 1 }
未来
目前, TC39有一项提案,允许我们执行以下操作:
obj?.arrayProp?[0]?.someProp?.someFunc?.();
看起来很棒,对吧?不过,这个方案目前还处于第一阶段,这意味着我们可能还需要一段时间才能看到它用 JavaScript 实现。尽管如此,目前有一个 Babel插件可以让我们使用这种语法。
终曲
null空安全的概念已经存在一段时间了,而且我敢肯定它是编程中最令人讨厌的概念之一。然而,还是有办法实现空安全。以下是我的一些看法,欢迎大家留言讨论,或者分享你们的其他方案。
附:这里有个小概要
文章来源:https://dev.to/pichardoj/optional-null-safe-in-javascript-1b7k