JavaScript: prevent unintentional creation of new property - javascript

Typos happen, and sometimes it is really hard to track them down in JavaScript. Take this for example (imagine it in between some more code):
// no error. I would like a warning
document.getElementById('out').innerHtml = "foo";
For undeclared variables, strict mode helps:
"use strict";
var myHTML = "foo";
myHtml = "bar"; // -> error
But it does not work for the example above. Is there a program or mode that can catch these bugs? I tried JSLint and JavaScript Lint, but they do not catch it.
And ideally, I would like this to still work (without warning):
// should work (without warning)
function MyClass(arg) {
this.myField = arg;
}

Using an IDE like WebStorm helps a lot in detecting this kind of errors.
To prevent accidentally adding properties to an object, you can freeze it:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
With ES6, you could use proxies to detect these errors:
http://www.nczonline.net/blog/2014/04/22/creating-defensive-objects-with-es6-proxies/

Well, thats the price of dynamically typed programming languages. because it is possible to add properties at runtime, there is no real solution to detect typos in code.
you could however add an innerHtml function to the Element prototype, internally calling innerHTML to eliminate certain typos, but I definately wouldn't recommend that.
As other comments already suggested: A good IDE can help you identifiying typos. I'm only working with WebStorm and VisualStudio which both are able to detect undeclared or unused functions.

Related

How can I detect use of invalid properties in javascript?

I'm learning Javascript and I wrote the following code:
if (mystring.len > 0) {
// do stuff
}
I accidentally used .len instead of .length. To my surprise, no error was raised. mystring.len returned undefined and this made the comparison fail but the code kept right on running. I would prefer an actual error to be raised so I can fix the code. Adding "use strict" didn't help, nor did jslint.
I know there are ways to actively check whether or not a property exists, but that's not what I'm looking for. I want Javascript to tell me when I've made a typo in a property name.
Is there a way to cause Javascript to give an error in this case?
Nope - that is how JavaScript works and it's incredibly useful. Who is to say that checking len is something that needs fixing? Consider:
if(mystring.len === undefined) {
mystring.len = "Test";
}
The best you can do is to simply check that the thing is defined before using it:
if(mystring.len !== undefined) {
}
I appreciate the strangeness, and how it doesn't feel robust (having originally come from a C# background) but there isn't a lot you can do unfortunately. The fact that JavaScript is case sensitive makes this even more frustrating. You will learn to live with it though!
If you really really wanted to run some static analysis then you could considering creating a transpiler (e.g. Babel) extension to run this sort of analysis - but it would get really difficult if you ever expected something to be undefined which I find is common place.
edit
Here's a real example that I'd use where undefined is useful. I'm working with a library that needs to move stuff from one location to another. It can't do that unless the original location has been specified, so I might write something like the following, initializing values if I don't have them for some reason:
function update(node) {
if(node.x === undefined) { node.x = 0; }
node.y = node.y || 0; // This is the shorthand way I'd actually write it
// Do some other stuff
};
"use strict" (in my experience) is used so that variables that aren't explicitly declared/instantiated that are then referenced will throw errors (else, JS would just make a new var on the fly). So that wouldn't help here.
This sounds like an error that would typically be picked up by a compiler in other languages, but since JS is interpreted, you won't have that kind of explicit error checking unless you're in a beefy IDE. Are you using a text editor or something to write JS?
Thats not the way JavaScript considers your above code. Every variable in JS is an object. So, when you do mystring.len, its actually trying to access the len property of mystring obj and when it doesn't find that property, it will return undefined - which is how it should be. Thats why you will not be able to find any error using JSLint.
Just to give you an example -
var myObj = {name: 'Hello', id: 1};
console.log(myObj.name); // Returns 'Hello'
console.log(myObj.text); // 'undefined'
In order to prevent such code from giving you any errors, you can easily use the hasOwnProperty() method like follows-
if(myObj.hasOwnProperty('text')) doSomething();
Since myObj doesn't have any property text, the doSomething() function will never be called.
This is the behaviour of JavaScript as mentioned by many/all answers. However there is an option to prevent new properties you might want to try:
Object.seal https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal
The simple answer is JavaScript is not ment to be typesafe...You shouldn't check it, but if you still want to check you can do it by:
if ('len' in mystring){
}
You should look into Typescript if you ask this question...

With operator & dashes in object keys

