I'm using objects to namespace my JavaScript code. These objects usually contain functions that are called mapping the this
-pointer to the object itself using apply
. However, I find it inconvenient to use the this
-pointer everytime I want to access other functions or properties of the object, especially because in lots of cases I use the new
-operator to use function-objects the way you would use classes. I would prefer writing new Line()
instead if new this.Line()
.
It would be great if you could add local variables to a function the way php does it with extract
(pseudocode following, it's a little more plicated)
var sample_object = {
"some_function": function() {}
}
test() {
extract(sample_object);
some_function(); // imported from sample_object
}
Is that even possible?
I'm using objects to namespace my JavaScript code. These objects usually contain functions that are called mapping the this
-pointer to the object itself using apply
. However, I find it inconvenient to use the this
-pointer everytime I want to access other functions or properties of the object, especially because in lots of cases I use the new
-operator to use function-objects the way you would use classes. I would prefer writing new Line()
instead if new this.Line()
.
It would be great if you could add local variables to a function the way php does it with extract
(pseudocode following, it's a little more plicated)
var sample_object = {
"some_function": function() {}
}
test() {
extract(sample_object);
some_function(); // imported from sample_object
}
Is that even possible?
Share Improve this question asked Jan 19, 2012 at 20:01 LukasLukas 10.4k17 gold badges82 silver badges128 bronze badges 9-
3
You could use a
with
block, but, I don't suggest it. I also think it's removed in ES5 strict mode. – gen_Eric Commented Jan 19, 2012 at 20:04 -
1
@Rocket I'd uparrow the not suggesting it part if I could :-) It's true it'd work, but
with
is confusing and weird. Also I'm pretty sure it causes performance problems but I can't remember the explanation for that. – Pointy Commented Jan 19, 2012 at 20:11 - What if you pass an array as argument? – elvenbyte Commented Jan 19, 2012 at 20:19
- @elvenbyte: How would that help? – gen_Eric Commented Jan 19, 2012 at 20:21
-
2
@Rocket has the answer.
with
is what you want, though, it is forbidden from ES5 strict mode, and as @Pointy remarks, it does have performance problems: "with
forces the specified object to be searched first for all name lookups. Therefore all identifiers that aren't members of the specified object will be found more slowly in a 'with' block. Where performance is important, 'with' should only be used to enpass code blocks that access members of the specified object." (MDN) Other than usingwith
, there's no other way I know of. – rgthree Commented Jan 19, 2012 at 20:24
3 Answers
Reset to default 5I'm pretty sure eval
is your only answer; but you need to be aware that if there's any input outside of your control involved, it isn't safe
function dynamicArgs (varName, varValue) {
eval("var " + varName + "=" + JSON.encode(varValue) );
alert(a);
}
dynamicArgs("a", "value");
You can see the problem with this. How is your function supposed to call the dynamic variable if it doesn't know its name? I hardcoded it to the a variable since I pass it in when calling it, but that's not a good solution. The only solution would be another eval
. You really should think about what you need to do and whether this is useful. But it's doable.
Here it is in action: http://jsfiddle/mendesjuan/GG3Wu/
function dynamicArgs (varName, varValue) {
eval('var ' + varName + "='" + varValue + "';");
alert(eval(varName));
}
dynamicArgs("f", "Here I am");
Now here's an example like what you're doing, creating a variable from this.MyConstructor
http://jsfiddle/mendesjuan/AK3WD/
var ns = {
MyConstructor: function(val) {
this.prop = val;
},
runConstructor: function(val) {
var Ctor = "MyConstructor";
eval('var ' + Ctor + ' = this.' + Ctor);
return new MyConstructor(val);
}
}
alert( ns.runConstructor("Hello").prop );
And here's an example if you wanted to import all the values from an object into the scope;
http://jsfiddle/mendesjuan/AK3WD/1/
var ns = {
MyConstructor: function(val) {
this.val= val;
},
anotherProperty: 5,
runConstructor: function(val) {
// Bring all the variables from this into this scope
for (var prop in this) {
eval('var ' + prop + ' = this.' + prop);
}
alert('Testing var anotherProperty: ' + anotherProperty);
var obj = new MyConstructor(val);
alert('Created MyConstructor: its prop is ' + obj.val)
}
}
ns.runConstructor("Hello");
There is controversial with
, which has some great applications, but is marginally slow and prone to errors. It throws an error in the strict mode (which you should always opt into) and is going to be deprecated.
var sampleObject = {
someFunction: function() {},
b: 10
}
with (sampleObject) {
typeof someFunction // "function"
var a = 42
var b = 20
}
sampleObject.a // undefined
sampleObject.b // 20
Note, that new variables defined in a with
-block won't be added to the object. Nevertheless, if the object already had an eponymous property in it, this property would be modified (thanks, @Rocket).
Just for fun, here's an implementation of extract
using eval
(which is even more evil than with
). You can do unspeakable things with it, for example if your object has properties like sampleObject['x; while (true) { alert("Hi!") }']
.
This is how I did it:
function smObject ( object) {
return function () {
function getter(prop) {
return function() {
return this[prop];
}
}
function setter(prop) {
return function(data) {
this[prop]=data;
}
}
for (var o = 0; o < object.length; o++) {
this[object[o]] = {};
this['get' + object[o]] = getter(object[o]);
this['set' + object[o]] = setter(object[o]);
}
}
}
now you can instantiate a function like this:
var fields = ['Name', 'Id', 'Other', '....' ]
var MyFunction = smObject( fields );
var myObject = new MyFunction();
// getter/setters
myObject.setId(5);
myObject.getId(); // will return 5
Regards, Emanouil