There seems to be a huge aversion to creating a function in JS. This aversion causes people try to be clever and use ridiculous tricks just to keep stuff in one line like a function call would have been. Of course the function name in a call acts as extra documentation too. We cannot attach a comment to a tricky expression because then it would defeat the point of doing it so we just call it "js idiom" and suddenly it is understandable.
Javascript is extremely accessible, most people don't eat specifications for breakfast like we do. So they will never understand what the hidden assumptions and edge cases of an idiom is.
x = x || 'default_value';
The average joe will either not understand this or has memorized that it is the idiom for default value. Both are harmful, in fact the latter is even more harmful. He will not understand the assumptions and edge cases here. He will not care to read the specification and understand it ever.
When I look at that code I see "if it's null or undefined, then set it to this default value. Although it will also implicitly treat +0, -0, NaN, false, and "" as not suitable values. I will have to remember that 3 months from now when that needs to change. I will probably forget it.".
The implicit assumption is extremely likely to cause a bug in the future and when your codebase is full of tricks like this then there is no chance you are keeping them all in your head whenever you are thinking about what a modification will affect. And this is for the "JS pro", the average joe would have written the bug even if the requirements were to accept a falsy value to begin with.
Your new snippet has more familiar syntax but still has the above problem.
You can go with:
function f(x) { x = valueOrDefault(x, "default_value"); }
Now you can have very complex logic to handle the edge cases and the client code still looks beautiful and readable.
Now, how do you differentiate between advanced language feature like passing a function as argument or a clever trick like || "default"?
Clever tricks are always operating under some hidden assumptions that could be ignored when the code was initially created. I will never have to modify an IIFE to something else because a requirement changed, it will always be there. Maybe in 2020 when I can use actual modules but yeah.
| 0 or the cargo cult version ~~num used for flooring assumes positive and 32-bit signed integer bounds.
|| "default" assumes all falsy values are same as not passing an argument at all.
And so on.