Declare variable based on object value in javascript - javascript

Simple issue I ran up against today and can't seem to find a way around it, whatever the solution is it's something I've never learned so leaving me stumped.
For reasons I won't get into, I need to get a value from an object and use it as the name of a new variable. Will be looping through a ton of these so needs to be repeatable. For example from an object like so,
obj = {
"name": "object101"
};
What I want to get out of that is a var declaration in such a way that object101 is the name of the new var,
"var object101 = // stuff"
But "var obj.name = // stuff" is not working, the dot is not valid it seems.
I also tried putting the object101 into it's own var like, var name = obj.name;.
Which holds the content fine. But then I immediately saw the hilarious problem with going, var name = // stuff
It just redefines "name" and is not placing the content of name. It does render right when in a console log like console.log("var "+name+" = stuff"); which is pretty much exactly how I want it to write to the code, but having this issue in the real code with the var being redefined in this case.
So I feel like I am missing something very basic in how you can provide the name to declare a var. Maybe some very simple syntax thing I am missing, or perhaps there is a process out there which will dynamically make a var declaration name based on some other object. I've done my fair share of js and never seen var [complex syntax resulting in name] =, it's always quite simple.
But my searching has drawn a blank so far on this exact issue. Anyone able to enlighten me?

Wow, you really don't want to do what you think you want to do, but here goes.
If you are operating in a browser and you want the variables to be in global scope, fortunately the global scope has a name: window.
You can just do this:
window[obj.name] = 'stuff';
If you want it in in local scope, you can use the almost-forbidden keyword with:
var f = function(obj, b) {
var fake_scope = {};
with (fake_scope) {
fake_scope[obj.name] = b;
console.log(d);
}
}
f( { name : 'd' } , 3 )
will print out 3.
If you need local scope and you don't want to use with, you will have to screw around with eval.
But seriously, if you do this, remember.

Well, in the interest of science:
(function() {
var dynaVarName = 'testName';
eval('var ' + dynaVarName + ';');
eval(dynaVarName + ' = 5;');
eval('console.log(' + dynaVarName + ');'); // => 5
}());
testName // => ReferenceError
So yes, you actually can create a local variable with a name based on an expression. I can't imagine the benefits of doing this though.

Related

JavaScript: Finding a variable's value by its name, is eval acceptable here?

WARNING: As other have stated here, this problem is based upon an inflexible customer requirement. While the question is valid, you should definitely use the simpler solutions (ex: putting your settings in a single object) if possible!
I have an array of variables I want to be "watching out" for. That is: if they were previously defined in the code, I need to be able operate on their values.
var a = 'foo'; // only a gets defined
// ...more code here... IMPORTANT: I can only control what comes BELOW this line!
var target = 'exmple.com/?';
var gets = ['a', 'b', 'c']; // these are the names of the variables I want to check for
gets.forEach(function(element) { // checking each one
if(typeof element !== 'undefined') { // if it's previously defined
target += "&"+element+"="+eval(element); // add `&a=foo` to target
}
});
alert(target);
I'd like the alert to be printing example.com/?&a=foo
I've seen the tons of other answers not to use the eval function, and I have tried window[element] in place without success. How would it be best to do this without having to write an if/else for every variable I want to check?
Assuming you can't do otherwise and everything lives in the global scope, you could do without resorting to using eval(). (I'm pretty sure there must be some valid use cases for eval but I don't think this is one is one of them.)
Since you shouldn't trust the content of these variables you shouldn't be using eval. What if your customer options get compromised? (Or simply your customer doesn't know what they're doing.)
var css_color = 'tweet(document.cookie)';
You can simply leverage the fact that the variables will be accessible from the global scope:
const serialize_options = (...vars) =>
vars
.filter(v => typeof this[v] !== 'undefined')
.map(v => `${v}=${this[v]}`)
.join('&');
console.log(serialize_options('a'));
console.log(serialize_options('a', 'b'));
console.log(serialize_options('a', 'b', 'c'));
console.log(serialize_options('a', 'x', 'y'));
<script>
var a = '10';
var b = '20';
var c = '30';
</script>
The answer to this kind of question is almost always "You're thinking about the problem wrong.", as is the case here.
You make the assumption that this should be done with variables and that backs you into the corner of only being able to use eval() to get out of that corner. I understand that the customer may have asked for regular variables, but if I were a house builder and the customer asked me to build a house out of marshmallow, I'd say "no", that's not the way to do it.
The solution is to use an object with keys and values from the start, which allows you to use array indexing syntax later, instead of eval.
// Keep the items you need to check in an object
var obj = {
a:'foo' // only a gets defined
}
var target = 'exmple.com/?';
var gets = ['a', 'b', 'c'];
// Check each array element
gets.forEach(function(element) {
// Objects can use array indexing syntax where a string is passed as the index
// because objects actually have keys, rather than numeric indexes
if(obj[element]) { // if it's previously defined
target += "&" + element + "=" + obj[element]; // add `&a=foo` to target
}
});
alert(target);

