Resurrecting The With Statement
You might think this must be a joke, well no, this is a partial lie behind the
Well, now we have the possibility to return it again when it's needed for security reasons or to be sure is the right one.
How about bringing back something that would throw an error otherwise in a strict context as
However, there were few things impossible to represent without that statement, and few of them have been proposed as the monocle mustache behavior.
How? Reactivating the
In few words, we can have a secured
Use cases might be tests related, DOM related, since there things are slow in any case, or quick API prototyping due implicit
"use strict";
directive.Roots Of The Hack
// global
"use strict";
function strict() {
"use strict"; // top of the function
return {
// invoked inline
withStrict: function(){
return this; // undefined
}(),
// invoked inline too
withoutStrict: Function("return this")()
};
}
// the test
var really = strict();
really.withStrict; // undefined
really.withoutStrict; // global, BOOOOM!
The Good News
I have been blaming since ever the fact that use strict makes impossible to retrieve the real global object ensuring nobody in the closure redefinedwindow
or global
by accident so that code is more reliable.Well, now we have the possibility to return it again when it's needed for security reasons or to be sure is the right one.
// a classic code for Rhino, node, and Web
var G = typeof window !== "undefined" ? window : global;
// then we need to use G
// with this hack
var global = Function("return this")();
// that's it, is the window or the global object
The Funny News: With Statement Is Back
So, we are able to deactivate the"use strict"
directive in the global scope, right?How about bringing back something that would throw an error otherwise in a strict context as
with(){}
is? It Works!!! Awesome, we can use a
"use strict";
Function("with({test:123}){ alert(test) }")();
// 123
with
statement always be executed through Function
which, differently from eval
, evaluates in the global scope. With Great Power Come Great Shenanigans
The reason number one for abandoning thewith(){}
statement is its ambiguity, together with the ability to pollute by mistake the global scope.However, there were few things impossible to represent without that statement, and few of them have been proposed as the monocle mustache behavior.
array.{
pop()
pop()
pop()
};
path.{
moveTo(10, 10)
stroke("red")
fill("blue")
ellipse(50, 50)
};
this.{
foo = 17
bar = "hello"
baz = true
};
A Mustache Like With statement
Latter snippet is not able to pollute the global context, neither it changes context, plus it can interact with the outer scope. OK, it is not possible to implement automagically the latter one, but we can still avoid context and global context pollution ensuring a properthis
value, and throwing errors if some variable does not belong to the mustached object.How? Reactivating the
"use strict";
directive again inside the non strict code: how crazy is that? So, let's see compared with previous examples, right ?
function With(o) {
// needs a block, a function
// can simulate that properly
return function (f) {
// deactivate during evaluation the strict directive
return Function(
// it is possible to use the with statement now
"with(this){return(" + ("" + f).replace(
// but we want to reactivate strict env inside
"{", "{'use strict';"
// avoid global context pollution
// forcing a different this
) + ").call(this)}"
).call(o);
};
}
Does it work nested too ? Yes!
With(array)(function(){
pop()
pop()
pop()
});
With(path)(function(){
moveTo(10, 10)
stroke("red")
fill("blue")
ellipse(50, 50)
});
With(this)(function(){
foo = 17
bar = "hello"
baz = true
});
After that, removing the error at the end, the original object would shave the number
With({test:{key:"value"}})(function(){
alert(test); // [object Object]
With(test)(function(){
alert(key); // "value"
});
// change the property
test = 456;
// by accident pollute the global scope
not_defined = "oops?"; // throws an error ^_^
});
456
as test
property.In few words, we can have a secured
with(){}
statement behavior without the possibility to hurt the generic surrounding scope anyhow, except for those death browser without the strict directive, of course :D Performance, Use Cases, etc
Yes, I believe the performance problem we know about that statement is still there, but with less problems to take care due strict behavior and a global environment. I would actually say that performance could be optimized with this technique, because no scope and context are implicit or modifiable anyhow, but I am not the right person to tell you what the hell happens in that case inside a JS engine :DUse cases might be tests related, DOM related, since there things are slow in any case, or quick API prototyping due implicit
return this
nature of the hack: you decide :-) Last Improvement
If you would like to adopt the technique but you want to be able to bring other local variables in that mustached block, you can use this version of the same function:With latest piece of code we can bring in that function whatever we need in this way:
// The Strictly Monocle With Statement
function With(o,a) {
return function(f) {
return Function(
"with(this){return(" + ("" + f).replace(
"{", "{'use strict';"
) + ").apply(this,arguments)}"
).apply(o,a);
};
}
:) Thanks for reading!
With(
document.body, // the implicit context
[ // arguments to pass
jQuery, // jQuery
window._ // lo-dash
]
)(function($, _){
// le the magic happens
});
// or simply
With({},[1, 2])(function(a, b){
alert([a, b]); // 1,2
});
Comments
Post a Comment