3

I know this may sound a little absurd, but I'm looking for a way to define every variable within a function as a property of this. I'm looking for any hack, any way possible to be able to have some way to track variables within a function (i.e. add them to the this object) without having to actually preface every single variable definition with this.. Is there a way? Is this possible with Proxy?

function () {
  // declare a variable
  var hello = 'hi'
  return this
}

let {hello} = function()
console.log(hello) // hi

For example this works:

function hi () { this.hello = true; return this }
hi.bind({})() // { hello: true }

What I want is a way to have all variables defined within hi to be added to the this object when they are defined.

ThomasReggi
  • 55,053
  • 85
  • 237
  • 424
  • Isn't `this` a singular nature? A reference to the function owner? Any variable can reference `this`, but it references one thing only. It can be changed by `.call` or `.apply`. – zer00ne Dec 08 '16 at 22:27
  • 1
    You can't do that. – SLaks Dec 08 '16 at 22:27

2 Answers2

11

You are looking for the worst hack imaginable? Sure, everything is possible:

function example () {
  with(horrible(this)) {
    var hello = 'hi';
  }
}
var x = new example;
x.hello; // 'hi'


function horrible(target) {
  return new Proxy(target, {
    has() { return true; }, // contains all variables you could ever wish for!
    get(_, k) { return k in target ? target[k] : (1,eval)(k) }
  });
}

The proxy claims to contain all names that could ever be used as a variable in the with scope. This basically leads to all assignments of undeclared or var-declared variables create properties on the target (unless you use let or const, they'll be truly local to the block scope).
However, for variable lookup everything that is not a target property will be resolved in the global scope (using global eval) as the original scope cannot be retained when the proxy said it can deliver all variables.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Much cleaner that my answer! But references to outer variables won't work, e.g `console.log('foo')` – Oriol Dec 08 '16 at 22:55
  • @Oriol I was just fixing that :-) – Bergi Dec 08 '16 at 22:58
  • 1
    @Bergi any way to include vars declared with `let` and `const`? – ThomasReggi Dec 09 '16 at 00:02
  • 1
    @ThomasReggi No, but that's actually quite useful if you need some variables that should not become properties. If you really want complete control, get the function's code (via `.toString`) and do static analysis or transpilation. – Bergi Dec 09 '16 at 00:29
  • @Bergi check this http://astexplorer.net/#/fIw4JAexWk/2 credit => https://twitter.com/drewml – ThomasReggi Dec 09 '16 at 00:47
  • @ThomasReggi Yeah, something like that. Though you probably want to do those assignments at the end of the function. Or just replace all the references to any variables by property accessors. – Bergi Dec 09 '16 at 01:12
  • Since you use an indirect call to eval to resolve in the global scope, wouldn't `window[k]` also do the trick? – Oriol Jan 17 '17 at 18:56
  • @Oriol Not in non-browser environments or for `let` and `const`. – Bergi Jan 17 '17 at 19:14
  • will this feature be depreciated by any chance ignoring 'use strict'? – Lime Sep 08 '18 at 22:50
  • @William For web compatibility reasons the behaviour of `with` in sloppy mode will probably never be deprecated – Bergi Sep 09 '18 at 09:57
  • Hey @Bergi, is there any way to include `function` declarations as well? As in `function foo() {}` so that it also gets assigned to the proxy target. – LordTribual Apr 27 '21 at 07:20
  • And the same for IIFE's inside the eval'ed code? For example `(function() { var foo = 1; })();` so that `foo` also gets assigned to the proxy target. – LordTribual Apr 27 '21 at 07:27
  • @LordTribual No, `function` declarations don't work, probably because of [hoisting rules for block-level functions](https://stackoverflow.com/q/31419897/1048572) (and we cannot use strict mode). And no, it's certainly impossible to affect the private scope of IIFEs. – Bergi Apr 27 '21 at 10:08
3

You can, kind of. But it's a very very dirty hack which requires you to

  • Wrap the code inside a with statement, which has performance costs, is bad practice, and is not allowed in strict mode.
  • Use evil eval to get the values of the variables.
  • There can be false positives. Only var variables are detected, but not let or const ones.

function func() {
  // The proxy will detect the variables
  var vars = [];
  var proxy = new Proxy({}, {
    has(target, property) {
      vars.push(property);
    }
  });
  with(proxy) {
    // Place your code here
    var hello = 'hi';
  }
  // Assign the variables as properties
  for (var property of vars)
    this[property] = eval(property);
  return this;
}
let {hello} = func.call({});
console.log(hello) // hi
Oriol
  • 274,082
  • 63
  • 437
  • 513