imho, the best practice is to steal ideas from FP: Monadic Options and Results. They enable you to leave out try..catch and enforce proper error management, but at the same time allow for very data-driven patterns.
Why not try..catch? Try..catch is an agreement; you can use it, but it is not enforced, even if the called function might throw. Also, browser, which do not use V8 Turbofan have trouble optimizing functions with try..catch statements. Additionally, try..catch has to unwind the stack, which is very unperformant. Last but not least, try..catch uses side-channel magic. You have no influence over the way errors are passed. They are magically thrown and propagated in many parts of the JS interpreter and the whole process is only accessible in small parts of your script.
Unfortunately, I do not understand what you mean by if-statements. They can be used to tests a condition, but that requires proper error management to be in place (for example by using monads). Otherwise, you might also have a bad time, because JS has so many types and error-types (like undefined vs null, typeof NaN === 'number' , etc.) that you will probably introduce bugs inside your error management code, which is very bad.