Some library is truly aggressive. It could change the entire behavior of each other lib present in the page via not-standard or future implementations of some global object prototype, such String, Object itseld, Array, or others.
Since this is a well known problem, some library/framework can try to avoid conflicts in different ways. As example, jQuery has a noConflict method which aim is to make development as painless as possible ... but it is not always that simple. One of Elsewhere purpose is to connect a completely different and external scope into the main window, avoiding any sort of conflict a library with its global properties, functions, or prototypes, could have. First example:
new Elsewhere(
"scripts/libs/jquery.1.3.2.min.js"
).execute(function(ready){
// this scope is elsewhere in a perfectly clean environment.
// In this case jQuery will be manifested as global "$" function.
// To bring jQuery outside, we can use this sandbox parent reference
parent.jQuery = function(search, context){
// to make sure we do not search in an "empty" document,
// the one part of this sandbox, we pass the context
// if not present as parent.document
return $(search, context || parent.document);
};
// we can call the ready callback
ready(parent.jQuery);
},
[ // one or more arguments to pass into Elsewhere function execution
function($){
// let's do something on ready event
// please note there is no var in front of jQuery
// since it has been declared as global
jQuery(function(){
// we could use the argument as well
// since in this case it is exactly
// the same jQuery function
$("body").html("Here I Am");
});
}
]);
Confused? Please read more ;)
Quick Answer: What Is A Sandbox
Every time we open a new window in our browser, the latter one creates a (almost)clean environment to run, if presents, each JavaScript file. The same happens for each tab, which means that if we define a function Z(){} in tab a, this will not be present in tab b. Finally, same things happens inside frames or iframes as well. Each window, framed, tabbed, or not, will have then its own JavaScript environment and in JS world this is called sandbox.
What Is Elsewhere
Elsewhere is a lightweight, less than 1Kb if minified and gzipped, almost fully cross-browser library (so far compatible with each browser I tested) able to create runtime a sandbox including zero, one, or more scripts. The first showed example, creates a sandbox including jQuery library and bringing its power outside the box.
The important thing to understand, is that used function scope will be completely extraneous to the one the function itself has been created ... in few words, that scope will be elsewhere!
Thanks to possibility to send arguments when this external function will be executed, and thanks to parent or top reference, it is possible to play between different scopes avoiding conflicts, creating bridges, loading JSONP, and more!
// JSONP example
new Elsewhere(
"http://mycoolservice.com?fn=parent.callback"
);
// that's it!
About JSONP, via Elsewhere a response could be more than just a callback. It could be an entire different environment with runtime created bridges. Example:
// output produced by a generic REST service
// myservice.server/name/WebReflection
// a function which name is not a problem
function getReferences(){
return userInfo.name + " has " + userInfo.posts + " posts";
};
// a global object which name is not a problem
var userInfo = {
name:"WebReflection",
site:"http://webreflection.blogspot.com/",
posts:12345
};
// a bridge for Elsewhere instance
// every Elsewhere sandbox has a reference to its isntance
// This reference is retrieved via global sandbox window object
// and the property @_Elsewhere
window["@_Elsewhere"].getUserProperty = function(name){
return userInfo[name];
};
// please note you cannot access that property directly
// window.@_Elsewhere will cause an error
window["@_Elsewhere"].getReferences = getReferences;
// ...
// our main page via Elsewhere
var myservice = new Elsewhere("myservice.server/name/WebReflection");
myservice.execute(function(fn){fn()}, [function(){
alert(myservice.getUserProperty("site")); // http://webreflection.blogspot.com/
alert(myservice.getReferences()); // WebReflection has 12345 posts
}]);
Understanding execute Method: How Elsewhere Works
How can be possible that a function defined inside a scope is totally ignorant about surrounding variables or scopes? It is really simple!
Elsewhere is based on Function.prototype.toString de-compilation, creating passed function directly as script inside the sandbox and returning, if status is ready, its value or undefined.
The important thing to understand is that if we create a global scope variable, this will be present next call, whenever we decide to perform it.
var sb = new Elsewhere();
// sb.ready is true, no external scripts loaded
alert(sb.execute(function(){
// generic function
function sum(a, b){
return a + b;
};
// define sum as global in this scope
window.sum = sum;
// test the function
return sum(1, 2);
})); // will be 3
// do other stuff and ...
sb.execute(function(){
return sum(3, 4);
}); // will be 7, sum was already defined
alert(typeof sum); // undefined
// sum exists only Elsewhere
About the ready status flag, it is instantly true if we do not load external script, while it could be true or false, depending on which browser we are using (in some browser an iframe document with external scripts is loaded sync while with Firefox 3.5 an iframe does not block main page content so it is not sync).
// more than a file ...
new Elsewhere([
// order is respected and reliable
"jquery.js",
"jquery.ui.js"
]).execute(function(onjQueryAndUILoaded, initialTime){
// execute is always performed
// after everything has been loaded
onjQueryAndUILoaded(jQuery, new Date - initialTime);
}, [
// we can optionally pass one or more arguments
function($, elaspedTime){
// let's understand how much it took
alert(elaspedTime);
},
new Date
]);
Understanding extend Method: Fast Bridge
Every time we execute a function inside an Elsewhere instance, we are de-compiling and injecting it inside another document. This operation is not that expensive but if we need to perform some task in that scope frequently, we could consider to extend an Elsewhere instance via its own method, creating a sort of fast bridge between two or more than two different worlds.
// empty sandbox
var sb = new Elsewhere()
// extend returns the instance itself
.extend(
{
extendObject:function(){
var toString = Object.prototype.toString;
// note, this Object is not the main one
// but the one from this sandbox (main not affected)
Object.prototype.type = function(){
var s = toString.call(this);
return s.substr(8, s.length - 9);
};
// avoid future modification
window.extendObject = function(){};
},
createObject:function(o){
// create an Elsewhere Object
return new Object(o);
},
// if an extend key is a global variable
// different from undefined
// this will simply be the variable from
// this sandbox
Object:1,
// if the value is not a function
// it will simply be saved as is
cool:true
}
);
// extend Elsewhere Object.prototype
sb.extendObject();
// test it
var o = sb.createObject("test");
alert(o.type()); // String
// o is not instanceof this main page Object
alert(o instanceof Object); // false
// but it could be used as if was
// an Object
alert(o instanceof sb.Object); // true
alert(sb.cool); // true
As Summary
Elsewhere can open hundreds of possibilities without bringing nothing new, except that browsers are starting to implement different threads for each sandbox (tab, or window) and I expect this library will bring an even more simple way to implement web workers. Right now main points are conflicts resolution, when libraries are able to deal from a sandbox, new JSONP interaction ways, multiple native constructor prototype definitions via one or more Elsewhere instances, just a simple way to load external scripts or ... well, whatever else our imagination could create with such tiny, cross-browser, library. Have fun with sandboxes 8).
The script? Via post title or here, in devpro.it.
No comments:
Post a Comment