# Proposal: Function static variables and blocks

**URL:** <https://es.discourse.group/t/proposal-function-static-variables-and-blocks/1781>\
**Category:** 💡 Ideas\
**Tags:** proposal\
**Created:** [August 21, 2023, 11:48pm UTC](https://es.discourse.group/t/proposal-function-static-variables-and-blocks/1781 "2023-08-21T23:48:38Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![yehoryatskevych](https://avatars.discourse-cdn.com/v4/letter/y/e68b1a/32.png) [@yehoryatskevych](https://es.discourse.group/u/yehoryatskevych)\
**Post date:** [August 21, 2023, 11:48pm UTC](https://es.discourse.group/t/proposal-function-static-variables-and-blocks/1781/1 "2023-08-21T23:48:38Z")

</div>

A proposal to add a `static` keyword for variable declaration in functions.  
Proposal repository: [yehoryatskevych/proposal-function-static-variables (github.com)](https://github.com/yehoryatskevych/proposal-function-static-variables)

## The problem

Allocation in JavaScript can hardly be called "fast". JavaScript is a great language, and its features significantly reduce development time, making it the best language for prototyping and fast development. However, it is still an interpreted language, which takes a lot of time for native calls and allocations. Therefore, it would be great to have more options for manual code optimization.

Let's consider an example with a function that just creates a 3D vector using some extra parameter:

```javascript
function task(i) {
    new Vector3(i + 1.13, i + 5.231, i + 7.1247);
}

```

Every time this function is called, JavaScript allocates a new object for the `Vector3` instance. We can easily measure the execution time of this function by calling it `10^8` times:

```javascript
const ITERATIONS_NUM = Math.pow(10, 8);

console.time('task');
for (let i = 0; i < ITERATIONS_NUM; ++i) {
    task(i);
}
console.timeEnd('task');

```

The result:

```Bash
task: 920.739ms

```

I don't think that's a good result. So let's test again, but move the vector allocation to the global scope:

```javascript
let vec = new Vector3(0, 0, 0);

function task(i) {
    vec.set(i + 1.13, i + 5.231, i + 7.1247);
}

const ITERATIONS_NUM = Math.pow(10, 8);

console.time('task');
for (let i = 0; i < ITERATIONS_NUM; ++i) {
    task(i);
}
console.timeEnd('task');

```

The result:

```bash
task: 78.667ms

```

Looks much better, even though we are setting the value every call. But is it reasonable to put all variables into the global context? I don't think so, especially if your goal is to write high-performance code with many functions that need to be optimized. So you can use closure with an Immediately Invoked Function Expression (IIFE), like this:

```javascript
const task = (() => {
    let vec = new Vector(0, 0, 0);
    return function task(i) {
        vec.set(i + 1.13, i + 5.231, i + 7.1247);
    }
})();

```

However, we are immediately confronted with the fact that we can't use function declarations, and hoisting no longer works for us. So we have to declare these functions before we use them, and we already get restrictions that slow down development speed and interfere with the desired structuring of the code, especially when there are a lot of such functions. Using such optimizations in methods becomes almost impossible.

## The solution

One solution to this problem could be local static variables, as implemented in some other C-like low-level languages. These variables would allow us to optimize memory allocation and function execution time by allocating and initializing the variables only once after the first function call. Subsequent function calls would use the already allocated variable with the last value. It's similar to global variables that can only be accessed from the specific function in which they are declared.

Here is an example of how it could be used:

```javascript
function task(i) {
    static let vec = new Vector(0, 0, 0);
    vec.set(i + 1.13, i + 5.231, i + 7.1247);
}

```

and a more complex example of how it should work:

```javascript
function func() {
    static let isFirstCall = true;
    static let counter = 0;

    if (isFirstCall) {
        isFirstCall = false;
        console.log('Static variables initialized!');
    }

    console.log("Counter:", counter);

    counter++;
}

func(); // OUT: "Static variables initialized!", "Counter: 0"
func(); // OUT: "Counter: 1"
func(); // OUT: "Counter: 2"

```

This code proposal introduces a highly useful feature for functions with high execution rates and constants that require complex operations for initialization. It allows functions to store their own state for subsequent executions and remain independent of the global context. With this feature, functions can efficiently handle their own state persistence and eliminate the need for global variables or external dependencies.

To make it more consistent with the current static implementation and more flexible it also can be added with the support of [class-static-block](https://github.com/tc39/proposal-class-static-block) proposal.

```javascript
function func() {
    static let counter = 0;

    static {
        console.log('Static variables initialized!');
    };

    console.log("Counter:", counter);

    counter++;
}

func(); // OUT: "Static variables initialized!", "Counter: 0"
func(); // OUT: "Counter: 1"
func(); // OUT: "Counter: 2"

```

## Additional benefits

In addition to helping with optimization, it can also be useful to store some function states independently from global scope for example for util/helper functions that need to have some state and pre-allocation, refactoring is also made easier due to the elimination of the need to move variables and "helpers" for the "helper".

```javascript
function funtionWithComplexPreallocation(value) {
    static const magic = 6816;
    static const magicOffset = 285615417;
    static const magicRatio = complexOperation(magic);
    return magicOffset + (value * magicRatio);   
}

```

or

```javascript
function funtionWithComplexPreallocation(value) {
    static const magic = 6816;
    static const magicOffset = 285615417;
    static let magicRatio;

    static {
        for (let i = 0; i < 1024; ++i) {
            magicRatio = Math.sqrt(magicRatio) * magic;
        }
    }

    return magicOffset + (value * magicRatio);   
}

```

---

<div class="post-metadata">

**Author:** ![senocular](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/senocular/32/642_2.png) [@senocular](https://es.discourse.group/u/senocular)\
**Post date:** [August 22, 2023, 1:35am UTC](https://es.discourse.group/t/proposal-function-static-variables-and-blocks/1781/2 "2023-08-22T01:35:50Z")

</div>

Previous discussion: [C-style static variables](https://es.discourse.group/t/c-style-static-variables/511)

---

<div class="post-metadata">

**Author:** ![Clemdz](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/clemdz/32/813_2.png) [@Clemdz](https://es.discourse.group/u/Clemdz)\
**Post date:** [August 22, 2023, 3:58am UTC](https://es.discourse.group/t/proposal-function-static-variables-and-blocks/1781/3 "2023-08-22T03:58:21Z")

</div>

Note that you could also write:

```javascript
function task (i) {
  task.vec ??= new Vector(0, 0, 0);
  task.vec.set(i + 1.13, i + 5.231, i + 7.1247);
}

```

which should probably be almost as fast as your IIFE example

---

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [August 22, 2023, 5:25am UTC](https://es.discourse.group/t/proposal-function-static-variables-and-blocks/1781/4 "2023-08-22T05:25:47Z")

</div>

I've sometimes found myself slightly wanting a feature like this, but mostly for use-cases like "I'm making an id-retrieving function that keeps an internal state of the last ID used in order to decide the next one", or that sort of thing.

Though, for me, [do expressions](https://github.com/tc39/proposal-do-expressions) (if they ever come) would be a good enough solution. Not sure that it would satisfy you though, since you're saying that you'd still like function hosting and what-not, which do expressions can't provide, and they're technically not much better than IIFEs, just less clunky.

---

<div class="post-metadata">

**Author:** ![bergus](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bergus/32/152_2.png) [@bergus](https://es.discourse.group/u/bergus)\
**Post date:** [August 22, 2023, 12:45pm UTC](https://es.discourse.group/t/proposal-function-static-variables-and-blocks/1781/5 "2023-08-22T12:45:26Z")

</div>

> [@yehoryatskevych](#):
>
> But is it reasonable to put all variables into the global context? I don't think so, especially if your goal is to write high-performance code with many functions that need to be optimized.

No, it is not reasonable to put those variables in the global scope. However, you really should use modules, and it is totally reasonable to declare such variables in the module scope. Just don't `export` them.

> [@yehoryatskevych](#):
>
> So you can use closure with an Immediately Invoked Function Expression (IIFE)  
> […]  
> However, we are immediately confronted with the fact that we can't use function declarations, and hoisting no longer works for us. So we have to declare these functions before we use them, and we already get restrictions that slow down development speed and interfere with the desired structuring of the code, especially when there are a lot of such functions.

`const` declarations are still hoisted, though yes, you need to initialise them before you use them. This is usually not a problem in well-structured code that only calls functions from the script entrypoint, not during module initialisation.  
And really, I would not _want_ to rely on hoisting for initialising `static` variables, having the order of evaluation of `static` blocks depend on the order of calls to the functions seems like a nightmare to me.

---

<div class="post-metadata">

**Author:** ![gentlee](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/gentlee/32/1869_2.png) [@gentlee](https://es.discourse.group/u/gentlee)\
**Post date:** [September 29, 2023, 11:54am UTC](https://es.discourse.group/t/proposal-function-static-variables-and-blocks/1781/6 "2023-09-29T11:54:38Z")

</div>

Any thoughts how we can combine two approaches with my [Lazy inlined constants](https://es.discourse.group/t/lazy-inlined-constants/1806) ?

---

<div class="post-metadata">

**Author:** ![shrinktofit](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/shrinktofit/32/1773_2.png) [@shrinktofit](https://es.discourse.group/u/shrinktofit)\
**Post date:** [May 24, 2024, 3:00pm UTC](https://es.discourse.group/t/proposal-function-static-variables-and-blocks/1781/7 "2024-05-24T15:00:44Z")

</div>

Also propose this.

Let's compare various solutions.

## Simplest

```js
// Free function case
let fn_calls = 0;
const fn = () {
  if (fn_calls++) { return; }
};

// Class Method case
let A_fn_calls = 0;
class A {
  fn() {
      if (A_fn_calls++) { return; }
  }
}

```

**Cons**

- Parent scope is polluted.
- If you want do it better, you need to name it a long name to indicates "this variable is dedicated to fn"!

## Function as namespace

```js
// Free function case
const fn = () {
  if (fn.calls++) { return; }
}
fn.calls = 0;

// Class method case
class A {
  fn() {
      if (A.prototype.fn.calls++) { return; }
  }
}
A.prototype.fn.calls = 0;

```

**Cons**

- Not TypeScript friendly.
- Not tree shaking friendly.
- Not JS engine friendly.
- You don't want to write the `A.prototype.fn`, do you?

## IIFE

```js
// Free function case
const fn = (() => {
  let calls = 0;
  return () => {
    if (fn.calls++) { return; }
  };
})();

// Class method case
class A {
   // Cons:
   // - enumerable: true
   // - (new A().fn !== new A().fn)
   // - No longer able to use "class method decorator".
   fn = (() => {
     let calls = 0;
     return () => {
        // .....
     };
   })();
}

```

**Cons**

- Verbose.
- Can not be applied to class method, given you don't want it be a "dummy function property".

## Best(This Proposal)

```js
// Free function case
const fn = () => {
  static let calls = 0;
  if (fn.calls++) { return; }
};

// Class method case
class A {
  fn() {
      static let calls = 0;
      if (fn.calls++) { return; }
  }

  // You can even ...
  get surprise() {
    // readonly, expensive, cache
    static let cache = {};
    cache.x = this.x;
    return cache;
  }
}

```

**Cons**

- Easy to read.
- No pollution.
- TypeScript friendly.
- Tree shaking friendly.
- JS engine friendly.
- Easy to transform.
- No compatibility problem.
- Support all form of "function-like" language constructions.
- You can type less words.

**Pros**

- No

---

<div class="post-metadata">

**Author:** ![simonkcleung](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/simonkcleung/32/785_2.png) [@simonkcleung](https://es.discourse.group/u/simonkcleung)\
**Post date:** [May 26, 2024, 5:18am UTC](https://es.discourse.group/t/proposal-function-static-variables-and-blocks/1781/8 "2024-05-26T05:18:41Z")

</div>

Some questions:

1. What is the difference between static let and static var? Seems let and var here are meaningless.
2. What happen if we use `static let name = "abc" `?

And this proposal is different from IIFE because static variables are not fully enclosed.

---

<div class="post-metadata">

**Author:** ![shrinktofit](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/shrinktofit/32/1773_2.png) [@shrinktofit](https://es.discourse.group/u/shrinktofit)\
**Post date:** [May 26, 2024, 8:58am UTC](https://es.discourse.group/t/proposal-function-static-variables-and-blocks/1781/9 "2024-05-26T08:58:57Z")

</div>

> 1. What happen if we use `static let name = "abc" `?

Static function variable is a separate variable:

- declare at the point the enclosing function/method/accessor declares

- can only be accessed within the function

- finishe its initialization before first access.

```js
{ // containing scope
  /// ...
  function fn(p) {
    static let name = "abc" + p; // Referencing function-local variable? strange but allowed
    static let name_2 = "abc";
  }
  // ...
}

```

should be desugar to:

```js
{ // containing scope
  // ...
  var $name;
  var $name_initialized = false;

  var $name2 = "abc";

  function fn(p) {
    if (!$name_initialized) {
      $name_initialized = true;
      $name = "abc" + p;
    }
    // `name_2`'s initialization has been hoisted
  }
  // ...
}

```

---

<div class="post-metadata">

**Author:** ![simonkcleung](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/simonkcleung/32/785_2.png) [@simonkcleung](https://es.discourse.group/u/simonkcleung)\
**Post date:** [May 28, 2024, 3:11pm UTC](https://es.discourse.group/t/proposal-function-static-variables-and-blocks/1781/10 "2024-05-28T15:11:13Z")

</div>

I would think as OP said it should be consistent with the current static implementation.  
Please see the example at MDN's [Static\_initialization\_blocks](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Static_initialization_blocks)

Neither `let` nor `var` is used. And in fact, if you use `static let...` in Class, it is an error.

`static name = "abc" + p;` won't works in Class, so I don't think it should work in Function.

Also, the static variables should also be properties of the function.

My concern of a "name" static variable because it could be misleading for debugging tools.  
Try to run the following in node.js

```javascript
> class A {
static name="B"}
> A

```

it returns `[class B] { name: 'B' }` rather than `[class A] { name: 'B' }`
