void
void

Reputation: 36703

Programmatic Destructuring

I have a different use case in which when using the concept of destructuring I want the name of new variable to come from different variables.

For ex (usual destructuring):

var o = {p: 42, q: true};
var {p: foo, q: bar} = o;
console.log(foo, bar); // Works fine

Is working fine but when I try to replace p, foo, q, bar with some other variable I get the error because I can not get the syntax right.

The closest I have reached is:

var o = {p: 42, q: true};
var x1 = "p";
var x2 = "foo";
var x3 = "q";
var x4 = "bar";
var {[x1]: foo, [x3]: bar} = o;
console.log(foo, bar);

Othe things which I tried but didnt work are:

var {[x1]: window[x2], [x3]: bar} = o; //Uncaught SyntaxError: Unexpected token [
var {[x1]: (()=>x2)(), q: bar} = o; //Uncaught SyntaxError: Unexpected token (

But here I am still not able to replace foo, bar with x2, x4.

Any idea how can one achieve this? Or is it even possible to do this?

Upvotes: 4

Views: 234

Answers (2)

thefourtheye
thefourtheye

Reputation: 239573

It was answered perfectly in this comment. I'll try to explain with the specification details.

When we declare a variable, like this

var {[x1]: window[x2], [x3]: window[x4]} = o;

I removed all the subscripted text from the spec for brevity

it would follow the following production as per Variable Statement Section,

VariableStatement:
    var VariableDeclarationList;

VariableDeclarationList:
    VariableDeclaration
    VariableDeclarationList, VariableDeclaration

VariableDeclaration:
    BindingIdentifier Initializer
    BindingPattern Initializer

As {[x1]: window[x2], [x3]: window[x4]} is not a BindingIdentifier, it is a BindingPattern. And that production goes like this

BindingPattern:
    ObjectBindingPattern
    ArrayBindingPattern

ObjectBindingPattern:
    {}
    {BindingRestProperty}
    {BindingPropertyList}
    {BindingPropertyList, BindingRestProperty}

...
...

BindingPropertyList:
    BindingProperty
    BindingPropertyList, BindingProperty

...
...

BindingProperty:
    SingleNameBinding
    PropertyName: BindingElement

BindingElement:
    SingleNameBinding
    BindingPattern Initializer

SingleNameBinding:
    BindingIdentifier Initializer

In this case ({[x1]: window[x2], [x3]: window[x4]}), the production goes like this BindingPattern -> ObjectBindingPattern -> {BindingPropertyList} -> BindingProperty -> PropertyName: BindingElement -> SingleNameBinding -> BindingIdentifier.

At this point, window[x2] is not a BindingIdentifier. That is why you are getting the SyntaxError, Uncaught SyntaxError: Unexpected token [.


But it works in case of ({[x1]: window[x2], [x3]: window[x4]} = o) because, it is not a variable declaration anymore. It is a destructuring assignment.

Production for that goes like this

AssignmentPattern:
    ObjectAssignmentPattern
    ArrayAssignmentPattern

ObjectAssignmentPattern:
    {}
    {AssignmentRestProperty}
    {AssignmentPropertyList}
    {AssignmentPropertyList, AssignmentRestProperty}

AssignmentPropertyList:
    AssignmentProperty
    AssignmentPropertyList, AssignmentProperty

AssignmentProperty:
    IdentifierReference Initializer
    PropertyName: AssignmentElement

AssignmentElement:
    DestructuringAssignmentTarget Initializer

DestructuringAssignmentTarget:
    LeftHandSideExpression

LeftHandSideExpression:
    NewExpression
    CallExpression

CallExpression:
    CoverCallExpressionAndAsyncArrowHead
    SuperCall
    CallExpression Arguments
    CallExpression[Expression]
    CallExpression.IdentifierName
    CallExpression TemplateLiteral

Here the production is complete with CallExpression[Expression]. That is why ({[x1]: window[x2], [x3]: window[x4]} = o) is valid and it is working.

Upvotes: 1

ASDFGerte
ASDFGerte

Reputation: 5193

You need an lvalue to assign to, but from what i know, you cannot declare dynamically generated variable names, as in let [dynamicName] = value. However, object properties are an alternative, which can dynamically be generated:

var o = {p: 42, q: true};
var container = {};
var x1 = "p";
var x2 = "foo";
var x3 = "q";
var x4 = "bar";
({[x1]: container[x2], [x3]: container[x4]} = o);
console.log(container.foo, container.bar);

On global scope, you can abuse the fact that the global object is being used as object environment, and use window[x2] and window[x4], but it's not really useful in production code.

Upvotes: 2

Related Questions