All possible ways to declare Javascript variables - javascript

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() {
}

Related

Searching local javascript variables by name patterns

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 javascript in the browser

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 is declaring a variable - right?

On a blog I see the following:
for (var key in map) {
if (map.hasOwnProperty(key)) {
var value = map[key];
// right, now we can get some work done
}
}
"Now you see that var key at the top of the for loop? That’s not
declaring a variable, oh no. It’s saying that somewhere else there’s a
variable called key"
Surely this is declaring a variable (if one named key did not previously exist in the scope chain)? What might the author mean by this?
Link: http://dannorth.net/2011/12/19/the-rise-and-rise-of-javascript/
Surely this is declaring a variable (if one named key did not previously exist in the scope chain)?
Yes, it is, within the function that for loop is in. (The variable is not limited to the for loop like it would be in, say, Java. Its scope is the entire function it's in.)
More about var (on my blog): Poor, misunderstood var
What might the author mean by this?
It sounds sarcastic, actually, like the author is trying to make a point by saying the opposite of what they mean.
Edit Since you've posted the link, here's the complete quote:
Now you see that var key at the top of the for loop? That’s not declaring a variable, oh no. It’s saying that somewhere else there’s a variable called key (right at the top of the nearest containing function, it turns out). Oh, and that key variable is visible all through that function, and any functions it contains, not just tucked away in the for loop. Brilliant!
The point he's trying to make there is that the variable isn't just limited to the for loop. But that initial statement is flatly incorrect. I know what he means, but it's not what he said.
Re the first point above, in ES6 JavaScript will be getting a new keyword, let, which would declare something only for the for loop:
// New in ES6, you probably can't use this yet
for (let key in map) {
// ^^^---------------- change is here
if (map.hasOwnProperty(key)) {
let value = map[key];
// ^^^----------------- probably want to change this one, too
// right, now we can get some work done
}
}
Some engines already support it, but you can't use it broadly yet, as many don't. And in theory the ES6 draft spec could change, though I really doubt it will. :-)
That, actually is a full definition of a variable from an iterator value that's fetched at the current iteration of variable map. It's a declaration + initialization.
To understand the notion of iterators you would have to go deeper and look into the underlying interpreter code which supports the value of the key to every iteration of the loop.
Also this: "Now you see that var key at the top of the for loop? That’s not declaring a variable, oh no. It’s saying that somewhere else there’s a variable called key" sounds stupid, doesn't explain anything and creates confusion.
I wouldn't read such articles, because such a statement shows that the author isn't really acquainted with the real world behind JavaScript - that is C/C++ or even assembly, which work at the basic memory level, and use constructs called iterators to support values to loops in case of data structures that are more advanced than simple arrays.

Should I use a var per each require? [duplicate]

