javascript unpacking an object?
coming from clojure I nee开发者_Go百科d to write some javascript functions
Lets say I have a function that acts on some spec object.. I want to refer to the variables in spec in a local scope without having to write spec.container, spec.nrow, spec.ncol. It seems there is no native support for destructuring, is this correct? If so how would I write an unpack (depth 1) function as below? - I don't really want to use eval in it either.
var spec = {container: {width: 900, height: 600}, nrow: 4, ncol: 5, vgap: "5px"}
function grid(spec) {
// bad..
var width = spec.container.width;
var height = spec.container.height;
var nrow = spec.nrow;
etc...
// I would prefer to write this, then have local access to nrow, width, height, etc
unpack(spec);
unpack(container);
}
Thanks
You can do what you want using the following:
var spec = {container: {width: 900, height: 600}, nrow: 4, ncol: 5, vgap: "5px"}
function grid(obj) {
// I would prefer to write this, then have local access to nrow, width, height, etc
unpack(obj, this);
unpack(container, this);
//do stuff here...
}
function unpack(obj, dest) {
for (var key in obj) {
dest[key] = obj[key];
}
}
Here is a working example: http://jsfiddle.net/Dsaaw/
The with() { }
construct was designed for this, but it's widely considered a dangerous language element, as the rules for scope-conflict are not well defined.
I'd say either use the fully-qualified object reference, or live with the one-by-one assignment.
I'm a big fan of Coffeescript, which has "native" destructuring assignment in that it compiles to the equivalent JavaScript. I don't think there's a really elegant solution that's strictly pure JS, though. The CS would look like this
{ nrow, container: { width, height } } = spec
The JavaScript that this produces when compiled will be:
var height, nrow, width, _ref;
nrow = spec.nrow, _ref = spec.container, width = _ref.width, height = _ref.height;
aroth's solution is neat, and I was going to suggest it, actually, but I think you run a risk of scope issues. Worth a try, though.
If you want to extend this
with your other object, then aroth's solution is pretty okay, especially with RobG's comment, but just make sure you know what object you are extending. For deeply nested function (whose parent is not window
) you would have to say this.width
rather than spec.width
so that doesn't help you much, in terms of typing. Or you could not pass in this
and just make global variables on window
(which is essentially what is going on), which may demonstrate why this approach could be dangerous-ish.
if you want locally scoped variables to the function, then I think you pretty much have to use an eval.
function decomp(obj) {
var result = []
for(var i in obj) {
switch(typeof(obj[i])) {
case "number":
case "boolean":
case "string":
result.push("var "+i+"="+obj[i].constructor.name+"(\""+obj[i].valueOf()+"\");")
}
}
return result.join('');
}
var someObject = {
worker: function(obj) {
eval(decomp(obj));
eval(decomp(obj.container));
alert(width);
}
}
var spec = {container: {width: 900, height: 600}, nrow: 4, ncol: 5, vgap: "5px", test: true }
someObject.worker(spec)
Play around with it here here
I know that you said you would prefer not to use eval()
but I can't think of any other way to really do what you want. Its between eval()
, extending this
(which may or may not be window
), or writing a js pre-processor that unrolls it for you.
精彩评论