javascriptscopegetter-setterlocal-variablesdefineproperty

Where does a local variable get stored, and how can I define a getter/setter for one?


So in JavaScript, I know that when you define a variable in the global scope, like this,

let a = 0;
console.log(a); // > 0

it's (more or less) the same as defining a property on the window object:

window.a = 0;
console.log(a); // > 0, exactly the same

Is there a way to do this "window method" within the local scope a function?


The reason I ask is because you can define a getter/setter on a window quite easily (which I find very helpful, especially when I want to make personal libraries and such), and be able to use the variables it defines just like a regular variable:

let value = 0;
Reflect.defineProperty(window, 'counter', {
  get() { return value++ },
  set(v) { value = v }
});
console.log(counter); // > 0
console.log(counter); // > 1
console.log(counter); // > 2
counter = 100;
console.log(counter); // > 100
console.log(counter); // > 101
// etc

... but as soon as you put it into a function, it works, but the variable becomes defined on the window object, making it leak out of the scope of the function.

Where do variables declared inside the local scope of a function get stored? How can I define one of them with a getter/setter?


I've Tried

In this example, the variable a doesn't leak out as it isn't defined on the window object.

(function () {
  let a = 42;
  console.log(a); // > 42
  console.log(window.a); // > undefined
  console.log(this.a); // > undefined, this === window in this case
})();

... but it's not defined on this either, as this is the same as window. So then I tried binding it to a custom object:

(function () {
  let a = 42;
  console.log(window.a); // > undefined, not here...
  console.log(this.a); // > undefined,
                       // even though `this` isnt the window anymore,
                       // and it is an object that can be changed.
  console.log(this.b); // > 69, see?
}).bind({ b: 69 })();

(note: using new (function () { ... })() does the same as this bind example)


tl;dr

(function () {
  let value = 0;
  Reflect.defineProperty(this, 'counter', {
    get() { return value++ },
    set(v) { value = v }
  });
  counter; counter; // increment twice
  console.log(counter); // > 2
  counter = 100;
  counter; counter; counter; // increment thrice
  console.log(counter); // > 103
  // etc
})();
console.log(counter); // > 104
// ^ this should throw an error saying that counter
// isn't defined, but it is - on the window. I only
// want it defined within the function scope.
  1. Is there a way to define a variable only within the local scope a function?
  2. Where do variables declared inside this local scope get stored? How can I define one of them with a getter/setter?

Solution

  • I've figured it out, no thanks to the StackOverflow community.

    For anyone interested, I'd recommend using with. (shocking, I know.)

    // make a useful function
    const install = (canvas) => ({
      get width()  { return 0|canvas.width },
      set width(v) { canvas.width = 0|v },
      get height()  { return 0|canvas.height },
      set height(v) { canvas.height = 0|v },
      ctx: canvas.getContext('2d')
    });
    // use it in scope
    with (install(document.getElementById('my_canvas'))) {
      width = height = 256;
      ctx.fillStyle = 'black';
      ctx.fillRect(0, 0, width, height);
    }
    console.log(width); // error: width is not defined
    

    with docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with