JavaScript 中的可选链

21

我最近一直在使用Swift进行编程。今天当我在JavaScript中做一些工作时,有一个问题出现在我的脑海中:

JavaScript中是否有类似可选链的东西?一种能够防止undefined is not an object而不需要任何变量的方法吗?

例如:

function test(){
   if(new Date() % 2){
      return {value: function(){/*code*/}};
   }
} 

test().value();

有时候test会返回undefined,因此这个测试有一半的几率失败。

我能想到的唯一解决方法是编写一个函数:

function oc(object, key){
   if(object){
      return object[key]();
   }
}

oc(test(), 'value');

我希望能够做到像这样:

test()?.value()

如果test返回一个对象,问号后的部分才会被执行。

但这并不是很优雅。有更好的方法吗?一些神奇的运算符组合吗?

编辑 我知道我可以重写test来返回一个值。但我想知道是否有类似可选链的东西。我对上面示例中的特定解决方案不感兴趣。而是想找一些即使在无法控制返回undefined的函数时也能使用的东西。


@ZaheerAhmed 是 false 或 undefined,其实并不重要。 - idmean
@dandavis 但这需要我为每个要调用的函数编写一个包装器函数。而在这些函数中应该返回什么呢?(看看我的第二个例子) - idmean
1
我在想你可以在每个想要默认值的函数上使用相同的包装器,甚至只需要一个函数名称数组和一个map(),但也许我不理解目标。在JS中,函数非常灵活,因此不应该重复或使用样板文件。关于编辑,“(x=test()) && x.value()”比包装器更短,或者如果有时返回真值而不是对象,则为(x=test()) && x.value && x.value(); 但是,一个操作函数的可重用函数将留下更清晰的代码。 - dandavis
在JS中不可能实现,但是你可以使用&&来简化解决方法 - Teemu
2
是的,我知道。我只是想知道JavaScript是否有像可选链式调用这样的东西,因为JS充满了惊喜。 - idmean
显示剩余2条评论
8个回答

22

这是目前的第四阶段提案,你可以在此处查看其进展:
https://github.com/tc39/proposal-optional-chaining

你可以今天就使用Babel插件:
https://www.npmjs.com/package/babel-plugin-transform-optional-chaining

2020年1月11日更新:Babel 现在默认支持可选链操作符了。https://babeljs.io/blog/2020/01/11/7.8.0

可选链运算符的拼写方式为 ?.,它可以出现在三个位置:

obj?.prop       // optional static property access
obj?.[expr]     // optional dynamic property access
func?.(...args) // optional function or method call

注意:

为了兼容旧版本的语法,允许解析foo?.3:0foo ? .3 : 0,因此在词法语法层面添加了一个简单的向前查看,以便在这种情况下不将字符序列?.解释为单个token(?. token后不能紧跟着十进制数字)。

另外值得一提的是:

https://github.com/tc39/proposal-nullish-coalescing

https://github.com/babel/babel/tree/master/packages/babel-plugin-proposal-nullish-coalescing-operator


2
这应该是2020年的最佳答案。 - connexo

7
在纯JavaScript中,您必须进行类型检查或构造代码以确保对象存在。
CoffeeScript是一种编译为JavaScript的语言,如果您愿意考虑使用预处理语言,则提供了一个安全链接的存在运算符?.
这里还有另一个讨论关于为什么无法在JS中复制此行为
ESDiscuss论坛上也有一些关于将存在运算符添加到未来版本的JavaScript中的讨论。不过看起来进展并不是很大,肯定还远没有实际应用的地步。目前更多的是一个想法。

2
我接受了你的答案,因为它真正回答了我的问题,而不像下面的其他答案,并且提供了一些有趣的信息。 - idmean

5

可选链已经在JS中推出。我们可以通过对象属性访问中的 ?.运算符使用可选链。它允许我们尝试访问对象的属性,即使这些属性可能不存在(即为undefined),而不会引发错误。

以下是一个代码示例:

const obj = {
  foo: {
    bar: 1
  }
};

// If we try to access property which doesn't exists
// it just returns undefined
console.log(obj.baz);

try {
  // Now we try to access a property of undefined which throws an error
  obj.baz.foz;
} catch (e) {
  console.dir(e.message);
}

// Now we use the optional chaining operator ?.
// We get undefined instead of an error
console.log(obj.baz?.foz);
            
console.log(obj.foo?.bar);


4

您可以使用

test() && test().value();

或者

var testResult = test();
testResult && testResult.value();

如果你问我,这最类似于Swift的可选链接。

如果test()具有副作用或返回除nullundefined之外的假值,则test() && test().value()是不合适的。当然,如果test()没有value属性,或者其value属性不是函数,则它仍然会失败。 - Sebastian Simon

2

可选链终于加入了JavaScript标准!

以下是一些示例:

// properties
foo?.bar
foo?.bar()
foo?.bar.baz()
foo?.bar?.baz()

// indexing
foo?.[0]
foo?.['bar']

// check if a function is defined before invoking
foo?.()
foo.bar?.()
foo?.bar?.()

我觉得这比大多数人手动检查 null 值要好得多。

不是通过评估

foo?.bar

对于我们都习惯编写的这段小代码片段

foo ? foo.bar : null

它实际上被评估为。
foo == null ? undefined : foo.bar

适用于所有假值,如空字符串、0或false。

虽然与问题无关,但您可能也对??运算符感兴趣。

它与||的作用类似,但它仅检查null或undefined。

例如:

foo ?? bar

将会与以下内容相同:

foo != null ? foo : bar

这是一个非常新的功能,所以即使很多用户已经使用支持此功能的浏览器,您仍然需要使用工具将其转换为旧版本的javascript。


2

var Obj = {Prop: {name: 'peter'}}

console.log(Obj.Prop.name) console.log(Obj?.Prop?.name)

第一句话中,您只是访问对象属性。问题在于,如果您发现Prop不是对象,则会抛出异常。这就是可选链操作符的原因。

假设您尝试执行Obj.Prop2.name。 如果您改用Obj.Prop2?.name,则只会收到undefined作为值,而不是异常。

当访问深度嵌套的属性时,这特别有用。

警告:这是一个相对较新的JS功能,尚未在所有浏览器中实现,因此在使用它进行生产应用程序时要小心。


0

如果测试是一个对象方法,你总是可以使用return this;

但是为什么要通过创建模拟函数来防止这样的错误呢?


OP询问了一个关于安全链接的通用问题,并不是如何构建一个特定函数以确保安全。 - Ben McCormick

0

如果条件不满足,返回一个什么也不做的noop函数怎么样?

function test(){
   if(new Date() % 2){
      return {value: function(){/*code*/}};
   }
   return {value: function(){ /* just return a type consistent with the function above */ }
} 

这只是一个例子。我说的是我无法控制的函数。 - idmean
抓住了。抱歉。你需要像Groovy中的?.操作符一样的东西。在JS中没有这样的东西。 - sebnukem
1
@sebnukem - 现在有一个操作员的提案 - 请看我的答案。 - Jon Wood

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接