benchpresser
benchpresser

Reputation: 2188

Javascript null check performance (compare if and try/catch)

I have to get a value of a inner component. Which is better 1 or 2 when compared ONLY for performance, this method is called several times. The foo may be null depending on the runtime, but foo may have a value but bar may be null etc. It is not an error case or unexpected case that any of foo, bar, innerfoo, innerbar are null, any of them can be null at the runtime depending on the hierarchy. But innerbar can not have any value of if foo is null so there is no need to check the inner components if foo is not defined.

//1
function getInnerValue() {
    if (foo && foo.bar && foo.bar.innerfoo && foo.bar.innerfoo && foo.bar.innerfoo.innerbar) {
        return foo.bar.innerfoo.innerbar.x;
    }
    return 0;
}

//2
function getInnerValue() {
    try {
        return foo.bar.innerfoo.innerbar.x;
    }
    catch (e) {
        return 0;
    }
}

Upvotes: 1

Views: 550

Answers (1)

user120242
user120242

Reputation: 15268

Anything with a try catch that ever gets triggered will incur a large performance hit.

Personally I don't find the long chain of truthy checks any less readable than a try catch block. Just longer, ugly and verbose. But really, would anyone ever have trouble reading that (or editing it)? Or better put, would you even need to read it to understand what it is doing?
In fact if anything the try catch block feels like more of a hack. I still have to think about why I'm trying to catch an exception, and it's still ugly.

In any case, we now have optional chaining, so the above doesn't even really matter anymore. I'd go with something like return x?.y?.z?.a?.b?.c?.d||0.

You can of course go with guard functions, as you've probably already noticed reading the thread you've linked. Something like lodash _.get or your own guard(a,'b','c','d').

optional chaining browser compatibility

Babel transpiled output of optional chaining

x?.y will immediately return undefined if x is null or undefined
Browser compatibility is relatively new. Check table of compatibility. You should add Babel transpilation to your pipeline.

optional chaining without transpilation:

foo = {}
function fn() {
  if (foo?.bar?.innerfoo?.innerbar)
      return foo.bar.innerfoo.innerbar.x
  return 0
}

console.log(fn())

foo = {bar: { innerfoo: { innerbar: { x: 5 }}}}

console.log(fn())

transpiled code:

foo = {}
function fn() {
  var _foo, _foo$bar, _foo$bar$innerfoo;

  if ((_foo = foo) === null || _foo === void 0 ? void 0 : (_foo$bar = _foo.bar) === null || _foo$bar === void 0 ? void 0 : (_foo$bar$innerfoo = _foo$bar.innerfoo) === null || _foo$bar$innerfoo === void 0 ? void 0 : _foo$bar$innerfoo.innerbar) return foo.bar.innerfoo.innerbar.x;
  return 0;
}

console.log(fn())

foo = {bar: { innerfoo: { innerbar: { x: 5 }}}}

console.log(fn())

Upvotes: 2

Related Questions