|wrap| suggestion (was |with|'s redesign attempt, but it seems nobody want to hear again about |with| in strict mode) in strict mode which is supposed to address at least 4 subjects mentionned below.
The concern here is more to address the subject of binding variables to properties than using properties of an object passed as an argument to |wrap| (then it is somewhere the contrary of what is doing a usual |with|). Then the suggestion here is to pass nothing to |wrap| and implicitely declare in the wrap statement the |this| binding
Issue (one of...) : |wrap| is not a reserved keyword
##Suggestion
"use strict"
var a='a';
wrap () {
//wrap (a) { --> syntax error
console.log(this); //undefined or outside |this| if hard binding
var window=new require('WINDOW')(); //load a WINDOW module
//can be loaded outside but then will be accessible to other windows
var b="b";
this=window; //disruptive assignment
//LHS lexical this
//|this| is now window within the |wrap| statement
console.log(window.window); //window - "var window" defined above gets bound to |this|, then to window, this defines (automatically) a window property for window
//this is not uninteresting, if not you should not forget to do manually window.window=window
console.log(window.b); //"b"
var c="c";
console.log(window.c); //"c"
console.log(c); //"c"
window.d="d";
console.log(d); //"d"
var code=module.load(web_api_code);
eval(code); //ThisBinding is window
//code : a='A' --> console.log(a); //A
//code : d='d' --> syntax error, code must follow strict mode, small limitation...
//code : var e='e' --> console.log(e or window.e) //e
//code : var c='z' --> console.log(c or window.c) //c - strict mode
//code : console.log(this) --> window
console.log(window.parseInt); //undefined
//global object properties could be copied see below
console.log(parseInt); //parseInt
//copy global property
var setTimeout_=setTimeout;
var setTimeout=(a,b)=>{var c=a.bind(this);setTimeout_(c,b)};
var f=function(){console.log(this)};
setTimeout(f,1000);//window
this=a; //syntax error, can not redefine |this|
}
##Rationale :
This is supposed to be related to at least four topics :
- SES securization : |wrap| is used to separate initial context from new window context, if something was to be passed to |wrap| then it would be impossible to ensure separation of both contexts. You could then spend your time wondering in what context things get executed depending on where you define it, example (see Annex below too) :
var window=new WINDOW();
wrap(window) {
var a='a';
window.document.onload=function() {console.log(a)};//"a"
};
window.document.onload=function() {console.log(a)};//undefined
Of course depending on what developers are doing, both contexts (and mainly what is defined within the |wrap| statement) can communicate, if not what is defined within the |wrap| statement is not accessible. Depending on what needs to be done other security features can be added (freeze global properties, etc)
-
modules loader : unless I am wrong, I did not see in modules proposals something that can load just the code of a web API (so not a module designed to work as such) for example and that can execute in a way that it can interact with the code from where it is requested, as far as I saw and as far as it exists today, modules contexts are always separated with a possibility to import things but not to interact directly. Anyway, loading js and executing it is something that everybody know how to do
-
multiple globals : global properties are not assigned to the new virtual global (window), but again do we need it ? I am thinking since some time about what is written in the multiple globals strawman : "the presence of multiple global objects interacting with one another, but this has long been the reality on the web". Web projects defining multiple globals, OK, but interacting between each others I could not find any example up to now, except maybe the small and limited interaction between window and things such as iframe's windows. Anyway, the above proposal might fit, if you want your multiple window to interact between each other then give access to what you want of windows objects.
-
VMs : this could simplify the concepts used in VMs, in particular node.js's VM (which has some defaults like not binding things correctly in some situations)
##Annex :
Note : examples given below are simplified to try to get a better understanding.
var window=new WINDOW();
window=Object.createContext(window); //sandbox object
eval(code,window); //creates a new global, clone sandbox properties to it, execute code, clone global properties to sandbox
As mentionned above, this is a complete headheck where you spend your time wondering where things get executed, example :
window.setTimeout=setTimeout;
code='setTimeout(function(){console.log(this)},1000)';
eval(code,window); //window is expected but it does echo the global of initial context
In addition, contexts can not really communicate :
window.document.onload=function() {
eval('var b=document.body',window);
var web_stuff=require('console.log(b)'); //undefined
}
This could be replaced by :
wrap() {
var window=new WINDOW();
this=window;
eval(code);
}
var import=Object.clone(global); //clone global var
var freevar=extract_var_assignment_from_code=['a','b','c']
var scopedObject={
a: {get:scopedGet, set:scopedSet},
b: {get:scopedGet, set:scopedSet},
c: {get:scopedGet, set:scopedSet}
}
Where scopedGet/Set gets/sets the (a or b or c) property of import
with(scopedObject) {
eval(code,import)
}
This approach is not trivial to understand, you have to extract the free var and then set multiple getters/setters, so probably there is an impact on performances
This could be replaced by :
//freeze globals
wrap() {
var import=new TameWindow();
//copy globals into import
this=import;
eval(code);
}
And another one can be based on (subset of shadow which is intented for other use, but the example is interesting) :
var compiled=Function.apply(null,['document','screen',code]); //properties of the global object
//function anonymous(document, screen) {code}
var glob={document:value_of_document,screen:value_of_screen}; //the new global
compiled.apply(glob,[glob.document,glob.screen]); //execute code with |this| bound to glob and scope knowing glob's var and their values
Not easy too, requires manipulations, if code='var a="a"', a does not get bound to glob, would require freevar manipulations as above
This could be replaced by :
code='wrap() {'+code+'}';
Thanks, I have updated the gist