I am a beginner in JavaScript, and I am now given a project that used Javascript heavily. In most of the JS files, it will contain statements of the following format:
Jebber.Ajax.enqueueAction(smsType, true, options, callback);
I am familiar with Java, so I assume there might be some import or sth. at the top of the file, but there isn't any, and there is no explicit definition of "Jebber" variable. So I am curious about where does this Jebber variable come from. I am using Eclipse to edit the JavaScript files, it seems F3 doesn't work to retrieve the definition of a method.
So I am a little bit stuck here:could experts teach me a good way to dive through a large collection of JavaScript files to find the definition of variables/functions etc.? Thanks in advance.
You can do that using JS Object setters:
Object.defineProperty(window, 'Jebber', {
set: function() {
debugger;
},
get: function() {}
});
When something is assigned to a global Jebber variable you'll get a break in a js debugger (use the one in chrome dev tools for example)
Live demo: http://jsfiddle.net/XtZkJ/
Open debugger and see on the stack trace - it will point to the exact line the variable modified.
The "import" will be <script> tags within the HTML file, not the script file.
Related
Im using jQuery-migrate on a big project. I corrected all the warnings I could but now the warnings are inside libraries.
Some libraries are not updated anymore so I can't update them to make them work with jQuery-3.3.1.
Also, I can't replace the depreciated functions directly in the libraires because it is creating errors.
So I think that I'll keep jQuery-migrate in my project.
My question is : If jQuery-migrate is able to correct the depreciated functions when called, why it can not correct them directly in the code ?
JavaScript does not lend itself to static code analysis.
Say you needed to replace the foo function.
That would be fairly trivial if the code that called it was simply:
something.foo();
It becomes rather harder if it is:
function call(bar, method) {
bar[method]();
}
call(something, "foo");
… and even harder if the logic needed to get there was more complicated.
Creating something.foo so it just exists if anything try to access it at runtime is much simpler.
This is going to be a problem that very few if any people have, but it was time consuming and difficult to solve and i figured it out, so here is the solution, one other person in the world who has this problem.
Are you getting all kinds of weird "this.getOnOffValueFromModelValue is not a function" kinds of errors from guideRuntime.js, guide.js , or guidelib -- or other adobe frontend libraries that you do not control?
These may not apply to you directly but if they do definitely keep reading:
Are you using webpack and including custom code on the page elsewhere (perhaps via a data-sly-use or something)?
Does your project use lodash? or perhaps another library that uses a "_" global variable?
Or perhaps jQuery? or other frontend library?
Then you may be asking questions like:
Why is my form not working properly?
Why is some stuff on the form just not working?
Some rules are just not working... why not and how do i fix them?
Oh I'll feed you, baby birds.
( can someone with a higher reputation add the tags adobe-form, aem-form to this, please)
TLDR:
If you're using Webpack, add this to your module rules:
module: {
rules: [
{ parser: { amd: false } }
// ... your other rules
]
}
or if you're not using webpack, make sure your own variables are not leaking into the global scope, because adobe's are and yours may overwrite theirs or vice-versa.
The explanation that may be applicable even if you're not using webpack that may get you in the right direction:
Adobe's client libraries (like guide.js and guideRuntime and the like) aren't particularly good at dependency injecting their stuff. Their code leaks global variables a lot. For example (see picture below) they use underscore's _.each a lot. if you're using lodash, the function signatures are different-- underscores' allows you to define a "this" context as the third parameter, whereas lodash's doesn't.
So, in our case, our lodash was overwriting their underscore causing a bunch of weird issues... EVEN THOUGH we were NOT declaring lodash globally anywhere. WEBPACK WAS defining lodash globally
So moral of the story, open up console, check to see if any of your libraries are defined globally, and if they are, figure out how to make them not global until Adobe decides to fix their stuff (don't hold your breath).
I know that I can type into Chrome or FF the following command:
Object.keys(window);
but this displays DHTMLX stuff and also function names in which I'm not interested in. And it doesn't display variables in functions that have not been executed yet. We have more than 20,000 lines of JavaScript codebase, so I would prefer static code analyis. I found JavsScript Lint. It is a great tool but I don't know how to use it for displaying global vars.
So I'm searching for memory leaks, forgotten var keywords, and so on...
To do [only] what you're asking, I think you're looking for this:
for each (obj in window) {
if (window.hasOwnProperty(obj)) {
console.log(obj);
}
}
I haven't linted that code, which is unlike me, but you get the idea. Try setting something first (var spam = "spam";) and you'll see it reported on your console, and not the cruft you asked about avoiding.
That said, JLRishe is right; JSLint executes JavaScript in your browser without "phoning home", so feel free to run it. There are also many offline tools for JSLinting your code. I use a plugin for Sublime Text, for instance.
If you'd like some simplest-case html/JavaScript code to "wrap" JSLint, I've got an example here. Just download the latest jslint.js file from Crockford's repository into the same directory, and poof, you're linting with a local copy of JSLint.js. Edit: Added code in a new answer here.
Though understand that you're linting locally with that wrapper or when you visit JSLint.com. Honestly, I can say with some confidence, Crockford would rather not see our code. ;^) You download JSLint.js (actually webjslint, a minified compilation of a few files) from JSLint.com, but you execute in the browser.
(Admittedly, you're technically right -- you never know when that site could be compromised, and to be completely on the up and up, you sh/c/ould vet jslint.js each time you grab a fresh copy. It is safer to run locally, but as of this writing, you appear safe to use JSLint.com. Just eyeball your favorite browser's Net tab while running some test, non-proprietary code, and see if any phoning happens. Or unplug your box's network cable!)
Rick's answer to use "use strict"; is another great suggestion.
A great way to catch undeclared variables is to add 'use strict' to your code.
The errors will appear in the console, or you could display them in a try ... catch block:
'use strict';
try {
var i= 15;
u= 25;
} catch(ee) {
alert(ee.message);
}
I found a very good solution to list all the global variables with the jsl command line tool:
Here is the documentation
I just have to put /*jsl:option explicit*/ into each file that I want to check. Then it is enough to run ./jsl -process <someFile> | grep 'undeclared identifier'
It is also possible to use referenceFile that contains some intentional global variables /*jsl:import <referenceFile>*/ so these variables will not be listed.
I have simple example:
Javascript:
function testBut(b){
alert("!");
}
HTML:
<button onclick="testBut(this)">Test</button>
Now I run my .js script through Google Closure compiler (running from commandline), and I want to keep testBut function intact.
I read in many places that I have to use --externs option and define another file with names of exported functions, in this case it would hold just:
function testBut(b){}
And additionally I need to add funny line to my js code:
window['testBut']=testBut;
So now questions:
is that really so stupid system in Closure with two bug-prone steps to be done, in order to keep desired function?
is there no "#..." annotation that would simply suit same purpose? I tried #export, but it requires --generate_exports option, and still it generates similar ugly and useless goog.a("testBut", testBut); in target code (I tried same code, and those goog.a(...) seems simply useless), and this still requires the exports file
Ideally I'm looking for simple annotation or commandline switch that would tell "don't remove/rename this function", as simple as possible, no code added, no another file.
Thanks
Don't confuse externs and exports.
Externs - provide type information and symbol names when using other code that will NOT be compiled along with your source.
Exports - Make your symbols, properties or functions available to OTHER code that will not be compiled.
So in your simple example, you need:
function testBut(b){
alert("!");
}
window["testBut"] = testBut;
However this can be simplified even further IF testBut is only for external calls:
window["testBut"] = function(b) {
alert("!");
};
Why not always use the second notation? Because internal usage (calls within the compiled code) would then have to use the full quoted syntax which blocks type checking and reduces compression.
Why not use a JSDoc Annotation for Exports?
This question comes up a lot.
For one, there isn't global consensus on how exports should be done. There are different ways to accomplish an export.
Also, exporting symbols and functions by definition blocks dead-code elimination. Take the case of a library author. The author wishes to compile his library exporting all public symbols. However, doing so means that when other users include his library in a compilation, no dead-code elimination occurs. This negates one of the primary advantages of ADVANCED_OPTIMIZATIONS.
Library authors are encouraged to provide their exports at the bottom of the file or in a separate file so that they can be excluded by other users.
It has been suggested before to provide a command line argument to control exporting based on a namespace. IE something like --export mynamespace.*. However, no author has yet tackled that issue and it is not a trivial change.
I've written a server-side utility that is meant to manage javascript page dependencies. During development, it serves the javascript as separate files (embeds individual script tags in the order defined), and in production mode it reads the files, minifies them (Google Closure in Whitespace only mode), and embeds them via a single script tag.
For the moment, I've turned off minification to eliminate that variable...so all it is doing is joining the files together with a newline between each.
When I go to production mode, I get spurious problems in a number of the files that are embedded.
So, does anyone have any ideas of what problems I could be causing by serving a set of files as a single, concatenated file? I'm at a loss.
For those wanting more detail:
I am certain the order is correct.
The list of files in question is rather large for this example, but include jquery, angular, controllers, jquery dnd fileupload, controllers, etc.
I get "Uncaught Type Error: undefined is not a function" at this line of the combo:
(function(b){b.support.touch="ontouchend" in document;if(!b.support.touch){return;}var c=b.ui.mouse.prototype,e=c._mouseInit,a;function d(g,h){if(g.originalEvent.touches.length>1){return;}g.preventDefault();var i=g.originalEvent.changedTouches[0],f=document.createEvent("MouseEvents");f.initMouseEvent(h,true,true,window,1,i.screenX,i.screenY,i.clientX,i.clientY,false,false,false,false,0,null);g.target.dispatchEvent(f);}c._touchStart=function(g){var f=this;if(a||!f._mouseCapture(g.originalEvent.changedTouches[0])){return;}a=true;f._touchMoved=false;d(g,"mouseover");d(g,"mousemove");d(g,"mousedown");};c._touchMove=function(f){if(!a){return;}this._touchMoved=true;d(f,"mousemove");};c._touchEnd=function(f){if(!a){return;}d(f,"mouseup");d(f,"mouseout");if(!this._touchMoved){d(f,"click");}a=false;};c._mouseInit=function(){var f=this;f.element.bind("touchstart",b.proxy(f,"_touchStart")).bind("touchmove",b.proxy(f,"_touchMove")).bind("touchend",b.proxy(f,"_touchEnd"));e.call(f);};})(jQuery);
which follows some other file embeds that generate global vars:
var FocusElementDirective = function() {
...
}
var DirectiveApplier = function(){
...
}
var AgeCalculator = function(){
...
}
Another one: when I trigger an "add" event in the angular plugin for jquery fileupload, it gives Uncaught TypeError: Object # has no method 'scope'. The line it is pointing to is within the jquery fileupload angular module (line 89 of version 9.0.5):
add: function (e, data) {
if (e.isDefaultPrevented()) {
return false;
}
var scope = data.scope(), // this line
Just happening upon this and the first thing that comes to mind is that it could be something to do with global variables, one of those things Crockford complains about as a weakness of javascript.
For a quick description:
JavaScript global variables & self-invoking anonymous functions
Just off the top of my head.
So, I found it, and it turned out to be something ultra-simple...so simple that I pulled my hair out for hours looking for something technical:
I had a duplicate.
When loaded as tags, the browser only bothered to load the file once. When concatenated, the code was actually duplicated. This led to all sorts of weirdness (e.g. event handlers were getting hooked to the wrong version of things).
Coming from a compiled language background, I kinda would expect some duplicate symbol kinds of notifications...ah dynamic languages.