This question already has answers here:
Are there legitimate uses for JavaScript's "with" statement?
(33 answers)
Closed 9 years ago.
Pretty much every resource documenting with that I can find warns against using it, mostly because if a variable is not defined it may have unpredictable effects.
I want to understand it so that I can make effective use of it - after all, it's there for a reason. Even eval has its non-evil uses!
So, with that in mind, let's say I want to delete all child nodes from an element, without using elem.innerHTML = "";
Would the following be safe?
with(elem) while(firstChild) removeChild(firstChild);
Note that at this point I'm not caring for readability, just functionality. Since firstChild is a property of, and removeChild a method of, all element nodes, it should be fine to use with this way, right?
Similarly, let's say I want to set some styles.
with(elem.style) {
color = "red";
backgroundColor = "black";
fontWeight = "bold";
}
Since all of these are properties of the style object (even if not defined in the stylesheet, they are there as empty strings), it's fine to use with like this, right?
Am I missing something, or is the constant warning to not use with similar to the one against PHP's mysql extension: a protection against dumb programmers?
The with keyword in Javascript is a bit of a throwback to when it was competing with VBScript in the late 90s. It's still there, but disabled if you 'use strict'; and considered an error by just about every Javascript validator out there.
There are two major problems with it, both related to the way scope works in Javascript:
var color = "green";
var foo = "bar";
with(elem.style) {
color = "red";
foo = "something else";
}
What's color now? What is foo? Not only is this confusing code, but due to how Javascript looks up variables in the with scope it's also very slow code as now every statement has an additional scope to search for each variable used.
This is one of the reasons why modern frameworks like jQuery and Prototype use chaining. They use call and apply on functions to control what this represents, meaning that you make calls to this.something rather than with(this){something}.
So to answer your question: there is one good use of with in Javascript - allowing IE4 and IE5 websites written in the 90s to still work today.
That's it. You shouldn't use it in new code.
The only safe use of with is not using it at all. There's no task that can't be accomplished without it and modern standards outright disable it in strict mode.
For all purposes it is considered a design mistake that is only preserved for backwards compatibility.
with simply puts the variable on top of the stack of "maps" searched for variables.
Normal stack is (searched top to bottom)
window
(root - just having window)
So if you have
var foo = { document: "doc.pdf" };
window.myFunc = function(){
with( foo ){
alert( document );
}
}
The stack within with is
foo
window
(root - just having window)
It will surely print foo.document and not window.document.
In this case it's obvious that you shouldn't use document like that, as it's typically used in browsers. But ECMAScript specification doesn't define that, so in other environments, other variables may be on the stack by default (even more of them).
The danger is that whatever you call within the with statement has that on the stack too.
This would fail on the document.url call:
// Some 3rd-party library
function redirect( url ){
document.url = url; // url is undefined in document
}
var bar = { document: "20x20" };
with( bar ){
redirect(); //
}
The with statement is just a shorthand for writing recurring accesses to objects:
For
foobar.foo.bar.baz = 'bubu';
foobar.foo.bar.buz = 'baba';
you can write
with(foobar.foo.bar){
baz = 'bubu';
buz = 'baba';
}
Thats nice! BUT will foobar.foo.bar be modified or will the global variables baz and buz modified?
In some cases it is impossible to know for sure.
JavaScript provides a better alternative
var better = foobar.foo.bar;
better.baz = 'bubu';
better.buz = 'baba';
Now there is no ambiguity.
Related
In Javascript, local variables do not live on any object that I'm aware of. That is,
function foo() {
const x = 2;
self.x; // undefined
this.x; // undefined
window.x; // undefined
x; // 2, obviously
eval('x'); // 2
}
The last option eval('x') shows that it is possible to refer to these variables by name. I'm looking to extend this and access a variable using a name pattern:
function foo() {
// Code not under my direct control
function foobar_abc() {}
function other_functions() {}
// Code under my control
const matchingFunction = // find the function with name matching 'foobar_*'
}
If this lived on an object, I would use something like myObject[Object.keys(myObject).find((key) => key.startsWith('foobar_'))]. If it were in the global scope, myObject would be window and everything works.
The fact that eval is able to access the variable by name implies that the value is available somewhere. Is there any way to find this variable? Or must I resort to techniques which re-write the (potentially very complex) code which is not under my direct control?
I'm targeting modern browsers, and in this use case I don't mind using eval or similarly hacky solutions. Arbitrary code is already being executed, because the execution of user-provided code is the purpose.
Another option is to use code parsing to deduce the function names using a javascript AST (abstract syntax tree) library. The "esprima" package will probably be good place to look:
https://www.npmjs.com/package/esprima
So you can do
import esprima from 'esprima'
const userCodeStructure = esprima.parseScript( userProvidedJavascriptString );
const allTopLevelFunctionDeclarations = userCodeStructure.body.filter( declaration => declaration.type === "FunctionDeclaration" );
const allTopLevelFunctionNames = allTopLevelFunctionDeclarations.map( d => d.id );
I haven't tried this myself by the documentation suggests it should work (or something like it):
http://esprima.readthedocs.io/en/latest/
One possible approach that might help you here is to evaluate at global scope rather than in a function, and that might put the functions on the window object instead of in local scope.
Easiest way to do this is probably to write a new tag into the document and inject the user-provided code.
Relying on variable names is the wrong approach.
eval is evil. It may not be available under CSP. Considering that the code is supposed to run in browser, the biggest problem is that variables don't have expected names in minified code. They are a, b, c...
In order to maintain their names, they should be object properties - and so they will be available on the object.
Or must I resort to techniques which re-write the (potentially very complex) code
This is refactoring and that's what should be done to avoid bad code that smells and creates major problems.
Limiting side effects when programming in the browser with javascript is quite tricky.
I can do things like not accessing member variables like in this silly example:
let number = 0;
const inc = (n) => {
number = number + n;
return number;
};
console.log(inc(1)); //=> 1
console.log(inc(1)); //=> 2
But what other things can I do to reduce side effects in my javascript?
Of course you can avoid side effects by careful programming. I assume your question is about how to prevent them. Your ability to do so is severely limited by the nature of the language. Here are some approaches:
Use web workers. See MDN. Web workers run in another global context that is different from the current window.:
Isolate certain kinds of logic inside iframes. Use cross-window messaging to communicate with the iframe.
Immutability libraries. See https://github.com/facebook/immutable-js. Also http://bahmutov.calepin.co/avoid-side-effects-with-immutable-data-structures.html.
Lock down your objects with Object.freeze, Object.seal, or Object.preventExtensions. In the same vein, create read-only properties on objects using Object.defineProperty with getters but no setters, or with the writeable property set to false.
Use Object.observe to get asynchronous reports on various types of changes to objects and their properties, upon which you could throw an error or take other action.
If available, use Proxy for complete control over access to an object.
For considerations on preventing access to window, see javascript sandbox a module to prevent reference to Window. Also http://dean.edwards.name/weblog/2006/11/sandbox/. Finally, Semi-sandboxing Javascript eval.
It is useful to distinguish between inward side effects and outward side effects. Inward side effects are where some other code intrudes on the state of my component. Outward side effects are where my code intrudes on the state of some other component. Inward side effects can be prevented via the IIFEs mentioned in other answers, or by ES6 modules. But the more serious concern is outward side effects, which are going to require one of the approaches mentioned above.
Just what jumps to my mind thinking about your question:
Don't pollute the global namespace. Use 'var' or 'let', those keywords limit your variables to the local scope.
"By reducing your global footprint to a single name, you significantly reduce the chance of bad interactions with other applications, widgets, or libraries." - Douglas Crockford
Use semicolons
The comment section of this article provides some good (real life) reasons to always use semicolons.
Don't declare String, Number or Boolean Objects(in case you were ever tempted to)
var m = new Number(2);
var n = 2;
m === n; // will be false because n is a Number and m is an object
"use strict"; is your friend. Enabling strict mode is a good idea, but please don't add it to existing code since it might break something and you can not really declare strict only on lexical scopes or individual scripts as stated here
Declare variables first. One common side effect is that people are not aware about JavaScript's Hoisting. Hoisting searches your block for variable declaration and moves them all together to the top of your block.
function(){
var x = 3;
// visible variables at runtime at this point: x,y,i !
// some code....
var y = 1; // y will be moved to top!
for( var i = 0; i < 10; i++ ){ // i will be moved to top!
// some code...
}
}
Here is discussed what hoisting actually means and to what kind of 'unexpected behaviour' it may lead.
use '===' instead of '=='. There are many good reasons for this and this is one of the most common 'side effects' or 'errors' in JavaScript.
For more details see this great answer on SO, but let me give you a quick demonstration:
'' == '0' // false
0 == '' // true
// can be avoided by using '==='
'' === '0' // false
0 === '' // false
Make use of IIFE. An IIFE (Immediately Invoked Function Expression) lets you declare an anonymus function which will call itself. This way you can create a new lexical scope and don't have to worry about the global namespace.
Be careful with prototypes. Keep in mind that JavaScript objects of the same class share the same prototype. Changing a prototype means changing the behaviour of all instances of the class. (Even those which are used by other scripts/frameworks) like it happened here
Object.prototype.foo = function(){...} // careful!
Those are the 'side effects' that came to my mind. Of course there is way more to take care of (meaningful variable names, consistent code style etc...) but I don't consider those things as 'side effects' since they make your code hard to maintain, but won't break it immediately.
My favorite trick is to just use a language that compiles to javascript, instead of using javascript.
However, two important tricks you can do :
start your file with "use strict";. This will turn on validation of your code and prevent usage of undeclared variables. Yes, that's a special string that the browser will know how to deal with.
use functions when needed. Javascript cannot do normal scopes, but for functions it works fine, so get (function() { })(); in your muscle memory.
Normal CS fundamentals also apply: separate your code into logical pieces, name your variables properly, always explicitly initialize variables when you need them, etc.
This question already has answers here:
jQuery - Is it okay to use $('#ElementId') everytime?
(7 answers)
Closed 9 years ago.
While working, I am often using same nodes as a selectors for jQuery objects, like $('.often') As I suppose, jQuery every time composes a new object for it, but I can also assign already made object to variable: often=$('.often') Apart of possibly better readability would the use of same object as variable increase the script's performance?
Calling a function with the same parameters twice is a code smell... in any language.
A function's implementation is usually a detail with which the function's caller isn't concerned, but the caller can usually safely assume that calling the function twice is more expensive than assigning its return value to a local variable.
Unless the function behaves differently depending on state (for example, in a multi-threaded application), just call it once.
This may be more a matter of style than technique, but if I was to see something like this:
if (get_my_value("param") == 1) {
return "Got 1.";
}
if (get_my_value("param") == 2) {
return "Got 2.";
}
I would rewrite it to:
var my_value = get_my_value("param");
if (my_value == 1) {
return "Got 1.";
}
if (my_value == 2) {
return "Got 2.";
}
The reasons I would rewrite it are:
If get_my_value is expensive, I've improved performance.
By reducing code duplication, I've improved maintainability.
By reducing code duplication, I've improved readability.
I would say typically, yes, it is worth caching the object in a variable. However, performance is not necessarily my reason for doing so.
As a convention, you will find many javascript/jQuery devs prefix variables which refer to jquery objects with a $, eg
var $myObject = $('#id');
The reason to do so is that you now know later on that there is no need to wrap that variable again, ie it avoids this mistake:
var myObject = $('#id');
... much later on out of sight of that declaration
var myJQueryObject = $(myObject); // No need for this, but its a common mistake.
Before I begin, I want to confess that I am a JavaScript novice and I have very little understanding/knowledge of JavaScript patterns and terminologies so please feel free to explain basic concepts to me like I'm 5!
I have previously used the JavaScript prototype pattern to great effect in my work.
Here is a sample of my previous work with the prototype pattern
var SomeNameSpace = SomeNameSpace || {};
SomeNameSpace.SomeClass = function(oSomeParameter){
this.SomeProperty = oSomeParameter
...
}
SomeNameSpace.SomeClass.prototype = {
SomeClassMethod: function (oSomeOtherParameter) {//code here}
}
var someClassInstance = new SomeNameSpace.SomeClass("some string");
var result = someClassInstance.SomeClassMethod("some other string");
That snippet is an example of how I have always worked with javascript
I have been put in charge of supporting some new javascript code. I would like to introduce the same sort of prototype pattern to this new library. However, the namespace is written in a way which is foreign to me and I do not know how to modify it to suit my needs.
An example
if (typeof SomeNamespace == "undefined") {
SomeNamespace = { __namespace: true };
}
SomeNamespace.SomeOtherNamespace = {
SomeClass: function(oSomeParameter){
this.SomeProperty = oSomeParameter
...
}
}
I don't know how to add prototype functions to this code....
(Sorry if I'm vague on details, I'm not even sure why the namespace is declared like that in my 2nd example so if someone could explain that to me, that'd be great!)
*Edit*
Corrected syntax in 2nd example
*Edit*
Left out the "new" keyword in my example
Defining methods
This piece of code is not syntactically correct:
SomeNamespace.SomeOtherNamespace = {
SomeClass = function(oSomeParameter){ // you probably have : instead of =
this.SomeProperty = oSomeParameter
...
}
}
To add an instance method in the second example, you can simply do after the definition of SomeClass:
SomeNamespace.SomeotherNamespace.SomeClass.prototype.SomeClassMethod = function() {
};
In both the first and the second way you mentioned, your code wants to show that these functions (instance methods in first example, classes in second example) all belong to the same object (prototype in first example, namespace in second example). That is all nice and good for a few properties, but i find this gets more in the way when you're dealing with classes with many methods or even worse, namespaces with many classes.
I would recomend you separate your code using different files and minify them together. A folder represents a namespace and a file represents a class. Follow the pattern in your first example, but instead of saying "this is the prototype object with these methods", simply add them one at a time using the example line above.
Declaring namespaces
First of all, we need to be on the same page. In JavaScript a namespace is simply an object (that contains as properties whatever interests you, constructors, static functions - ex factory methods, other namespaces, etc).
The first example a = a || {} makes sure that namespace a is defined but makes sure not to overwrite it if it was defined elsewhere. For most use cases it is enough and it has the advantage of being very concise and clear to most people reading your code.
The second example does something similar to what the first does, but with two differences:
Specifically checks that a was undefined before defining it (ex1 only checked for falsyness which is usually enough)
Adds the _namespace property to a
Regarding the check for undefined, i doubt you need it. If your code has collisions with something that uses 'a' as something else than an object, then there's a high chance something will break regardless of the method used.
The _namespace property is something purely conventional to that code i think. It may help with various tools (perhaps during debugging or for automatic documentation generation), but that's about all i can think of. Obviously you're in a much better position to see if it is actually used for something, so if you encounter an interesting usage, perhaps you could leave a comment.
To sum it up, i prefer the first variant because it is more concise and even more frequent (so easier to recognize by someone reading the code).
Full example:
// class definition
a = a || {}; // global namespace, all good
a.b = a.b || {}; // both lines are needed
a.b.Class = function() {
this.myProp = 'hello';
};
a.b.Class.prototype.myMethod = function() {
};
// usage
var myInstance = new a.b.Class();
instance.myMethod();
var x = instance.myProp;
To create an IDE that would autocomplete all variables the user declares but would be oblivious to other variables such as Math.PI or even the module Math, the IDE would need to be able to identify all identifiers relating to variables declared by the user. What mechanism could be used to capture all such variables, assuming you already have access to the AST (Abstract Symbol Table) for the program?
I am using reflect.js (https://github.com/zaach/reflect.js) to generate the AST.
I think it's pretty much impossible
Here is why I think it's pretty much impossible without executing it:
Let us go through the unexplored parts, from easy to hard.
Easy to catch:
Function scope is missed here:
(function(x){
//x is now an object with an a property equal to 3
// for the scope of that IIFE.
x;
})({a:3});
Here is some fun dirty tricks for you all.:
Introducing... drum roll... Block Scoping!!
with({x:3}){
x;//x is now declared in the scope of that with and is equal to 3.
}
try{ throw 5}catch(x){
x // x is now declared in the scope of the try block and is equal to 5;
}
(people reading: I beg you to please not use these last two for actual scoping in code :))
Not easy:
Bracket notation:
var n = "lo";
a["h"+"e"+"l"+n] = "world"; // need to understand that a.hello is a property.
// not a part of the ast!
The really hard parts:
Let us not forget invoking the compiler These would not show up in the AST:
eval("var x=5"); // declares x as 5, just a string literal and a function call
new Function("window.x = 5")();// or global in node
In node.js this can also be done with the vm module. In the browser using document.write or script tag injection.
What else? Of course they can obfuscate all they want:
new Function(["w","i","n","dow.x"," = ","5"].join(""))(); // Good luck finding this!
new Function('new Function(["w","i","n","dow.x"," = ","5"].join(""))()')();// Getting dizzy already?
So what can be done?
Execute the code, once, in a closed, timed environment when you update the symbol table (just the relevant parts)
See what's the generated symbol table is from the execution
Boom, you got yourself a symbol table.
This is not reliable but it's probably as close as you get.
The only other alternative I can think of, which is what most IDEs are doing is to simply ignore anything that is not:
object.property = ... //property definition
var a = ... //scoped
b = ... //global, or error in strict mode
function fn(){ //function declaration
object["property"] //property with a _fixed_ literal in bracket notation.
And also, function parameters.
I have seen no IDE that has been able to deal with anything but these. Since they're the most common by far, I think it's perfectly reasonable to count those.
By adding them onto am object that already exists....ie
window.mynewvar = 5;
function mynewfunc() {
}