DEV Community

Cover image for perf: private count vs #count
PuruVJ
PuruVJ

Posted on • Originally published at puruvj.dev

perf: private count vs #count

Which one is faster: private count or #count?

If you are a JavaScript developer, you might have heard about the concept of private and public properties in JavaScript. JavaScript only had public properties until 2019, until Chrome shipped the Private Class Fields feature.

class Person {
  #name: string;
  age: number;
}
Enter fullscreen mode Exit fullscreen mode

TypeScript has had public and private properties since forever:

class Person {
  private name: string;
  public age: number;
}
Enter fullscreen mode Exit fullscreen mode

As you can see, it's not as compact as JS version, but it works well. But what are the differences between the two? How do TypeScript's private properties work?

TypeScript lies

TypeScript lies to you. The private property is not actually private. It's invisible, not inaccessible. TypeScript will mark the property as private, but it can not prevent you from accessing it.

Input:

class Person {
  private name: string;
  public age: number;

  constructor() {
    this.name = 'Harry';
    this.age = 17;
  }
}
Enter fullscreen mode Exit fullscreen mode

JS Output:

'use strict';
class Person {
  constructor() {
    this.name = 'Harry';
    this.age = 17;
  }
}
Enter fullscreen mode Exit fullscreen mode

Declaration(d.ts) output:

declare class Person {
  private name;
  age: number;
  constructor();
}
Enter fullscreen mode Exit fullscreen mode

There is nothing stopping anyone from accessing person.name. Sure there will be red squiggles in the editor, but it can be bypassed.

An excerpt from Harry Potter to understand it better:

Snape’s eyes were fixed upon the door. His nostrils flared. He was walking slowly toward it, his wand held out in front of him like a weapon.

“Potter,” he breathed, his eyes narrowing, “I know you’re here. You can’t hide forever.”

Snape took another step forward, his free hand outstretched, fingers splayed, as though he could almost sense Harry’s presence through the cloak. Harry held his breath, trying to remain as silent and still as possible while Snape’s hand moved closer and closer, searching the air just inches from his face.

What is the actual performance difference?

We know TypeScript lies. So what? There is no performance difference. Right? Right???

TLDR: There is no performance difference for most any practical use case. Literally none. Go use JS private properties(#count) over private count in TypeScript, it's more robust.

However if you wanna hang around to see how I reached that conclusion and when there actually is a performance difference, then you can read on.

Setting up benchmark

We'll use an extremely simple benchmark to see the performance difference. Plain vanilla JS.

How are you going to emulate TypeScript private properties in JS?

Easy: TypeScript private properties are just JS public properties. In fact, I also lied to you. This post isn't a difference between private count vs #count in JS. It is a difference between public and private properties in JS. Plain and simple.

class Something {
  #a = 1;
  #b = 2;

  a = 4;
  b = 2;

  constructor() {
    const start_time = performance.now();
    for (let i = 0; i < 1_000_000; i++) {
      this.#a + this.#b;
    }
    console.log('Private:', performance.now() - start_time);

    {
      const start_time = performance.now();
      for (let i = 0; i < 1_000_000; i++) {
        this.a + this.b;
      }
      console.log('Public:', performance.now() - start_time);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Pretty simple, right? We're just creating a class with two public and two private properties, and then we're timing how long it takes to access them a million times.

Executing this is one line:

new Something();
Enter fullscreen mode Exit fullscreen mode

And this is it. I pasted this directly into Chrome Devtools console, and it gives me this:

Private: 3.4000000059604645
Public: 2.300000011920929
Enter fullscreen mode Exit fullscreen mode

So private property access is 1.47x slower than public property access. That's it. It may seem a lot but don't forget, we're accessing them a million times. If I run it for only 1000 times, then the output is this, most of the time:

Private: 0
Public: 0
Enter fullscreen mode Exit fullscreen mode

It's so fast there's no difference calling it a thousand times, and in most prod apps it will be called just a few times.

There's more: Caching

The above is the result on first run, means the class instance is created for the first time and Browser hasn't cached the class yet. But what if we run it a few more times and see how cache affects the result?

new Something(); // Repeat this line 10 times
new Something();
new Something();
new Something();
new Something();
new Something();
new Something();
new Something();
new Something();
new Something();
Enter fullscreen mode Exit fullscreen mode

And this is the result:

Private: 3.7000000029802322
Public: 0.6000000089406967

Private: 0.7000000029802322
Public: 0.699999988079071

Private: 0.800000011920929
Public: 0.5

Private: 0.8999999910593033
Public: 0.5

Private: 0.7999999970197678
Public: 0.5999999940395355

Private: 0.7000000029802322
Public: 0.5999999940395355

Private: 0.7000000029802322
Public: 0.6000000089406967

Private: 0.5999999940395355
Public: 0.6000000089406967

Private: 0.7000000029802322
Public: 0.5

Private: 0.7000000029802322
Public: 0.5
Enter fullscreen mode Exit fullscreen mode

The very first time, the browser cached public property access, making it almost 6.2x faster than (correspondingly) private property access. But the rest of the time, public properties are cached well enough such that there are multiple instances of 0.5.

Private properties are always a bit slower even when cached, in fact most of the time its 1.4-1.5x slower, in line with uncached benchmark above.

I bet this is because browser has to keep track of checks for private properties to deny any external access to them. It's like building a backend. A backend may be cached really really well in Redis, but every request requires checking authentication adds some overhead.

Conclusion

There is no performance difference for most any practical use case. Literally none. Go use JS private properties(#count) over private count in TypeScript, it's more robust.

Peace ☮️

Top comments (3)

Collapse
 
moopet profile image
Ben Sinclair

TypeScript has had public and private properties since forever [...] As you can see, it's not as compact as JS version

It's not as terse, but it's more readable. Someone who is unfamiliar with the language seeing #foo vs. private foo is going to have a lot easier time with the latter!

There's a trend with javascript (and therefore typescript) for making things as terse as possible at the expense of maintainability. Javascript - in this case, driven by Google - has made things objectively worse for everyone.

There is nothing stopping anyone from accessing person.name

This is true at runtime, but your typescript transpiler or IDE will throw an error, I think, making it effectively the same. As you say, you can bypass these checks, but it's something you explicitly have to do.

Collapse
 
puruvj profile image
PuruVJ

Someone who is unfamiliar with the language
No harm in learning the language. Sticking to standards pays off in the long run compared to short-term niceties(Typescript experimental decorators, remember them?)

Javascript - in this case, driven by Google - has made things objectively worse for everyone.
Agree to disagree

As you say, you can bypass these checks, but it's something you explicitly have to do.
Exactly what I said, it can be bypassed easily. Those who wanna exploit a library will do whatever they can, and using language's built-in security features is the golden route here

Collapse
 
alekseiberezkin profile image
Aleksei Berezkin

Wanna real magic? Swap “public“ and ”private” parts, and remove additional {} block. Voila! Now “private” is faster.