I'm trying to tidy up some javascript code and one of the steps is removing all useless (or plain wrong) global variables that have slipped in from errors like:
for (prop in obj) { ...
instead of
for (var prop in obj) { ...
JSLint helps a bit in finding out this nastiness, but it is not 100% foolproof when the nastiness happens at runtime.
I already tried to add some monitoring code that routinely checks the global scope logging to the console if some new variable is detected, and that helped some more, but when it tells me that a new global variable named "i" has been detected ... well, it's a mess finding out where that happened in thousands of lines of code.
So here we come: is there a better way/tool/script/whatever to find the little pests?
My dream is something like a Firebug plugin that stops the execution whenever a new global variable is created...
Thanks!
You may find this bookmarklet useful.
Also, checkout this answer: How to detect creation of new global variables?
You can now intercept variable definition as explained on this similar question
window.__defineSetter__('sneakyVariable', function() {
debugger
})
and you'll be able to find where it was defined
I wonder if you could set a timeout to create a list of all global variables and then compare that against the last time the timeout fired. I found this on Stack Overflow, and maybe you could use this code in conjunction with a setTimeout() to get what you want.
Blockquote
Yes and no. "No" in almost every situation. "Yes," but only in a limited manner, if you want to check the global scope. Take the following example:
var a = 1, b = 2, c = 3;
for ( var i in window ) {
console.log(i, typeof window[i], window[i]);
}
Stack Overflow link: Getting All Variables In Scope
well, I wrote this long time ago, so code sucks, but it does the job: https://gist.github.com/1132193
paste in the firebug console or include as a script.
You say, you are trying to tidy up some code.
In that case - use IDE, like NetBeans PHP (free) or JetBrains WebStorm (30$). They both color global variables, and do lots of other useful stuff ;)
If your polling script will still detect creation of global variables - trace down offending functions, and make them suffer ;) Eventually, the code will become clean.
Related
Dear Javascript programmers,
Google's Closure Library is always good for driving people crazy. I hope you can help me on this issue:
I want to catch the ondevicemotion-Event by javascript within a function within the Closure lib.
Without Closure everything works fine with the following code which I grabbed from this page (thanks to the author): http://www.peterfriese.de/how-to-use-the-gyroscope-of-your-iphone-in-a-mobile-web-app/
The following snippet shows "my" code:
if (window.DeviceMotionEvent != undefined) {
console.log("DME");
window.ondevicemotion = function(e) {
console.log("ODM");
// handle events like e.rotationRate
...
}
}
This works perfectly in a standalone html page. Both console.logs are triggered.
In contrast, Closure seems to have a problem with window.ondevicemotion = function(e) { because the console logs "DME" but not "ODM".
There are no compiler warnings or errors.
Has anybody recognized such a problem, too? I sadly have no idea why Closure acts so stupid (more probably I am so stupid). ;-)
Thanks for reading! Any help appreciated!
Running in ADVANCED_COMPILATION, the Closure Compiler minimized and obfuscated window.ondevicemotion for me, so I'll assume that is your problem as well. There are two ways to step around this:
1. Use bracket notation to set / access the property.
The Closure Computer will not rename any properties that are referenced via the bracket notation. The following should not get obfuscated (though, potentially, it could get rewritten as window.ondevicemotion):
window['ondevicemotion'] = function(event) { ... }
2. Use an extern to let the compiler know not to rename this property.
Similar to the situation above, the Closure Compiler takes a hint and doesn't rewrite the property, leaving it as is. This, however, has a nice benefit of giving you some type checking, since you're defining what the signature of the extern is to the Closure Compiler:
/**
* #param {goog.events.Event} event
*/
window.ondevicemotion = function(event) {};
The decision of which one to use is ultimately yours. In this scenario I would most likely go for the second option and only go back to the first if there was some reason you could not use externs.
I noticed that Google Closure Compiler did not rename document to something like d to reduce space.
I cannot think of a case where this would break the code (ie where document points to something else down the road). Actually the same goes for window.
Is there a reason for protecting document this way?
== EDIT ==
By renaming it I was thinking reassigning it. Example below.
var d=document;
var obj1=d.getElementById("obj1");
var obj2=d.getElementById("obj2");
... // with enough uses of document so it makes to reassign it size-wise.
Closure-compiler does not perform this "optimization" by default for the simple reason that it produces LARGER source when used with gzip. You can enable this optimization by turning on the AliasExternals pass using either the Java API or a custom build.
See https://code.google.com/p/closure-compiler/source/browse/src/com/google/javascript/jscomp/AliasExternals.java#38
What happens?
ProblemFactory's guess is correct.
This is a //TODO in the closure compiler source code. If we didn't preserve document and window and instead ran them over with d for example, at the moment the closure compiler does not know if it's overriding a global from another file. Like the comments say this will be resolved in the future at which point.
Enough words, show me the source!
If we check the closure compiler source code inside VariableReferenceCheck.java we can find the following:
private class ReferenceCheckingBehavior implements Behavior {
#Override
public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {
// TODO(bashir) In hot-swap version this means that for global scope we
// only go through all global variables accessed in the modified file not
// all global variables. This should be fixed.
// Check all vars after finishing a scope
for (Iterator<Var> it = t.getScope().getVars(); it.hasNext();) {
Var v = it.next();
checkVar(v, referenceMap.getReferences(v).references);
}
}
If we check the hot-swap algorithm itself we can see that:
// Note we use the global scope to prevent wrong "undefined-var errors" on
// variables that are defined in other JS files.
So, we can see that this is just the closure compiler not understanding the code of globals across multiple files well enough to make that replacement. You can always do the replacement yourself :)
I think document is standardized, always-global variable. To use the same way d it has to be global also, thus global namespace will have another "junk" variable.
It could be dangerous for not aware developers (which wont be aware of that thus it is not standard variable).
I saw many code that began like that
var a=a||{};
Now I know that its check if a exist.
My question is why to check it if its at the first of the code?
I mean the programmer know that a is not exist.
The programmer should know if the variable exists or not.
It's used to mash different pieces of script together without having to keep track of which part of the script is loaded first. Several scripts will have the same piece of code at the start, so they share the same variable.
For the first script, the variable is declared, and there is no previous value for it, so the {} value is used.
For the following scripts that use it, the var is ignored as the variable is already declared, and the previously set value of the variable is used.
Of course, declaring the same variable in several places has a code smell to it. Preferrably you should keep track of what you are doing so that you only declare it once.
Translated into clearer code
var a; // The variable declaration gets brought up because of hoisting
More info on hoisting here
if( a ) {
a = a; // if a is a truthy value then assign it to itself
} else {
a = {}; // if a is a falsy value then set the default to an empty object
}
Hope that helps
That's a shortcut to fall back on a default value - {} in this case.
Basically, javascript can be written in multiple files and within each file you can have multiple declarations and functions defined.
Even if the programmer knows for a given instance if the variable exists or not, there is no way to know if it already exists when this code is called from somewhere else.
This should not happen in well written code (all from one developer / house) but it does happen in projects where the js code is amalgumated from multiple places.
This SO question has a very nice answer about variable scopes in javascript, it should clarify your doubts.
Quick question. I just came across the following in a JS file:
if (0) {
// code
}
What's the purpose of this if statement? Which cases would the code execute? It currently doesn't run the code in the if clause.
It looks like an artifact of the development process. Wrapping code like that gives you a quick, 1-character way to effectively comment out a block of code. It's quicker to toggle than a typical multi-line comment.
Someone has used that to effectively comment out a large chunk of code.
They probably meant to remove it entirely before releasing to the public, but forgot.
Actually, in Javascript, some code runs even in such block. For example, variables being defined in if(0) block will be defined with the value undefined:
if (0)
{
var f = 1;
}
f; //undefined
g; //ReferenceError
Another good example is the case of declaring functions in blocks. This is undefined behaviour in Ecmascript, so the results may vary across browsers:
if (0)
{
function f() {}
}
typeof f; //"undefined" in Firefox, "function" in other browsers
IIRC some bulletin board software generates 0 or 1 on the server side; see #eds's post.
It won't run. It could be someone was trying to comment out the code and there were too many /* */ in the middle (or maybe there was some other reason why the original author didn't want to use /* */?). Regardless of the reason, if(0){} means if(false){}. The code between the braces will never run.
that's possible temporary disabled part of code
It's probably just an easy way to toggle enabling/disabling code, but what you may be seeing is JavaScript code that was rendered by a server-side language. So, for example, if you were writing in PHP, and you wanted client-side JavaScript to run if your PHP variable $doThis was non-null, you might write
if (<? echo $doThis; ?>) {
// do stuff...
}
There aren't many situations where this happens, but I know Blogger uses technique where it displays the number of comments below a post.
I'm trying to get 2 scripts to run on the same page but they won't play nice with each other. One is called TabTop http://www.isdntek.com/tagbot/tabtop.htm and the other is Clic*Pic http://www.isdntek.com/tagbot/gallery.htm, both by isdntek. I can get either one of them to run fine all by themselves, but not both together. I looked around and tried to find the answer to this problem by myself, but to no avail.
I would greatly appreciate any help that can be provided.
Thanks!
You can wrap each script in a self calling function:
(function(){
//As long as you don't use global variables
//the content here is protected from any interaction with the outside
})();
Now, if both codes use global variables, the task will be unfortunately harder.
The RainbowCodeModule6.js file is used by both pages, it sets a very large number of global variables (quite a few because it doesn't declare local variables within functions), so it is quite possible that with two scripts trying to use the same set of globals, they are getting conflicts. e.g. (my wrapping for posting here)
function changeShades(color){ //--update the vertical column of light/dark shades
var ymax=paletteymax
if (!color){return}
for (i=0; i<ymax; i++){
document.getElementById('colorShades'+i).
style.backgroundColor=colorBrightness(color,(ymax-1-i)/(ymax-1))
}
}
The above doesn't keep it's counter i local and depends on the global paletteymax. I can't say if that's your problem, but it is indicative of poor programming and application architecture. Another example:
function dec2hex(R,G,B) { //--Converts three R-G-B components to
// a single internet hex color
var hexTest="0123456789ABCDEF";
Rd_hi=R/16; Rd_lo=R%16;
Rd=hexTest.substr(Rd_hi,1)+hexTest.substr(Rd_lo,1)
Gn_hi=G/16; Gn_lo=G%16;
Gn=hexTest.substr(Gn_hi,1)+hexTest.substr(Gn_lo,1)
Bu_hi=B/16; Bu_lo=B%16;
Bu=hexTest.substr(Bu_hi,1)+hexTest.substr(Bu_lo,1)
hexval='#'+Rd+Gn+Bu
return hexval;
}
Why they decided to keep hexTest local but let all the other variables go global is beyond me. Variables R, G and B are also global, but here they are kept local because they are formal parameters in the function declaration.
It also uses document.write to write a table in parts, which is never a good idea. I think it's just a poorly written script, find something else.