Change the value of an evaluated var in JavaScript

I have a var containing a JSON string. (Here I won't parse it because it is not the main problem)
var variable = '{"name":"Johnny Appleseed"}';
var string = "variable";
I evaluate the string.
var evaluatedstring = eval(string);
Now I would like to delete the value of the var variable.
variable = undefined;
If I do:
console.log(evaluatedstring)
It works fine and returns me the JSON string as:
{"name":"Johnny Appleseed"}
But if I do:
evaluatedstring = undefined;
Of course it doesn't work.
eval(string) = undefined;
doesn't work either. (Returns the error Uncaught ReferenceError: Invalid left-hand side in assignment)
How can I delete the content that via the evaluated var?
No jQuery, please.
On Chrome 50+ so not a problem with the browser.
If you're working in the browser: Global variables in JavaScript are created as properties of the window object. Therefore, what you're trying to do can be done like this:
var variable = 'some variable';
var variableName = 'variable';
window[variableName] = undefined;
variable === undefined // Will now be true
However, just the fact that this is possible doesn't mean it's a good idea - in fact, it's just the opposite, it's bad style and will probably confuse anyone looking at your program to no end. If this is really necessary for your algorithm to work, you might want to rethink your data architecture. If this is just a scoping issue (you don't want that variable to pollute your global namespace), an IIFE will likely solve your problem.

Getting the nth names of vars in scope

I have a few variables set up like this:
var foo, bar, fizz, buzz, hello, world;
Now I want to find out how to get those names as a collection of some sort. I am trying to get them since I want to push them in an array as a string.
I hope it doesnt require eval or something hacky. Is what I am trying to do possible?
I just need to know how to get the names, I prefer to solve the rest myself.
Edit: About the answer from the linked duplicate, I dont really get what he is doing there as I get everything other than names. Ive read it before.
In my opinion it would be better if you have used javascript object here and put variables in it as properties:
var obj = {foo:'', bar:'', fizz:''};
This way you can access both name of the property and its value:
obj.foo
for(key in obj) {
alert(key + ': ' + obj[key]);
}
You can get list of variables by Object.keys() method, but it returns all variables that object has:
var ar = Object.keys(this);
console.log(ar);

Security considerations using "new Function(...)" (during rendertime, expression coming from my Javascript sources)

I'd like to use new Function(...) to generate a function from very reduced code. I'l like to do this to
avoid parsing the expression on my own and
being as flexible as possible.
I avoid eval() whenever possible. But I'm not sure if it's secure enough to use new Function(...) which is also known as being liable to security holes.
Background
I want to manage the states of menu buttons. So, while defining the buttons, I'd like to write something like
{
..., // More button definition
state: "isInEditmode && (isWidgetSelected || isCursorInWidget),
...
}
While handling the statechange during several events I'll check (summarize) the states of the current overall state object against those in the states attribute.
So I'll generate a Function during rendertime and attaching it as a DOM object attribute, not DOM attribute this way:
...
$el.stateFn = new Function("stateObj", "with (stateObj) {return " + item.state + ";}");
...
Testing state:
visible = $el.stateFn.call(currentStates, currentStates);
The with statement helps me providing the current state object's attributes as variables so that the above expression does not need something like obj.isInEditmode.
Security question
In my opinion this does not introduce security holes as the function attached to the DOM object is generated during render time and read from source. Or am I wrong? Should I avoid this?
Performance hints are appreciated (comment) (I think as long as I evaluating a new Function once during render time, this is acceptable).
Edit 1
I'm using Backbone.js. Using another Framework is out of question.
Some menu items need to be bound to different or even multiple models.
Delegation (or facade / proxy?) models are considerable.
Security-wise both are just as bad if user input is allowed to break out in the code. However, maintenance wise you don't have to worry about hidden bugs when local eval messes with your scope and causes dynamic scoping.
Performance-wise the function generated by new Function is exactly the same as any other function. The generation is slower but inlike eval it doesn't cause the containing scope to be unoptimizable.
In fact, new Function can be used to improve performance in situations like:
//Will behave like function a( obj ) { return obj.something }
function makePropReader( propName ) {
return new Function( "obj", "return obj." + propName );
}
The constructed function will perform better than the function returned here:
function makePropReader( propName ) {
return function( obj ) {
return obj[propName];
}
}
Due to having to dynamically read propName from closure context and do a dynamic read on the object everytime it is called.
Please never ever use eval no matter what. There is a much better alternative. Instead of eval, use the Function constructor. eval is evil, and there's no question about that, but most people skip over the most evil aspect of eval: it gives you access to variables in your local scope. Back in the 90s, back before the concept of JIST compilation, eval sounded like a good idea (and it was): just insert some additional lines dynamically into the code you're already executing line-by-line. This also meant that evals didn't really slow things down all. However, now-a-days with JIST compilation eval statements are very taxing on JIST compilers which internally remove the concept of variable names entirely. For JIST compilers, in order to evaluate an eval statement, it has to figure out where all of its variables are stored, and match them with unknown globals found in the evaled statement. The problem extends even deeper if you get really technical.
But, with Function, the JIST compiler doesn't have to do any expensive variable name lookups: the entire code block is self-contained and in the global scope. For example, take the following terribly inefficient eval snippet. Please note that this is only for the purpose of being an example. In production code, you shouldn't even be using eval or Function to generate a function from a string whose content is already known.
var a = {
prop: -1
};
var k = eval('(function(b){return a.prop + b;})');
alert( k(3) ); // will alert 2
Now, let's take a look at the much better Function alternative.
var a = {
prop: -1
};
var k = (Function('a', 'b', 'return a.prop + b')).bind(undefined, a);
alert( k(3) ); // will alert 2
Notice the difference? There is a major one: the eval is executed inside the local scope while the Function is executed inside the global one.
Now, onto the next problem: security. There is a lot of talk about how security is difficult, and yes, with eval it is pretty much impossible (e.x. if you wrap the whole code in a sandboxing function, then all you have to do is prematurely end the function and start a new one to execute code freely in the current scope). But, with Function, you can easily (but not the most efficiently) sandbox anything. Look at the following code.
var whitelist = ['Math', 'Number', 'Object', 'Boolean', 'Array'];
var blacklist = Object.getOwnPropertyNames(window).filter(function(x){
return whitelist.indexOf(x) === -1 && !/^[^a-zA-Z]|\W/.test(x)
});
var listlen = blacklist.length;
var blanklist = (new Array(listlen+1)).fill(undefined);
function sandboxed_function(){
"use-strict";
blacklist.push.apply(blacklist, arguments);
blacklist[blacklist.length-1] =
'"use-strict";' + arguments[arguments.length-1];
var newFunc = Function.apply(
Function,
blacklist
);
blacklist.length = listlen;
return newFunc.bind.apply(newFunc, blanklist);
}
Then, fiddle around with the whitelist, get it just the way you want it, and then you can use sandboxed_function just like Function. For example:
var whitelist = ['Math', 'Number', 'Object', 'Boolean', 'Array'];
var blacklist = Object.getOwnPropertyNames(window).filter(function(x){
return whitelist.indexOf(x) === -1 && !/^[^a-zA-Z]|\W/.test(x)
});
var listlen = blacklist.length;
var blanklist = (new Array(listlen+1)).fill(undefined);
function sandboxed_function(){
"use-strict";
blacklist.push.apply(blacklist, arguments);
blacklist[blacklist.length-1] =
'"use-strict";' + arguments[arguments.length-1];
var newFunc = Function.apply(
Function,
blacklist
);
blacklist.length = listlen;
return newFunc.bind.apply(newFunc, blanklist);
}
var myfunc = sandboxed_function('return "window = " + window + "\\ndocument = " + document + "\\nBoolean = " + Boolean');
output.textContent = myfunc();
<pre id="output"></pre>
As for writing code to be runned under this strict sandbox, you may be asking, if window is undefined, how do I test for the existence of methods. There are two solutions to this. #1 is just simply to use typeof like so.
output.textContent = 'typeof foobar = ' + typeof foobar;
<div id="output"></div>
As you can see in the above code, using typeof will not throw an error, rather it will only just return undefined. The 2nd primary method to check for a global is to use the try/catch method.
try {
if (foobar)
output.textContent = 'foobar.constructor = ' + foobar.constructor;
else
output.textContent = 'foobar.constructor = undefined';
} catch(e) {
output.textContent = 'foobar = undefined';
}
<div id="output"></div>
So, in conclusion, I hope my code snippets gave you some insight into a much better, nicer, cleaner alternative to eval. And I hope I have aspired you to a greater purpose: snubbing on eval. As for the browser compatibility, while the sandboxed_function will run in IE9, in order for it to actually sandbox anything, IE10+ is required. This is because the "use-strict" statement is very essential to eliminating much of the sneaky sand-box breaking ways like the one below.
var whitelist = ['Math', 'Number', 'Object', 'Boolean', 'Array'];
var blacklist = Object.getOwnPropertyNames(window).filter(function(x){
return whitelist.indexOf(x) === -1 && !/^[^a-zA-Z]|\W/.test(x)
});
var listlen = blacklist.length;
var blanklist = (new Array(listlen+1)).fill(undefined);
function sandboxed_function(){
blacklist.push.apply(blacklist, arguments);
blacklist[blacklist.length-1] =
/*'"use-strict";' +*/ arguments[arguments.length-1];
var newFunc = Function.apply(
Function,
blacklist
);
blacklist.length = listlen;
return newFunc.bind.apply(newFunc, blanklist);
}
var myfunc = sandboxed_function(`return (function(){
var snatched_window = this; // won't work in strict mode where the this
// variable doesn't need to be an object
return snatched_window;
}).call(undefined)`);
output.textContent = "Successful broke out: " + (myfunc() === window);
<pre id="output"></pre>
One last final comment is that if you are going to allow event API's into your sandboxed environment, then you must be careful: the view property can be a window object, making it so you have to erase that too. There are several other things, but I would recommend researching thoroughly and exploring the objects in Chrome's console.
Lastly, note that Function is a very unique constructor which returns a function instead of an object instance, so there's no need to use new.
Old thread with answers considered dangerous these days.
new Function() still allows access to global variables. So an adversary, when given the chance to effect the function string - which is usually the very reason for considering new Function and hard to guarantee it can't be done maliciously -, can read and modify any global. Good luck from that point on :-)
Which is why new Function falls under the same category as eval from the viewpoint of CSP (Content Security Policy) as mentioned here.
Example:
a = 10
> 10
b = new Function('a = 20; return 42')
> function(...)
a
> 10
b()
> 42
a
> 20
As you have said that you will only be doing this on code you wrote yourself - I'd say that it's fine. new Function() is definitely better than using eval() in any case. You won't be messing with any local variables and you're enforcing your own context by using fn.call.
It seems to me that the problem you are trying to solve would be fairly straight forward if you were using an MVC or MVVM framework that supports 2 way data binding. I.e. changing the UI updates a backing model and updating the model will automatically refresh the UI for you.
For example, knockout.js. In this case the visible databinding would be appropriate.

Are there legitimate uses for JavaScript's "with" statement?

Alan Storm's comments in response to my answer regarding the with statement got me thinking. I've seldom found a reason to use this particular language feature, and had never given much thought to how it might cause trouble. Now, I'm curious as to how I might make effective use of with, while avoiding its pitfalls.
Where have you found the with statement useful?
Another use occurred to me today, so I searched the web excitedly and found an existing mention of it: Defining Variables inside Block Scope.
Background
JavaScript, in spite of its superficial resemblance to C and C++, does not scope variables to the block they are defined in:
var name = "Joe";
if ( true )
{
var name = "Jack";
}
// name now contains "Jack"
Declaring a closure in a loop is a common task where this can lead to errors:
for (var i=0; i<3; ++i)
{
var num = i;
setTimeout(function() { alert(num); }, 10);
}
Because the for loop does not introduce a new scope, the same num - with a value of 2 - will be shared by all three functions.
A new scope: let and with
With the introduction of the let statement in ES6, it becomes easy to introduce a new scope when necessary to avoid these problems:
// variables introduced in this statement
// are scoped to each iteration of the loop
for (let i=0; i<3; ++i)
{
setTimeout(function() { alert(i); }, 10);
}
Or even:
for (var i=0; i<3; ++i)
{
// variables introduced in this statement
// are scoped to the block containing it.
let num = i;
setTimeout(function() { alert(num); }, 10);
}
Until ES6 is universally available, this use remains limited to the newest browsers and developers willing to use transpilers. However, we can easily simulate this behavior using with:
for (var i=0; i<3; ++i)
{
// object members introduced in this statement
// are scoped to the block following it.
with ({num: i})
{
setTimeout(function() { alert(num); }, 10);
}
}
The loop now works as intended, creating three separate variables with values from 0 to 2. Note that variables declared within the block are not scoped to it, unlike the behavior of blocks in C++ (in C, variables must be declared at the start of a block, so in a way it is similar). This behavior is actually quite similar to a let block syntax introduced in earlier versions of Mozilla browsers, but not widely adopted elsewhere.
I have been using the with statement as a simple form of scoped import. Let's say you have a markup builder of some sort. Rather than writing:
markupbuilder.div(
markupbuilder.p('Hi! I am a paragraph!',
markupbuilder.span('I am a span inside a paragraph')
)
)
You could instead write:
with(markupbuilder){
div(
p('Hi! I am a paragraph!',
span('I am a span inside a paragraph')
)
)
}
For this use case, I am not doing any assignment, so I don't have the ambiguity problem associated with that.
As my previous comments indicated, I don't think you can use with safely no matter how tempting it might be in any given situation. Since the issue isn't directly covered here, I'll repeat it. Consider the following code
user = {};
someFunctionThatDoesStuffToUser(user);
someOtherFunction(user);
with(user){
name = 'Bob';
age = 20;
}
Without carefully investigating those function calls, there's no way to tell what the state of your program will be after this code runs. If user.name was already set, it will now be Bob. If it wasn't set, the global name will be initialized or changed to Bob and the user object will remain without a name property.
Bugs happen. If you use with you will eventually do this and increase the chances your program will fail. Worse, you may encounter working code that sets a global in the with block, either deliberately or through the author not knowing about this quirk of the construct. It's a lot like encountering fall through on a switch, you have no idea if the author intended this and there's no way to know if "fixing" the code will introduce a regression.
Modern programming languages are chocked full of features. Some features, after years of use, are discovered to be bad, and should be avoided. Javascript's with is one of them.
I actually found the with statement to be incredibly useful recently. This technique never really occurred to me until I started my current project - a command line console written in JavaScript. I was trying to emulate the Firebug/WebKit console APIs where special commands can be entered into the console but they don't override any variables in the global scope. I thought of this when trying to overcome a problem I mentioned in the comments to Shog9's excellent answer.
To achieve this effect, I used two with statements to "layer" a scope behind the global scope:
with (consoleCommands) {
with (window) {
eval(expression);
}
}
The great thing about this technique is that, aside from the performance disadvantages, it doesn't suffer the usual fears of the with statement, because we're evaluating in the global scope anyway - there's no danger of variables outside our pseudo-scope from being modified.
I was inspired to post this answer when, to my surprise, I managed to find the same technique used elsewhere - the Chromium source code!
InjectedScript._evaluateOn = function(evalFunction, object, expression) {
InjectedScript._ensureCommandLineAPIInstalled();
// Surround the expression in with statements to inject our command line API so that
// the window object properties still take more precedent than our API functions.
expression = "with (window._inspectorCommandLineAPI) { with (window) { " + expression + " } }";
return evalFunction.call(object, expression);
}
EDIT: Just checked the Firebug source, they chain 4 with statements together for even more layers. Crazy!
const evalScript = "with (__win__.__scope__.vars) { with (__win__.__scope__.api) { with (__win__.__scope__.userVars) { with (__win__) {" +
"try {" +
"__win__.__scope__.callback(eval(__win__.__scope__.expr));" +
"} catch (exc) {" +
"__win__.__scope__.callback(exc, true);" +
"}" +
"}}}}";
Yes, yes and yes. There is a very legitimate use. Watch:
with (document.getElementById("blah").style) {
background = "black";
color = "blue";
border = "1px solid green";
}
Basically any other DOM or CSS hooks are fantastic uses of with. It's not like "CloneNode" will be undefined and go back to the global scope unless you went out of your way and decided to make it possible.
Crockford's speed complaint is that a new context is created by with. Contexts are generally expensive. I agree. But if you just created a div and don't have some framework on hand for setting your css and need to set up 15 or so CSS properties by hand, then creating a context will probably be cheaper then variable creation and 15 dereferences:
var element = document.createElement("div"),
elementStyle = element.style;
elementStyle.fontWeight = "bold";
elementStyle.fontSize = "1.5em";
elementStyle.color = "#55d";
elementStyle.marginLeft = "2px";
etc...
You can define a small helper function to provide the benefits of with without the ambiguity:
var with_ = function (obj, func) { func (obj); };
with_ (object_name_here, function (_)
{
_.a = "foo";
_.b = "bar";
});
Hardly seems worth it since you can do the following:
var o = incrediblyLongObjectNameThatNoOneWouldUse;
o.name = "Bob";
o.age = "50";
I don't ever use with, don't see a reason to, and don't recommend it.
The problem with with is that it prevents numerous lexical optimizations an ECMAScript implementation can perform. Given the rise of fast JIT-based engines, this issue will probably become even more important in the near future.
It might look like with allows for cleaner constructs (when, say, introducing a new scope instead of a common anonymous function wrapper or replacing verbose aliasing), but it's really not worth it. Besides a decreased performance, there's always a danger of assigning to a property of a wrong object (when property is not found on an object in injected scope) and perhaps erroneously introducing global variables. IIRC, latter issue is the one that motivated Crockford to recommend to avoid with.
Visual Basic.NET has a similar With statement. One of the more common ways I use it is to quickly set a number of properties. Instead of:
someObject.Foo = ''
someObject.Bar = ''
someObject.Baz = ''
, I can write:
With someObject
.Foo = ''
.Bar = ''
.Baz = ''
End With
This isn't just a matter of laziness. It also makes for much more readable code. And unlike JavaScript, it does not suffer from ambiguity, as you have to prefix everything affected by the statement with a . (dot). So, the following two are clearly distinct:
With someObject
.Foo = ''
End With
vs.
With someObject
Foo = ''
End With
The former is someObject.Foo; the latter is Foo in the scope outside someObject.
I find that JavaScript's lack of distinction makes it far less useful than Visual Basic's variant, as the risk of ambiguity is too high. Other than that, with is still a powerful idea that can make for better readability.
I think the obvious use is as a shortcut. If you're e.g. initializing an object you simply save typing a lot of "ObjectName." Kind of like lisp's "with-slots" which lets you write
(with-slots (foo bar) objectname
"some code that accesses foo and bar"
which is the same as writing
"some code that accesses (slot-value objectname 'foo) and (slot-value objectname 'bar)""
It's more obvious why this is a shortcut then when your language allows "Objectname.foo" but still.
You can use with to introduce the contents of an object as local variables to a block, like it's being done with this small template engine.
Using "with" can make your code more dry.
Consider the following code:
var photo = document.getElementById('photo');
photo.style.position = 'absolute';
photo.style.left = '10px';
photo.style.top = '10px';
You can dry it to the following:
with(document.getElementById('photo').style) {
position = 'absolute';
left = '10px';
top = '10px';
}
I guess it depends whether you have a preference for legibility or expressiveness.
The first example is more legible and probably recommended for most code. But most code is pretty tame anyway. The second one is a bit more obscure but uses the expressive nature of the language to cut down on code size and superfluous variables.
I imagine people who like Java or C# would choose the first way (object.member) and those who prefer Ruby or Python would choose the latter.
Having experience with Delphi, I would say that using with should be a last-resort size optimization, possibly performed by some kind of javascript minimizer algorithm with access to static code analysis to verify its safety.
The scoping problems you can get into with liberal use of the with statement can be a royal pain in the a** and I wouldn't want anyone to experience a debugging session to figure out what the he.. is going on in your code, only to find out that it captured an object member or the wrong local variable, instead of your global or outer scope variable which you intended.
The VB with statement is better, in that it needs the dots to disambiguate the scoping, but the Delphi with statement is a loaded gun with a hairtrigger, and it looks to me as though the javascript one is similar enough to warrant the same warning.
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.
Source: Mozilla.org
The with statement can be used to decrease the code size or for private class members, example:
// demo class framework
var Class= function(name, o) {
var c=function(){};
if( o.hasOwnProperty("constructor") ) {
c= o.constructor;
}
delete o["constructor"];
delete o["prototype"];
c.prototype= {};
for( var k in o ) c.prototype[k]= o[k];
c.scope= Class.scope;
c.scope.Class= c;
c.Name= name;
return c;
}
Class.newScope= function() {
Class.scope= {};
Class.scope.Scope= Class.scope;
return Class.scope;
}
// create a new class
with( Class.newScope() ) {
window.Foo= Class("Foo",{
test: function() {
alert( Class.Name );
}
});
}
(new Foo()).test();
The with-statement is very usefull if you want to modify the scope, what is necessary for having your own global scope that you can manipulate at runtime. You can put constants on it or certain helper functions often used like e.g. "toUpper", "toLower" or "isNumber", "clipNumber" aso..
About the bad performance I read that often: Scoping a function won't have any impact on the performance, in fact in my FF a scoped function runs faster then an unscoped:
var o={x: 5},r, fnRAW= function(a,b){ return a*b; }, fnScoped, s, e, i;
with( o ) {
fnScoped= function(a,b){ return a*b; };
}
s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
r+= fnRAW(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );
s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
r+= fnScoped(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );
So in the above mentioned way used the with-statement has no negative effect on performance, but a good one as it deceases the code size, what impacts the memory usage on mobile devices.
Using with also makes your code slower in many implementation, as everything now gets wrapped in an extra scope for lookup. There's no legitimate reason for using with in JavaScript.
I think the with-statement can come in handy when converting a template language into JavaScript. For example JST in base2, but I've seen it more often.
I agree one can program this without the with-statement. But because it doesn't give any problems it is a legitimate use.
It's good for putting code that runs in a relatively complicated environment into a container: I use it to make a local binding for "window" and such to run code meant for a web browser.
I think the object literal use is interesting, like a drop-in replacement for using a closure
for(var i = nodes.length; i--;)
{
// info is namespaced in a closure the click handler can access!
(function(info)
{
nodes[i].onclick = function(){ showStuff(info) };
})(data[i]);
}
or the with statement equivilent of a closure
for(var i = nodes.length; i--;)
{
// info is namespaced in a closure the click handler can access!
with({info: data[i]})
{
nodes[i].onclick = function(){ showStuff(info) };
}
}
I think the real risk is accidently minipulating variables that are not part of the with statement, which is why I like the object literal being passed into with, you can see exactly what it will be in the added context in the code.
I created a "merge" function which eliminates some of this ambiguity with the with statement:
if (typeof Object.merge !== 'function') {
Object.merge = function (o1, o2) { // Function to merge all of the properties from one object into another
for(var i in o2) { o1[i] = o2[i]; }
return o1;
};
}
I can use it similarly to with, but I can know it won't affect any scope which I don't intend for it to affect.
Usage:
var eDiv = document.createElement("div");
var eHeader = Object.merge(eDiv.cloneNode(false), {className: "header", onclick: function(){ alert("Click!"); }});
function NewObj() {
Object.merge(this, {size: 4096, initDate: new Date()});
}
For some short code pieces, I would like to use the trigonometric functions like sin, cos etc. in degree mode instead of in radiant mode. For this purpose, I use an AngularDegreeobject:
AngularDegree = new function() {
this.CONV = Math.PI / 180;
this.sin = function(x) { return Math.sin( x * this.CONV ) };
this.cos = function(x) { return Math.cos( x * this.CONV ) };
this.tan = function(x) { return Math.tan( x * this.CONV ) };
this.asin = function(x) { return Math.asin( x ) / this.CONV };
this.acos = function(x) { return Math.acos( x ) / this.CONV };
this.atan = function(x) { return Math.atan( x ) / this.CONV };
this.atan2 = function(x,y) { return Math.atan2(x,y) / this.CONV };
};
Then I can use the trigonometric functions in degree mode without further language noise in a with block:
function getAzimut(pol,pos) {
...
var d = pos.lon - pol.lon;
with(AngularDegree) {
var z = atan2( sin(d), cos(pol.lat)*tan(pos.lat) - sin(pol.lat)*cos(d) );
return z;
}
}
This means: I use an object as a collection of functions, which I enable in a limited code region for direct access. I find this useful.
I think that the usefulness of with can be dependent on how well your code is written. For example, if you're writing code that appears like this:
var sHeader = object.data.header.toString();
var sContent = object.data.content.toString();
var sFooter = object.data.footer.toString();
then you could argue that with will improve the readability of the code by doing this:
var sHeader = null, sContent = null, sFooter = null;
with(object.data) {
sHeader = header.toString();
sContent = content.toString();
sFooter = content.toString();
}
Conversely, it could be argued that you're violating the Law of Demeter, but, then again, maybe not. I digress =).
Above all else, know that Douglas Crockford recommends not using with. I urge you to check out his blog post regarding with and its alternatives here.
I just really don't see how using the with is any more readable than just typing object.member. I don't think it's any less readable, but I don't think it's any more readable either.
Like lassevk said, I can definitely see how using with would be more error prone than just using the very explicit "object.member" syntax.
You got to see the validation of a form in javascript at W3schools http://www.w3schools.com/js/js_form_validation.asp where the object form is "scanned" through to find an input with name 'email'
But i've modified it to get from ANY form all the fields validate as not empty, regardless of the name or quantity of field in a form. Well i've tested only text-fields.
But the with() made things simpler. Here's the code:
function validate_required(field)
{
with (field)
{
if (value==null||value=="")
{
alert('All fields are mandtory');return false;
}
else
{
return true;
}
}
}
function validate_form(thisform)
{
with (thisform)
{
for(fiie in elements){
if (validate_required(elements[fiie])==false){
elements[fiie].focus();
elements[fiie].style.border='1px solid red';
return false;
} else {elements[fiie].style.border='1px solid #7F9DB9';}
}
}
return false;
}
CoffeeScript's Coco fork has a with keyword, but it simply sets this (also writable as # in CoffeeScript/Coco) to the target object within the block. This removes ambiguity and achieves ES5 strict mode compliance:
with long.object.reference
#a = 'foo'
bar = #b
My
switch(e.type) {
case gapi.drive.realtime.ErrorType.TOKEN_REFRESH_REQUIRED: blah
case gapi.drive.realtime.ErrorType.CLIENT_ERROR: blah
case gapi.drive.realtime.ErrorType.NOT_FOUND: blah
}
boils down to
with(gapi.drive.realtime.ErrorType) {switch(e.type) {
case TOKEN_REFRESH_REQUIRED: blah
case CLIENT_ERROR: blah
case NOT_FOUND: blah
}}
Can you trust so low-quality code? No, we see that it was made absolutely unreadable. This example undeniably proves that there is no need for with-statement, if I am taking readability right ;)
using "with" statement with proxy objects
I recently want to write a plugin for babel that enables macros. I would like to have a separate variable namespace that keeps my macro variables, and I can run my macro codes in that space. Also, I want to detect new variables that are defined in the macro codes(Because they are new macros).
First, I choose the vm module, but I found global variables in the vm module like Array, Object, etc. are different from the main program, and I cant implement module and require that be fully compatible with that global objects(Because I cant reconstruct the core modules). In the end, I find the "with" statement.
const runInContext = function(code, context) {
context.global = context;
const proxyOfContext = new Proxy(context, { has: () => true });
let run = new Function(
"proxyOfContext",
`
with(proxyOfContext){
with(global){
${code}
}
}
`
);
return run(proxyOfContext);
};
This proxy object traps search of all variables and says: "yes, I have that variable." and If the proxy object doesn't really have that variable, show its value as undefined.
In this way, if any variable is defined in the macro code with the var statement, I can find it in the context object(like the vm module). But variables that are defined with let or const only available in that time and will not be saved in the context object(the vm module saves them but doesn't expose them).
Performance: Performance of this method is better than vm.runInContext.
safety: If you want to run code in a sandbox, this is not safe in any way, and you must use the vm module. It only provides a new namespace.
Here's a good use for with: adding new elements to an Object Literal, based on values stored in that Object. Here's an example that I just used today:
I had a set of possible tiles (with openings facing top, bottom, left, or right) that could be used, and I wanted a quick way of adding a list of tiles which would be always placed and locked at the start of the game. I didn't want to keep typing types.tbr for each type in the list, so I just used with.
Tile.types = (function(t,l,b,r) {
function j(a) { return a.join(' '); }
// all possible types
var types = {
br: j( [b,r]),
lbr: j([l,b,r]),
lb: j([l,b] ),
tbr: j([t,b,r]),
tbl: j([t,b,l]),
tlr: j([t,l,r]),
tr: j([t,r] ),
tl: j([t,l] ),
locked: []
};
// store starting (base/locked) tiles in types.locked
with( types ) { locked = [
br, lbr, lbr, lb,
tbr, tbr, lbr, tbl,
tbr, tlr, tbl, tbl,
tr, tlr, tlr, tl
] }
return types;
})("top","left","bottom","right");
As Andy E pointed out in the comments of Shog9's answer, this potentially-unexpected behavior occurs when using with with an object literal:
for (var i = 0; i < 3; i++) {
function toString() {
return 'a';
}
with ({num: i}) {
setTimeout(function() { console.log(num); }, 10);
console.log(toString()); // prints "[object Object]"
}
}
Not that unexpected behavior wasn't already a hallmark of with.
If you really still want to use this technique, at least use an object with a null prototype.
function scope(o) {
var ret = Object.create(null);
if (typeof o !== 'object') return ret;
Object.keys(o).forEach(function (key) {
ret[key] = o[key];
});
return ret;
}
for (var i = 0; i < 3; i++) {
function toString() {
return 'a';
}
with (scope({num: i})) {
setTimeout(function() { console.log(num); }, 10);
console.log(toString()); // prints "a"
}
}
But this will only work in ES5+. Also don't use with.
I am working on a project that will allow users to upload code in order to modify the behavior of parts of the application. In this scenario, I have been using a with clause to keep their code from modifying anything outside of the scope that I want them to mess around with. The (simplified) portion of code I use to do this is:
// this code is only executed once
var localScope = {
build: undefined,
// this is where all of the values I want to hide go; the list is rather long
window: undefined,
console: undefined,
...
};
with(localScope) {
build = function(userCode) {
eval('var builtFunction = function(options) {' + userCode + '}');
return builtFunction;
}
}
var build = localScope.build;
delete localScope.build;
// this is how I use the build method
var userCode = 'return "Hello, World!";';
var userFunction = build(userCode);
This code ensures (somewhat) that the user-defined code neither has access to any globally-scoped objects such as window nor to any of my local variables through a closure.
Just as a word to the wise, I still have to perform static code checks on the user-submitted code to ensure they aren't using other sneaky manners to access global scope. For instance, the following user-defined code grabs direct access to window:
test = function() {
return this.window
};
return test();

Categories

Resources