3

In a JavaScript app, suppose I have a nested object like this:

var myObject = {
    someProp: {
        someOtherProp: {
            anotherOne: {
                yetAnother: {
                    myValue: "hello!"
                }
            }
        }
    }
}

In another part of the code, I'm calling myValue multiple times (I just call it three times in this example but imagine there's a lot more):

someFunc() {
    doSomething(myObject.someProp.someOtherProp.anotherOne.yetAnother.myValue)

    doSomethingElse({
        arg1: "something",
        arg2: myObject.someProp.someOtherProp.anotherOne.yetAnother.myValue
    })

    if (myObject.someProp.someOtherProp.anotherOne.yetAnother.myValue == "hola") {
        doStuff()
    }

}

Apart from the obvious readability and maintainability gains, is it actually faster to do it like this:

someFunc() {
    let val = myObject.someProp.someOtherProp.anotherOne.yetAnother.myValue

    doSomething(val)

    doSomethingElse({
        arg1: "something",
        arg2: val
    })

    if (val == "hola") {
        doStuff()
    }

}

Or is it pretty much the same, behind the scenes?

In other words, does the interpreter have to walk the whole myObject tree and search for each nested property each time, in case 1, or does it somehow cache the value, or is there another mechanism that makes it as fast as case 2?

Context: any modern browser (Chrome, Safari, Firefox, Edge).

Jivan
  • 315
  • 1
  • 8
  • 6
    I'm guessing this is one of those questions, where the only answer is; "the spec doesn't say anything about how the interpreter should implement this feature, so it might already be caching stuff for you", in which case it's a matter of "do what's most readable" and "profile your code to see". Either way, I don't think you really want to think about this, as it seems like a micro-optimization. – cwap Jul 13 '16 at 12:42
  • @cwap it's just curiosity, nothing more. – Jivan Jul 13 '16 at 12:46
  • 2
    One reason to do this is to avoid having the variable change between the calls. If it's important that the value stay the same across the calls, you should definitely create a variable. Obviously, if you need to pass in changed values across the calls, this is a bad idea but I would avoid that kind of design because it's extremely hard to follow code like that. – JimmyJames Jul 13 '16 at 13:15
  • 1
    Possible duplicate of [Is micro-optimisation important when coding?](http://programmers.stackexchange.com/questions/99445/is-micro-optimisation-important-when-coding) – gnat Jul 13 '16 at 13:25
  • 1
    @jivan Curiosity is always nice, and it's a valid question imho. I was just trying to say that you probably shouldn't expect a "good" answer. – cwap Jul 13 '16 at 13:30
  • @gnat thanks for pointing this, but it's a totally different question - it's about some very specific part of the internal workings of modern JavaScript engines, and is not about micro-optimisations *per se* – Jivan Jul 13 '16 at 14:08
  • The problem you run into is that myObject.someProp.someOtherProp.anotherOne.yetAnother.myValue could be a getter with sideeffects. That would mean your first and second example don't always have to give the same result. – Pieter B Jul 13 '16 at 14:50
  • The actual issue here is likely another one. If you need to go deep into objects into a function you may have a function which is too big. You might want to consider if this function has a single responsibility at all. A hard coded path like that would be a maintenance issue likely. If you need that value it should likely be the parameter of the function. Off course we need full context to give a full answer. Just the performance itself at first priority should not worry you, JavaScript is objects which is performing very well in general. – Luc Franken Jul 13 '16 at 19:26
  • 1
    @Jivan: You're the only one who sees the duplicate header in the question (it is system-generated), and gnat's comment is inconsequential. – Robert Harvey Jul 13 '16 at 22:36

3 Answers3

7

Well, on the most basic level, val has fewer characters in it than myObject.someProp.someOtherProp.anotherOne.yetAnother.myValue so, simply parsing it will be (trivially) faster.

On the specification level, myObject.someProp.someOtherProp.anotherOne.yetAnother.myValue is the equivalent of myObject["someProp"]["someOtherProp"]["anotherOne"]["yetAnother"]["myValue"] which implies a lookup at each level. But, you knew that.

So, the question is, "Do some modern browsers' JavaScript engines handle this as a special case?"

My opinion (wild-ass guess) is a) Probably Not, b) if they do, it would be for one, maybe two levels, not five. c) support would vary wildly amongst the different browsers, and, most importantly, d) the browser vendors will never give you specifics about it, because if they did, they'd be bound to support that implementation detail forever.

James Curran
  • 1,809
  • 1
  • 10
  • 4
  • "if they did, they'd be bound to support that implementation detail forever" except V8 source is available for perusal. They regularly detail current implementations but not in an official manner as to suggest that it's a permanent API or behavior. – zzzzBov Jul 14 '16 at 17:52
6

The optimization you are performing by hand is called common subexpression elimination. According to this article, Chrome's V8 has performed this operation since at least 2011; Webkit's JIT also does it; SpiderMonkey, the JS engine in Firefox also does it. I haven't found a good description of the optimization performed by Chakra, Edge's JIT, but the source code is online and the function located here appears to perform the relevant optimization.

It therefore seems that there is little point in doing this for optimization purposes (at least for desktop browsers -- note that mobile browsers in many cases do not have JIT compilers, or if they do they are much more basic). But it's probably worth doing anyway in order to make your code more readable.

Jules
  • 17,614
  • 2
  • 33
  • 63
2

For speed: Measure.

However, the temporary variable has the huge advantage that as a reader, I know that all the places using the temporary value use the same actual value. If the same very long expression is used in multiple places, I don't know for sure that (a) the very long expression is actually the same in each place or has a subtle difference (for example, there could be "yetAnother" used in one expression and "yetanother" in another), (b) the very long expression has no side effects so executing it multiple times is different from executing it once, (c) none of the code running in between does anything that changes the result of evaluating the expression.

In many development systems, it may also be easier during debugging to see the value of the very long expression when it is stored in a variable. If during debugging I want to see how the very long expression is evaluated, I need to set a breakpoint in one place only. If the very long expression changes due to changed requirements, I have only one place to change. So it is often better to use a variable. In the three cases mentioned above (slight differences, execution has wanted side effects, value may change) that would be something unexpected and would need commenting so nobody changes the code.

gnasher729
  • 42,090
  • 4
  • 59
  • 119