2

In the following JavaScript, does the object destructuring that follows the creation of target create a new (temporary) object that has to be garbage collected? In other words, are there two objects being created here or just one? I know that from my perspective, a and b are just variables (not objects), but I'm interested in whether JavaScript has to create a throwaway object every time I destructure.

const target = {
    a: 123,
    b: 456
};

const { a, b } = target;

To be super duper sure that no extra object is created, I could do this:

const a = target.a;
const b = target.b;

But I'm wondering if object destructuring gets optimized to that.

4
  • There is no reason to create a new object. And I don't see no reason to assume there would be one. Commented Oct 13 at 16:17
  • 1
    Your 2nd code block is what actually runs (more or less) for a simple case like this. Note that you can also specify property renames, default values and/or nesting of values, which makes it more extensive but no temporary objects are allocated in any such case - except possibly for temporary arrays of the property names being used. Commented Oct 13 at 16:18
  • 1
    I can't even imagine what the "throwaway object" would be used for. What is your imagined implementation? Commented Oct 13 at 16:22
  • 1
    If there's nested destructuring, there might be a throwaway internal variable that points to the nested object, to avoid following the same chain of properties multiple times. This is more of an optimization, and might be done as well if you write out the assignments explicitly, e.g. const a = target.sub.a, b = target.sub.b; is optimized to const temp = target.sub, a = temp.a, b = temp.b; Commented Oct 13 at 16:26

1 Answer 1

2

No. The curly brace pair of { and } characters on the left hand of a destructuring assignment is a syntax mechanism to tell the compiler to expect a object on the right hand side of the = sign. It is not the same syntax as used to create a new object. In the case of the example shown

const {a, b} = someObject;

in long winded terms is telling the compiler to create two constant variables a and b and assign them values of the same named properties of the object on the right hand side of the assignment statement. More briefly it's telling the compiler to produce the code equivalent of the older syntax version:

const a = someObject.a;
const b = someObject.b;

Destructuring an Array object is similar in that the [ and ] characters on the LHS aren't defining an array but are telling the compiler to expect an iterable object on the RHS of the assignment. For example

const [a, b] = someArray;

is telling the compiler to generate code equivalent to

const a = someArray[0];
const b = someArray[1];

Note destructuring an iterable object (typically an Array object) in conjunction with using a rest operator (...) on the LHS of the assignment, will require creating a symbol iterator to inspect properties in sequence at run time. Thanks to @VLAZ for the pseudo code:

const _temp = someArray[Symbol.iterator]();
const a = _temp.next().value
const b = _temp.next().value

If the compiler generates this code for the JS engine to run, then the iterator held in _temp would need to be GC'ed afterwards. However it may be worth remembering that the ECMAScript specification of JavaScript is devoid of implementation details and if the JS engine were to call native code for some or all of the steps needed to implement rest operands then maybe requisite temporaries might not be on the heap and in need of GC later.

For context an example of JS array destructuring with a rest operator:

const someArray = [0,1,2,3];
let [a, b, ...c] = someArray; // as a change from const variables
console.log(`a = ${a}, b = ${b}, c = [${c}]`);

TL:DR:

  • The braces on the LHS of a destructuring assignment are syntactical to determine if an object (curly braces), or an iterable object or string ("[]" braces, is on the RHS of the assignment operator.
  • Destructuring syntax does not itself imply a temporary object, plain or Array, needs to be created.
  • Use of a rest operator (...) when destructuring an iterable on the RHS implies the need to create an iterator object at run time. It is not specified whether such iterator would used in native or JS code, or whether it would reside on the heap or call stack. Subsequent garbage collection of its memory may or may not be required depending on JS engine implementation.
  • Use of a rest operator when destructuring an ordinary object, if implemented in run time JavaScript is likely to call Object.keys or Object.entries to collect properties of the object that weren't assigned to variables. Such run time calls would create return values that would need to be GC'd after the rest operator completes. Again it all depends on how the JS engine implements the rest operator.
  • The consensus is that trying to optimize array or object destructuring is unlikely to be worthwhile.
Sign up to request clarification or add additional context in comments.

5 Comments

"Note destructuring an iterable object (typically an Array object) allows the use of the rest operator (...) on the left hand side to create a new variable set to a new Array object, populated with unconsumed entries of the iterable object on the RHS" same for object destructuring syntax const { a, b, ...rest} = obj will still collect the remaining key-values.
As a separate note on array destructuring const [a, b] = someArray; is not equivalent to const a = someArray[0]; const b = someArray[1];. Array destructuring will try to open the iterator of the RHS, then extract values from it. So it's more like const _temp = someArray[Symbol.iterator](); const a = _temp.next().value; const b = _temp.next().value; There is a bit of error handling, if the iterator is exhausted early, though. But array destructuring will create a new object, it's not the same as indexed access. const { 0: a, 1: b } = someArray; is going to be the same as the index.
With all that said, it's probably not worth trying to optimise array destructuring. Still, it's useful to know it uses a completely separate mechanism for extracting RHS values.
@VLAZ Thank you for your feedback: your first comment is correct and I misunderstood a testing error. Per you second comment that extra object would be a symbol iterator object (_temp) - which I imagine would need GC to dispose of. Do you concur? There remains the question of whether generated code could use the JS engine to call native code to execute some or all of the steps without executing them in JS pseudo code. I feel I need to change the "no" in the answer to a definite "maybe" even though the technical reason would be different from question comments.
"per you second comment that would be a symbol iterator object (_temp) - which I imagine would need GC to dispose of. Do you concur?" correct, that's why I brought it up. I probably should have been more explicit. So, yes specifically array destructuring can leave an object to GC. However, I'd expect there is not much benefit to optimising there.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.