16

As can be seen in the Mozilla changlog for JavaScript 1.7 they have added destructuring assignment. Sadly I'm not very fond of the syntax (why write a and b twice?):

var a, b;  
[a, b] = f();

Something like this would have been a lot better:

var [a, b] = f();

That would still be backwards compatible. Python-like destructuring would not be backwards compatible.

Anyway the best solution for JavaScript 1.5 that I have been able to come up with is:

function assign(array, map) {
    var o = Object();
    var i = 0;
    $.each(map, function(e, _) {
        o[e] = array[i++];
    });
    return o;
}

Which works like:

var array = [1,2];
var _ = assign[array, { var1: null, var2: null });
_.var1; // prints 1
_.var2; // prints 2

But this really sucks because _ has no meaning. It's just an empty shell to store the names. But sadly it's needed because JavaScript doesn't have pointers. On the plus side you can assign default values in the case the values are not matched. Also note that this solution doesn't try to slice the array. So you can't do something like {first: 0, rest: 0}. But that could easily be done, if one wanted that behavior.

What is a better solution?

Crescent Fresh
  • 115,249
  • 25
  • 154
  • 140
Anders Rune Jensen
  • 3,758
  • 2
  • 42
  • 53

4 Answers4

23

First off, var [a, b] = f() works just fine in JavaScript 1.7 - try it!

Second, you can smooth out the usage syntax slightly using with():

var array = [1,2];
with (assign(array, { var1: null, var2: null }))
{
   var1; // == 1
   var2; // == 2
}

Of course, this won't allow you to modify the values of existing variables, so IMHO it's a whole lot less useful than the JavaScript 1.7 feature. In code I'm writing now, I just return objects directly and reference their members - I'll wait for the 1.7 features to become more widely available.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Shog9
  • 156,901
  • 35
  • 231
  • 235
  • 2
    As of today `var [a,b] = [1,2];` results in a syntax error in Chrome. – Marcin Apr 06 '12 at 16:24
  • 9
    @Marcin: JavaScript 1.7 is a [Mozilla-only language](http://stackoverflow.com/questions/1330498/what-is-cross-browser-support-for-javascript-1-7s-new-features-specifically-ar). – Shog9 Apr 06 '12 at 17:25
  • 3
    Note that `with` can be dangerous, as [Mozilla's own documentation](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Statements/with) states "Using with is not recommended, and is forbidden in ECMAScript 5 strict mode. The recommended alternative is to assign the object whose properties you want to access to a temporary variable." – Michael Mior Apr 10 '13 at 13:59
  • This is what the OP is doing, @Michael – Shog9 Apr 10 '13 at 16:44
  • @Shog9 Indeed it is. My point was that there's not really anything wrong with it and that the alternative using `with` has some serious downsides. – Michael Mior Apr 13 '13 at 01:33
  • 2
    Will be available in ECMAScript 6 around mid 2015, see http://en.wikipedia.org/wiki/ECMAScript – pkopac Feb 25 '15 at 14:26
  • 2
    `with` is also disallowed in strict mode, which is a *really useful* mode to engage... – T.J. Crowder Apr 20 '15 at 14:31
  • 1
    As of today `var [a,b] = [1,2]` works in Chrome, Firefox, Edge, and Node. – Chris Sep 30 '16 at 01:17
3

You don't need the dummy "_" variable. You can directly create "global" variables by using the window object scope:

window["foo"] = "bar";
alert(foo); // Gives "bar"

Here are few more points:

  • I wouldn't name this function "assign" because that's too generic a term.
  • To more closely resemble JS 1.7 syntax, I'd make the function take the destination as the first argument and the source as the second argument.
  • Using an object literal to pass the destination variables is cool but can be confused with JS 1.7 destructuring where the destination is actually an object and not an array. I prefer just using a comma delimited list of variable names as a string.

Here's what I came up with:

function destructure(dest, src) {  
    dest = dest.split(",");  

    for (var i = 0; i < src.length; i++) {  
        window[dest[i]] = src[i];  
    }  
}  

var arr = [42, 66];  

destructure("var1,var2", arr); 

alert(var1); // Gives 42
alert(var2); // Gives 66
Ates Goral
  • 137,716
  • 26
  • 137
  • 190
  • 2
    The only thing I agree with is that destructure might be a better name. * source, destination is unix style. * populating global scope is not nice. It doesn't lead to composability. * Writing the output variables as a string is tiring and harder for your editor to check. Like writing SQL. – Anders Rune Jensen Oct 15 '08 at 18:31
  • @AndersRuneJensen Since this mimics an assignment, the destination should almost certainly be on the left—that way it sort of looks like `[var1, var2] = arr`. And `destructure` is an awful, non-intention-revealing name. I think `assign` is better—after all, this is *supposed* to be a generic utility function. If not, then maybe `massAssign`? – Marnen Laibow-Koser Jun 27 '14 at 19:36
1

Here's what I did in PHPstorm 10:

File -> Settings -> Languages & Frameworks -> ...

... set JavaScript language version to e.g. JavaScript 1.8.5...

-> click Apply.

Sam
  • 900
  • 10
  • 18
0

In standard JavaScript we get used to all kinds of ugliness, and emulating destructuring assignment using an intermediate variable is not too bad:

function divMod1(a, b) {
    return [ Math.floor(a / b), a % b ];
}

var _ = divMod1(11, 3);
var div = _[0];
var mod = _[1];
alert("(1) div=" + div + ", mod=" + mod );

However I think the following pattern is more idomatic:

function divMod2(a, b, callback) {
    callback(Math.floor(a / b), a % b);
}

divMod2(11, 3, function(div, mod) {
    alert("(2) div=" + div + ", mod=" + mod );
});

Note, that instead of returning the two results as an array, we pass them as arguments to a callback function.

(See code running at http://jsfiddle.net/vVQE3/ )

Eamonn O'Brien-Strain
  • 3,352
  • 1
  • 23
  • 33