A Safer JS Environment

Oh well, apparently I wasn't joking here and I went even further ... so here I am with a weird hack you probably never thought about before ;)

A Globally Frozen Environment

Have you ever thought about this in the global context?

Object.freeze(this);

Apparently not even browser vendors such Chrome or Safari since this condition, today, is always false: Object.isFrozen(Object.freeze(this));, and even if it works as expected after freezing.
Firefox and node.js got it right while Opera Next throws an error .. but latter a part ...

Stop Any Global Pollution

That's right, if you freeze the window or global object, guess what happens here:

Object.freeze(this);

var a = 123;
alert(this.a); // undefined
alert(a); // error: a is not defined

We cannot even by mistake create a global variable ... there's no lint that could miss that.

Moreover, if you are worried about malicious code able to change some global function or constructor, you can stop worrying with proposed freeze call: that will break instantly and any manual quick test will instantly fail rather than keep going.

What About Namespaces

Well, if global namespaces, this hack will prevent the creation of any namespace.
However, we are in RequireJS and AMD module loader era where a module is imported inline through require() or inside a callback with AMD. The only important thing, is that at least the require function must be defined before this hack is performed or even that one cannot be used.

Once we have require() things are that easy, you create your own private scope and you do your own stuff in that scope being still sure that you won't pollute the global scope plus you won't be scared about other scripts ... you have your virtual sandbox

Object.freeze(this) && function (global) {
// here all local variables you want
var
fs = require("fs"),
mymodule = require("./mymodule")
;
// do the hack you want
}(this);

// AMD style
require(["fs", "./mymodule"], function (fs, mymodule) {
// already in a closure so ...
// do the hack you want
});


Too Restrictive? Object.prototype Then!

At least we can think about freezing the Object.prototype as the very first script in any webpage so the nightmare JSLint is talking about, the slow, boring, and probably already not necessary since no recent library is extending the Object.prototype since ages, hasOwnProperty() check, does not need to be in every bloody for/in 'cause you know what? Nobody can change, add, pollute, the global Object.prototype!

Object.freeze(Object.prototype);

// everything else after

for (var key in {}) {
// screw {}.hasOwnProperty, IT'S NOT NEEDED!
}



How About Both

If you are the only owner of your scripts, if you load node.js modules, by default with the ability to screw things up in the global context, or if you use AMD where global pollution should never be necessary, you might decide that this script is your best friend.

try {
// (C) WebReflection - Mit Style License
!function(global, Object){"use strict";
// buggy in both Chrome and Safari
// always false
if (!Object.isFrozen(global)) {
var freeze = Object.freeze;
Object.getOwnPropertyNames(
global
).forEach(function (prop) {
var tmp = global[prop];
switch(typeof tmp) {
case "function":
tmp = tmp.prototype;
case "object":
if (tmp) {
// console might not be freezable
// same is for String.prototype
// if applied twice and only in Safari
try {freeze(tmp)} catch(o_O) {}
}
break;
}
});
// Opera Next has some problem here
try {freeze(global)} catch(o_O) {}
}
}(this, Object);
} catch(o_O) { /* still in IE < 9 browser ... */ }

As summary, let's write down benefits of this technique:
  1. global context is free from pollution, no missed var will work anymore so it's instantly fixed before the eventual usage of a linter
  2. for/in loops could have a massive boost over object literals since nobody can possibly change the Object.prototype
  3. ES5 enabled browsers, which means all current browsers for desktop and mobile except desktop IE < 9, will prevent greedy or outdated scripts, to make the environment less secure
  4. nobody can redefine eval or Function, used sometimes for reasons but the most insecure global functions we have in JS
  5. we are forced to think in modules, 'cause there won't be any other way to load external script or dependency
  6. the only script that needs, eventually, to be loaded, will be the require() one, which means faster bootstrap by default thanks to smaller amount of synchronous bytes required to initialized our project
  7. in node.js, we are forced to write in a function even the main program, rather than polluting global object with stuff that might disturb required modules (which is true for all require/AMD based solutions too)

What do you think?

Comments

Popular posts from this blog

8 Things you should not be afraid of as a Developer

News

Why REST is so important