From a linguistic point of view, allowing code to use global variables in an uncontrolled manner is one of the most wrong choices. What is more frightening is that a variable "may" become global (at unknown time and place). But these two, along with the JavaScript language successfully came to the present. Dingzhou Technology Industry Bureau
It may be limited to the size of the browser application, so it's not a disaster. Prior to this, there were two solutions. One is that ECMA restricts this in the new specification (Edition 5), the most important of which is that the use of eval () is no longer arbitrary and excessive. The alternative, however, is relatively less bureaucratic and scholarly, although it also has the same academic name: sandbox.
Sandbox is not a new thing, even for JavaScript, it has been around for a long time. In the source code of SpiderMonkey JS, a closure is explicitly described as a sandbox. This contains a lot of potential information: it has an initial environment that can be reset, can be duplicated, and most importantly, all operations within it, without affecting the outside.
Of course it is far from true. The closure in JavaScript is just a "sandbox" thing--still out of JavaScript's early language specification, closures have to allow "legitimate leaks" to external things. And for all this unbearable front-end engineers, began to seek another solution, which relatively early attempt, is based on the practice of IFRAME. For example, the programme proposed by Dean.edwards in 2006:
View Source print?
1 |
a_frames.document.write( |
3 |
"var MSIE/*@cc_on [email protected]*/;" + // sniff |
4 |
"parent.sandbox=MSIE?this:{eval:function(s){return eval(s)}}" + |
Obviously, because the respective JavaScript engine instances are running in different IFRAME, the above scenario also means that the sandbox is the "engine" level: A crash in any sandbox will cause the engine and the corresponding IFRAME to crash. But--in theory--will not affect the entire browser.
The problem is that this is not so ideal. Often, the engine causes the entire browser to be locked there, for example by using alert () to pop up a dialog box and then lose focus because of some kind of accident. Or a single IFRAME can cause the global CPU to be consumed, such as a dead loop. So the more complicated scenario-the inclusion of a complete actuator in the JAVASCRITP-appeared. The most famous is narrative JavaScript, which built an executor that interprets the execution of JavaScript code on a row-by-line basis, which allows it to control all code execution sequences, or to reset the entire execution engine at any time-as a sandbox would do.
It's either too dependent on the environment or too complex, but there are followers. For example jsfiddle the project has achieved impressive results on the path of "embedding or loading". However, Yui in the new version has given its own choice: a more explicit programming conventions to implement the application-level sandbox. This includes a very simple, new YUI syntax:
View Source print?
1 |
YUI().use( ‘dom-base‘ , function (Y) { |
In the ' dom-base ' position, it can be 1 to n strings, indicating a list of modules that need to be loaded in the sandbox. This can be the initial list of sandboxes, or the list of modules that are required for subsequent callback functions (i.e., user code). In this implementation, Yui maintains the respective load module list and the variables, members in the context environment for each sandbox. But because of the JavaScript language's own limitations, the sandbox is still pretty fragile. For example, the code in the sandbox in the next example will contaminate the global:
View Source print?
1 |
YUI().use( ‘‘ , function (Y) { |
2 |
abc = 1234; //<--这里可能导致一个全局变量‘abc‘被隐式地声明 |
Similarly, you can use global variables like window, document, modify their members, or call methods without restrictions (for example, using settimeout () to create clocks) in the sandbox above. So Yui's sandbox is in fact bound by the "statute", rather than the real meaning of the sandbox. This, of course, also means that if the user is able to handle the code in the sandbox according to the protocol, then it will be free to enjoy the convenience it brings: security, porting, and effective isolation of side effects.
And we comprehend its foundation, the essence of Yui Sandbox is just a line:
View Source print?
2 |
// - mod.fn(this, name) |
3 |
mod.entryFunc(sandbox, modName); |
The actual meaning is:
- MoD: The module currently loaded in the sandbox;
- Entryfunc: The entry function of the above module;
- Sandbox: The current instance of the sandbox, that is, Yui () return value;
- ModName: module Name
In addition to dependencies (and possibly asynchronous loading), the Yui sandbox environment simply calls the callback function with the following code:
View Source print?
But the realization of these requirements is not so complicated. First, we set the data structure mod as an object:
View Source print?
1 |
{ name:modName, fn: entryFunc, req: [], use: [] } |
An environment object, env, will include multiple mods (processing them into objects rather than arrays, primarily for the purpose of using names to index modules), and how to manage them:
View Source print?
1 |
{ mods:{}, used:{}, add:..., use:...} |
Finally, the so-called sandbox sandbox is an instance of the above environment object, and at the initial time sandbox.mods and sandbox.used are empty. The simple implementation of this is:
View Source print?
02 |
* tiny sandbox framework |
03 |
* mirror from YUI3 by aimingoo. |
06 |
if (!( this instanceof arguments.callee)) return new arguments.callee(); |
07 |
this .mods = this .mods || {}; |
11 |
add: function (modName, entryFunc, reqArr, useArr) { |
12 |
this .mods[modName] = { fn: entryFunc, req: reqArr, use: useArr } |
15 |
var mods = [].slice.call(arguments, 0); // 0..length-2 is modNames |
16 |
var callback = mods.pop(); // length-1 is callback |
17 |
var recursive_load = function (name, mod) { |
18 |
if (! this .used[name] && (mod= this .mods[name])) { |
19 |
mod.req.forEach(recursive_load, this ); |
21 |
mod.use.forEach(recursive_load, this ); |
22 |
this .used[name] = true ; |
25 |
mods.forEach(recursive_load, this ); |
Now let's try a grammar style similar to Yui:
View Source print?
1 |
Sandbox().use( ‘‘ , function (){ |
Or, first register some modules with the entire sandbox (in a real-world framework implementation, this step may be initialized by a framework loader):
View Source print?
01 |
// for test, entry of mods |
02 |
f1 = function () { alert( ‘f1‘ ) }; |
03 |
f2 = function () { alert( ‘f2‘ ) }; |
04 |
f3 = function () { alert( ‘f3‘ ) }; |
05 |
// mods for global/common env. |
06 |
Sandbox.prototype.mods = { |
07 |
‘core‘ : { fn: f1, req: [], use: [] }, |
08 |
‘oo‘ : { fn: f2, req: [ ‘core‘ ], use: [ ‘xml‘ ] }, |
09 |
‘xml‘ : { fn: f3, req: [], use: [] } |
Then try running the code in a sandbox instance:
View Source print?
1 |
// f1 -> f2 -> f3 -> user code |
2 |
Sandbox().use( ‘oo‘ , function (){ |
In fact, even the logic used to deal with module dependencies in the code above is not a "magical" code or technique. In addition to these, the ability to isolate leaks in such a sandbox is not an embedded DSL language. The latter is a very simple technique to use, and there is no trick to be seen:
View Source print?
1 |
with (YUI()) this .eval( "... mod_context ... " ); |
In this way, the code in the Mod_context will only cause pollution in one instance of Yui (). Of course, it still stems from JavaScript limitations, and we still can't avoid a variable leaking to the global--unless we go back to the JS in JS Project and really reinitialize a JS engine in the environment.
In this sense, the engine-level sandbox is the same as the process of the operating system, resulting in an end-level solution, so chrome, IE and other mainstream browsers have "independent process" mode. In this context, attempts to use the "framework built-in sandbox" to improve some of the design errors in ECMAScript Ed3 are just a short cheque.
Even if the cheque is signed, it may not be accepted by anyone.
About the sandbox mode of JavaScript