Reading Crockfords The Elements of JavaScript Style I notice he prefers defining variables like this:
var first='foo', second='bar', third='...';
What, if any benefit does that method provide over this:
var first='foo';
var second='bar';
var third='...';
Obviously the latter requires more typing but aside from aesthetics I'm wondering if there is a performance benefit gained by defining with the former style.
Aside of aesthetics, and download footprint, another reason could be that the var statement is subject to hoisting. This means that regardless of where a variable is placed within a function, it is moved to the top of the scope in which it is defined.
E.g:
var outside_scope = "outside scope";
function f1() {
alert(outside_scope) ;
var outside_scope = "inside scope";
}
f1();
Gets interpreted into:
var outside_scope = "outside scope";
function f1() {
var outside_scope; // is undefined
alert(outside_scope) ;
outside_scope = "inside scope";
}
f1();
Because of that, and the function-scope only that JavaScript has, is why Crockford recommends to declare all the variables at the top of the function in a single var statement, to resemble what will really happen when the code is actually executed.
Since JavaScript is generally downloaded to the client browser, brevity is actually quite a valuable attribute. The more bytes you have to download, the slower it gets. So yes, there is a reason apart from aesthetics, if not a massive one.
Similarly, you'll see people preferring shorter variable names to longer.
Personally, I don't bother minimising whitespace, as there are minimisers that can do this sort of thing for you (for example in YUI), and lack of indentation and spacing leads to less maintainable code.
No difference in semantics and no measurable difference in performance. Write whichever is clearest.
For simple examples like:
var first= 'foo', second= 'bar', third= 'bof';
the concise single-statement construct is probably a win for readability. On the other hand you can take this much too far and start writing half your program inside a single var statement. Here's a random example plucked from the jQuery source:
var name = match[1],
result = Expr.attrHandle[ name ] ?
Expr.attrHandle[ name ]( elem ) :
elem[ name ] != null ?
elem[ name ] :
elem.getAttribute( name ),
value = result + "",
type = match[2],
check = match[4];
I find this (by no means the worst example) a bit distasteful. Longer examples can get quite hard to read upwards (wait, I was in a var statement?) and you can end up counting the brackets to try to work out what's a multi-line expression and what's just an extended var block.
I believe that what he is going for is declaring all variables as abosultely the first statement in a function (You'll notice that JSLint complains about this if you use it and don't declare them on the first line). This is because of JavaScript's scope declaration limitations (or quirks). Crockford emphasizes this as good practice for maintainable JavaScript code. The second example declares them at the top, but not in the first execution statement. Personally, I see no reason as why to prefer the first over the second, but following the first does enforce that all variables are declared before doing anything else in the function.
David is right that the larger the script the more time it will take to down load, but in this case the difference between the two is minimal and can be handled by using YUI compress etc.
It's a personal programming style choice.
On the one hand there is readability, wherein placing each variable declaration on a separate line makes it more obvious what's going on.
On the other hand, there is brevity, wherein you're eliminating transmitting a few extra bytes over the network. It's generally not enough to worry about, unless you're dealing with slow networks or limited memory on the client browser side.
Brevity is also known as laziness on the part of the programmer, which is one reason that many purists avoid it.
It all comes down to personal taste or a set of style-guides, your development team follows. If you are serving JavaScript yourself, you usually compress or minify your script(s) into one long string in one single file. So the whole you-are-saving-bytes-and-your-scripts-download-faster argument is, well, not an argument :)
I usually declare my variables like this: (a style you didn't mention)
var something,
somethingElse,
evenMoreSomething,
andAnotherThing;
A statement like "var" is not minified/compressed.Every time you place a var, instead of a comma you lose 4 chars, if I count right.

Scope on Object not Behaving as Expected

I'm trying to clean up a long-standing bad habit in coding: Writing widgets to the global scope.
I've made a large, mostly self-contained script for a news roll on a webpage and was pleasantly surprised at how well it worked after I stuffed the whole thing into a function... TOO well in fact.
To ensure I wasn't cheating, I wrote the following code to make sure my scoping was correct:
var story_count = "THIS IS NOT A NUMBER";
console.log(story_count);
touch_roll = function()
{
this.story_count = 0;
}
console.log(story_count);
touch_roll();
console.log(story_count);
Fully expecting this to yield the following response in the console
THIS IS NOT A NUMBER
THIS IS NOT A NUMBER
THIS IS NOT A NUMBER
I was entirely surprised to find the output was, in fact, this:
THIS IS NOT A NUMBER
THIS IS NOT A NUMBER
0
Is this not what I think it is? Research hasn't really helped, but I'm a little burnt out so it's entirely possible I'm reading it all wrong. If not this, then how DO I keep all of my namespaces completely within the scope of that function so as not to mess with existing bits on the site?
As per Matt's answer below, here is the corrected code:
var story_count = "THIS IS NOT A NUMBER";
console.log(story_count);
var touch_roll = new function()
{
this.story_count = 0;
console.log(story_count);
}
console.log(story_count);
By declaring touch_roll as a var and instantiating the function as new Javascript evaluates said function into an object as run-time (so we can also remove the call to touch_roll() at the bottom).
The corrected output is as follows:
THIS IS NOT A NUMBER
0
THIS IS NOT A NUMBER
Because you're not invoking touch_roll with new, this is the window object (which is what global variables in browser environments belong to).
If you were to use new, then you'd get:
THIS IS NOT A NUMBER
THIS IS NOT A NUMBER
THIS IS NOT A NUMBER
Additionally, you're declaring touch_roll as a implicit global variable. Either use function declaration syntax, or add a var to it.

Categories

Resources