Why doesn't logical OR work with error throwing in JavaScript?

前端 未结 5 1087
慢半拍i
慢半拍i 2021-02-06 20:41

This is a pretty common and useful practice:

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

// default via a function call
var myval = () => 1
var          


        
5条回答
  •  南方客
    南方客 (楼主)
    2021-02-06 21:02

    As other answers have stated, it is because throw is a statement, which can't be used in contexts which expect expressions, such as on the right side of a ||. As stated by others, you can get around that by wrapping the exception in a function and immediately calling it, but I'm going to make the case that doing so is a bad idea because it makes your intent less clear. Three extra lines of code is not a big deal for making the intent of your code very clear and explicit. I personally think that throw being statement-only is a good thing because it encourages writing more straightforward code that is less likely to cause other developers to scratch their heads when encountering your code.

    The || defaulting idiom is useful when you want to provide default or alternative values for undefined, null, and other falsy values, but I think it loses a lot of its clarity when used in a branching sense. By "branching sense", I mean that if your intent is to do something if a condition holds (the doing something in this case being throwing an exception), then condition || do_something() is really not a clear way to express that intent even though it is functionally identical to if (!condition) {do_something()}. Short-circuit evaluation isn't immediately obvious to every developer and || defaulting is only understood because it's a commonly-used idiom in Javascript.

    My general rule of thumb is that if a function has side effects (and yes, exceptions count as side effects, especially since they're basically non-local goto statements), you should use an if statement for its condition rather than || or &&. You're not golfing.

    Bottom line: which is going to cause less confusion?

    return value || (() => {throw new Error('an error occurred')})()
    

    or

    if (!value) {
        throw new Error('an error occurred')
    }
    return value
    

    It's usually worth it to sacrifice terseness for clarity.

提交回复
热议问题