为什么逻辑OR不能与JavaScript中的错误一起使用?

ttu*_*lka 50 javascript

这是一种非常常见且有用的做法:

// default via value
var un = undefined
var v1 = un || 1

// default via a function call
var myval = () => 1
var v2 = un || myval()
Run Code Online (Sandbox Code Playgroud)

但是在抛出错误时它不起作用(SyntaxError):

var v3 = un || throw new Error('un is not set!')
Run Code Online (Sandbox Code Playgroud)

有没有办法以同样优雅的方式达到同样的效果?这是恕我直言的很多样板代码:

if (!un) {
    throw new Error('un is not set!')
}
var v3 = un
Run Code Online (Sandbox Code Playgroud)

或者是否有任何理论障碍,为什么这不是,也永远不可能?

Cer*_*nce 83

throw只是一个声明 ; 它可能不存在于需要表达式的位置.出于类似的原因,例如,你不能在if那里发表声明

var something = false || if (cond) { /* something */ }
Run Code Online (Sandbox Code Playgroud)

也是无效的语法.

只允许将表达式(计算为值的事物)分配给变量.如果你想throw,你必须throw为一份声明中,这意味着你不能把它赋值的右手边.

我想一种方法是在右侧使用IIFE ||,允许您在该函数的第一行使用语句:

var un = undefined
var v2 = un || (() => { throw new Error('nope') })();
Run Code Online (Sandbox Code Playgroud)

但那很奇怪.我更喜欢明确的if- throw.

  • 实际上,已经有[阶段2提议](https://github.com/tc39/proposals#stage-2)将throw表达式添加到该语言中.唉,它还有一段时间才可以使用. (5认同)
  • 我们的项目在Typescript中,所以模式可能更透明 - 我们有一个核心实用程序模块,包括`export function crash(message:string):never {throw new Error(message); }和所有抛出都是通过这个函数完成的.有用,因为现在它是一个表达式(并且`never`返回注释表明它不会因为它抛出而返回),并且因为我们可以在其中放置一个断点(我们有一个高级catch块来产生自定义错误消息而不是而不仅仅是它被发送到控制台,但这可以防止调试器在抛出时破坏). (4认同)
  • 值得指出的是,`throw`*可能已经成为一种表达方式.也许未来的语言版本将启用此模式. (3认同)

Pat*_*eck 32

你的问题是一个赋值需要一个表达式,但你给它一个语句

初始化/赋值变量的语法是:

var|let|const <variableName> = <expression>
Run Code Online (Sandbox Code Playgroud)

但你用

var|let|const <variableName> = <statement>
Run Code Online (Sandbox Code Playgroud)

这是无效的语法.

表达式

表达式是产生值的东西.

什么是"价值"?

值是Javascript中的任何类型

  • 数字
  • 字符串
  • 布尔
  • 对象
  • 数组
  • 符号

表达式的示例:

字面

var x = 5;
Run Code Online (Sandbox Code Playgroud)

x 被赋值为"5"

一个函数调用

var x = myFunc();
Run Code Online (Sandbox Code Playgroud)

myFunc() 生成一个分配给x的值

函数的生成值是它的返回值 - 函数总是返回,如果没有显式,则返回undefined.

函数具有额外的好处,能够在其正文中包含语句 - 这将是您的问题的解决方案 - 但稍后会更多.

声明

声明是执行操作的内容.例如:

一个循环

for (var i = 0; i < 10; i++) { /* loop body */ }
Run Code Online (Sandbox Code Playgroud)

该循环执行循环体10次的动作

抛出一个错误

throw new Error()
Run Code Online (Sandbox Code Playgroud)

展开堆栈并停止当前帧的执行

那么为什么我们不能混合两者呢?

如果要分配给变量,则需要表达式,因为您希望变量具有值.

如果你考虑一下,应该很清楚,它永远不会用于声明.给变量一个"动作"是无稽之谈.那甚至应该是什么意思?

因此,您不能使用该throw语句,因为它不会产生值.

你只能拥有一个或另一个.无论是你的are (expression)东西还是你的do (statement)东西.

修复

您可以通过将任何语句包装到函数中来将其转换为表达式,我建议使用IIFE (Immediately invoked function expression)- 基本上是一个调用自身的函数 - 来做到这一点

var x = 5 || (() => throw new Error())()
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为右侧现在是一个函数,而函数是一个产生值的表达式.

未来的可能性

从技术上讲,没有任何东西阻止这种工作.

许多语言(c ++,...)实际上已经被undefined视为表达式.有些人(kotlin,......)甚至完全忽略了陈述,并将一切都视为表达.

其他(c#,php,...)提供了类似thrownull隐藏或??elvis运算符的解决方法来解决这个用例.

也许在未来我们会将这些功能中的一个纳入ecmascript标准(甚至有一个包含此功能的公开提议),直到那时你最好的选择是使用如下功能:

function assertPresent(value, message)
{
  if(!value) {
    throw new Error(message);
  } else {
    return value;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 顺便说一下,C#6允许"throw"成为一个表达式,以准确启用这样的场景 - 表达式的返回类型是从上下文推断出来的.我想像这样的东西在概念上更容易添加到JavaScript中,因为它不会在编译时检查返回类型. (6认同)
  • 它也是一个表达式(类型为“ void”)[在C ++中](https://en.cppreference.com/w/cpp/language/throw#Notes)。 (2认同)

Nin*_*olz 20

您可以将异常的抛出移动到函数中,因为它throw是控制流的声明,而不是表达式:

一个表达式是代码的任何有效单元,其解析为一个值.

const throwError = function (e) { throw new Error(e); };

var un = undefined,
    v3 = un || throwError('un is not set!');
Run Code Online (Sandbox Code Playgroud)

  • 为什么不直接使用`const throwf = function(err){throw err}`然后它可以在任何地方使用. (7认同)