Until today, I had not known the with operator existed. I stumbled upon it while debugging an issue being thrown from a plugin (Backbone.Epoxy).
The operator creates block level scope for each property on the passed object.
var testObj = { "cat":true };
with (testObj) {
console.log(cat ? "Cat!": "dog"); // Cat!
}
Simple enough? Initially I thought this could potentially be really cool. Until I realized why my code was throwing an error. Here is an example derived from my code.
var testObj = { "css":true, "background-color":"blue" };
with (testObj) {
console.log(css ? background-color : ""); // throws
}
The actual code is a bit more dynamic, but this is essentially what occurs behind the scenes in the plugin. Since dashes are not allowed within variable names but are allowed in property names, which cause the error to be thrown.
So, to the questions:
Is there a way to sanitize the block scope local variable in order to avoid the issues with the dash while keeping it in my property name?
Has anyone else worked around this issue with epoxy?
You would have to make an exception and write:
testObj["background-color"]
As you may suspect, you cannot write just background-color, for the same reason you cannot write testObj.background-color. You should also ask whether using with, which is fairly non-standard, is worth the character-count savings. Usually the answer is "no".

Closure Library ondevicemotion never fired

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.

Intercepting global variable definition in javascript

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.

Obfuscate javascript properties?

I've recently tested UglifyJS and YUI Compressor and noticed something odd.
Both minifiers don't seem to change the names of object properties, only the names of variables and functions.
for instance if I have the following code:
var objName = {first:2, second:4};
alert(objName.first + " " + objName.second);
the names first and second remain unchanged in the minified version.
Why is that?
Since in javascript a new scope is created in a function, you can scope your code in an immediately invoked function.
// scoped
(function() {
var objName = {first:2, second:4};
alert(objName.first + " " + objName.second);
})();
Then using Google's Closure Compiler, if you turn on the "Advanced" optimization it will see that the properties are only used locally, and will obfuscate them.
// result
var a={a:2,b:4};alert(a.a+" "+a.b);
It's because it doesn't know where the object is going to be used. It could be used externally by other code and you wouldn't want your other code to have to change whenever you obfuscate it.
Edit So basically, it's like that to prevent obfuscation from breaking external/internal references to properties that may not be possible to figure out while obfuscating.
Since there are no well defined scoping rules around objects in JavaScript it's impossible to obfuscate the names in a way that is guaranteed to be correct.
For example, if you had the following function:
function f() {
return { first: 'foo', second: 'bar' };
}
In order to obfuscate the property names you would have to nail down all the places that f is called from. Since functions are first-class in JavaScript they can be assigned and passed around in arbitrary ways making it impossible to pin down where f is referenced without actually running the program.
Additionally, JavaScript doesn't have any way for you to specify intent around what's public API and what isn't. Even if the minimizer could reliably determine where the function is called from in the code you give it, there would be no way for it to make the same changes to code that it hasn't seen.
I guess that's because the minifiers would break the object properties. Consider this:
function getProp(ob,name) {
return ob[name];
}
var objName = {first: 2, second: 4};
var prop = getProp(objName, "second");
There's no way for the minifier to know the string literal "second" being an object property. The minified code could look like this then:
function a(b,c){return b[c]}var d={p1:2,p2:4};var e=a(d,"second")
Broken now.
The latest release of uglify (today) has object property mangling, see v2.4.19. It also supports reserved files for excluding both object properties and variables that you don't want mangled. Check it out.
The only public tool so far to obfuscate property and function names (afaik) is the Closure Compiler's Advanced mode. There are a lot of limitations and restrictions, but the end result is generally worth it.
As a passing note: the Dojo Toolkit is compatible (with some minor modifications) with the Closure Compiler in Advanced mode -- arguably the only large-scale public JavaScript library that can be fully obfuscated. So if you are looking at obfuscation to protect your IP, you should look into using Dojo for the task.
http://dojo-toolkit.33424.n3.nabble.com/file/n2636749/Using_the_Dojo_Toolkit_with_the_Closure_Compiler.pdf?by-user=t
Stephen
What about doing something like:
// scoped
(function() {
var objName = {first:2, second:4};
var vA = 'first';
var vB = 'second';
alert(objName[vA] + " " + objName[vB]);
})();
Once objName.first and/or objName.second are referenced enough times, this technique will start to save characters. I can't think of any reason that wouldn't work, but I can't find any minifiers that do it.

Categories