-3

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?
Dave Newton
  • 158,873
  • 26
  • 254
  • 302
Magnogen
  • 5
  • 1
  • 5
  • 1
    Why do you want to use getters and setters on a variable, instead of just regular functions? Also, a getter that increments a variable is terrible design. – Guillaume Brunerie Feb 27 '23 at 20:57
  • you need an object to declare setter and getter. the locals scpre does not have an object, at least not accessable, if in background for using the same approach like for windows for global space. – Nina Scholz Feb 27 '23 at 20:58
  • @Guillaume Brunerie I want to be able to do something like this for a canvas library im working on: ```js Reflect.defineProperty(OBJ, 'width', { get() { return 0|canvas.width/density }, set(v) { canvas.width = 0|v*density }, enumerable: true }); ``` – Magnogen Feb 27 '23 at 20:58
  • @NinaScholz Oh thats a shame. Is there no other way to get around it? – Magnogen Feb 27 '23 at 21:01
  • 1
    I still don't see the point of having getters and setters on local variables, as you will never be able to access them from outside the function anyway. – Guillaume Brunerie Feb 27 '23 at 21:02
  • 1
    This is confusing. Library functionality encapsulation *(if that's what this is, I can't really tell)* belongs in the library. This seems like an XY problem but design-/arch-related--the examples (try to) explain an infeasible solution rather than the underlying problem. – Dave Newton Feb 27 '23 at 21:02
  • 1
    And you can just make functions `getWidth` and `setWidth` without any need for getters and setters, which is a much better API in my opinion. – Guillaume Brunerie Feb 27 '23 at 21:04
  • 2
    "*it's (more or less) the same as defining a property on the window object:*" no, because [let and const are not defined on the window object](https://stackoverflow.com/q/55030498). – VLAZ Feb 27 '23 at 21:04
  • 1
    @VLAZ exactly. I was raising my eyebrows throughout the entire comments section to finally find your valid comment as - the last one – Roko C. Buljan Feb 27 '23 at 21:37

1 Answers1

-1

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

Magnogen
  • 5
  • 1
  • 5