"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?
"use strict"; Function("with({test:123}){ alert(test) }")(); // 123It Works!!! Awesome, we can use a
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?
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); }; }So, let's see compared with previous examples, right ?
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 });Does it work nested too ? Yes!
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 ^_^ });After that, removing the error at the end, the original object would shave the number
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:// 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); }; }With latest piece of code we can bring in that function whatever we need in this way:
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 });:) Thanks for reading!
1 comment:
http://www.reactionface.info/sites/default/files/images/YvEN9.png
Post a Comment