JavaScript TC39 implementing hashmark private class fields
github.com
github.com
I don't think the rationale for their inclusion is a good one. The inclusion of such keywords in languages like C++, Java, etc was well-meaning but a mistake in retrospect. In the presence of global variables and direct assignment these keywords are the PHB approach to information hiding. They require ceremony and diligence to achieve true encapsulation. It's a murky situation at best.
See Bob Martin https://www.youtube.com/watch?v=TMuno5RZNeE&t=2325 and Bertrand Meyer on Eiffel: http://se.inf.ethz.ch/old/teaching/ss2007/0050/slides/03_sof... (Bertrand Meyer coined the Open/Closed Principle and is the developer behind Eiffel).
At this point though I see their inclusion in JS being almost inevitable. Just another feature to ignore.
It'd be nicer if they would look to add syntax for higher-order operators like compose to the language instead of this.
Modern JITs are good at optimizing static objects (provided props are never added or deleted and the types never change). There is still (from what I remember) a small performance increase to normal objects, but it only becomes significant if you are creating hundreds or thousands of them.
I'd say that in typical cases though that closures should be faster because devs tend to use them in ways that are easier to optimize (Closures don't ever add/remove properties and types are much more likely to stay the same).
Never heard it explained this way. Can you elaborate a bit? I'm not sure I follow. Couldn't a closure have any of the same properties that other functions have? Call, apply, bind, length, etc, etc.
I'll first explain prototypical inheritance (because the two have close parallels). When you access a property in an object, a method runs behind the scenes. It does something like: search all the keys in the given object. If there is a matching key, return the value. If no such key exists, check for a __proto__ key. If it contains a value, call this function recursively on that object (and return whatever it gives back). If there is no __proto__ value, return `undefined`.
Let's assume an interpreter (to make it easy). Before we call a function, we need to setup the closure. The closure is an object with the names of all the variables you defined plus a few builtin things.
As we parse the function, any params, `var` or `function` statement creates an entries in the object. The values for the params are then pre-defined from the stuff provided by the caller. All the function statements are also pre-compiled. We add internal values for the return value, the parent closure (we'll call it __closure__), and some other things.
As the function runs, we come across a variable name. We then call a method to get it. That method searches the current scope for the name and returns the value. If none is found, it returns the result of calling itself on the __closure__ object. If no such object exists, it returns a `ReferenceError`.
Modern JITs do many fancy things with closures (just like they do with objects), but this basic mental model should cover most things.
Where there was coffeescript to highlight functional features in JS in a time where most JS was imperative, maybe we'll see a language that takes the OO sugar away to, again, reveal the functional language that's underneath.
0 - https://github.com/WebAssembly/gc/blob/master/proposals/gc/O...
https://github.com/SteveSanderson/Blazor
https://www.youtube.com/watch?v=MiLAE6HMr10
WebAssembly without native GC support is no different that targeting a real hardware CPU. One just has to implement it as well.
As soon as WebAssembly reaches a more mature state, expect the resurgence of plugins and this time around we won't be able to disable them.
Also if you bother to read the meeting minutes from WebAssembly meetings, developers from .NET team are present in such meetings.
Modern JavaScript is cool, but it loses many of the benefits of old school JS:
- only runs on newer devices
- requires a precompile step
- more language to learn
- confusing concurrency model
- confusing inheritance mechanisms
- no incentive to learn to use closures, callbacks, and prototypes properly
If I was willing to accept all of those things, I would just use a straight up better language, like Rust or Haskell, and get the additional benefits of type checking and deterministic performance.
But I'm not interested in those things. I want a simple run-everywhere language for beginners to get started on. JavaScript used to be that, but modern JavaScript is not that language anymore.
So, I'm going to bring back ES5. Why not. Who's with me?
But sometimes that's what you want. I think a good use case is view models... You're not mutating anything, but you have a large number of consumers that are using different subsets of views on a piece of data. Constructing those by hand can be a PITA.
Although, it's dangerous there. "Lots of views on a piece of data" might be a way of covering up "several disparate pieces of data in one place" or "data that is playing two roles when it should be transmuted instead". So, it's good to be afraid of new.
I am still frequently decomposing a single index of objects into separate indexes of literals fairly often. I often get sucked into thinking something is an object when it's really just a few separate pieces of data indexed together.
I also use new for libraries that export something that isn't exactly a function, but more a point of reference. For example my browser-bridge module is a point of reference for the computation between a web request and response. It's not really an action, but it's a thin representation over a handful of low level things you want to do in that space. It's purpose is to wrap up a set of concerns.
Essentially, OO is generally bad in JavaScript, but sometimes it's good and in those cases new is nice.
Imo, the language was not build or designed for some of these heavier OOP concepts. Just my opinion.
This seems to fly in the face of many of the great things ES6 implemented.
Classes also make a lot of sense for custom elements (Web Components.) See Polymer v1 compared to v2.
Not an expert just my opinion.
There's nothing hard about this:
function Person(name) {
this.name = name
}
Person.prototype.greet = function() {
return "Hi, "+this.name
}
var me = new Person("Erik")
console.log(me.greet())
The syntax hurts your brain if you're used to looking at something else, but the control flow is actually very simple and totally transparent, unlike ES6, which does magical things that can't be understood by thinking about where the thread is moving.It's an extra layer of indirection that you can't see in the debugger though. Not sure what to call that.
I think calling me dishonest is a little strange. How is poor word choice dishonest?
Whereas "prototype" has completely different English dictionary meaning, and thus requires in-depth redefinition.
The only instance of something dropped was cancelable observables. AFAIR it was only because some other group at Google opposed it.
Everything else is carried full steam ahead. Enjoy your import() instead of the vastly superior System.loader. Enjoy your hashes as member access specifiers. Dumpster fire. Template literals. BigInts. `new.target`. Dumpster fire. `import.meta`. Dumpster fire
1. Members marked private being accessible from other instances of the same class [1] is counter to every other OO language I have used. What is the prior art here? Why depart from all conventions like this?
2. The # syntax is not extensible. If TC39 decides to add protected, internal, etc. modifiers in the future, what will they look like? We should be looking to TypeScript's private/protected modifiers as a battle tested solution that fits the existing convention set by the "static" modifier.
---
[1] https://github.com/tc39/proposal-class-fields/issues/15#issu...
C++ works that way, and several other languages do as well. And the use case mentioned in that comment is exactly the reason: the implementation of a class knows how to access both its own private fields and those of another instance of itself, so that it can do comparisons, combinations, or other operations.
If you accept instances of your own class as arguments to a method, you may be templated to think you can access the private fields of those arguments, even if instanceof checks pass, but they may not be there.
1. Afaik every single OO language allows this. I wouldn't know of prior art for the opposite behavior (only allowing access to the same object).
2. TypeScript works under different constraints and - for all intends and purposes - doesn't implement private properties when it comes to the final, running program. The reasons why TypeScript isn't a battle test solution are laid out pretty explicitly in the Github thread.
Ruby and Scala disallow it, to name two (edit: to clarify, Scala supports both modes via private and private[this]). I stand corrected on Java, C#, and C++.
> The reasons why TypeScript isn't a battle test solution are laid out pretty explicitly in the Github thread.
I didn't see that, can you point me to it? Yes, in TS it's a compile-time-only check (as are all TS checks), but what does that have to do with reusing the same keyword for a runtime check?
Almost every statically typed OOP language works this way. In C++, Java, and C#, privacy is class-based, not instance-based.
Privacy is instance-based in Smalltalk and, as I understand it, has been a long-time source of frustration. It makes it very difficult to implement things like an equality method so that an object can compare itself to another of its own type without breaking its own encapsulation.
If you think about it from the perspective of software engineering (and not security, which is generally not what language-level privacy is for), instance-based privacy has no benefits over class-based privacy.
The goal of access control is to encapsulate regions of code from each other so that modification to one doesn't affect others. It establishes fences between different parts of the program to make them less coupled to each other and easier to independently change.
Every instance of the same class shares the exact same code, so there is no point in preventing access between them. It's not like you can encapsulate things such that a change to class A doesn't require a change to... class A. That's the same class that you're already touching.
> We should be looking to TypeScript's private/protected modifiers as a battle tested solution that fits the existing convention set by the "static" modifier.
I believe those rely on static analysis, which JavaScript does not have.
[2] This is addressed here: https://github.com/tc39/proposal-private-fields/blob/master/...
This has created an odd tension where the APIs that programmers use are implemented as OOP under the hood, yet JavaScript itself shied away from OOP concepts. With features like this, the JS standard is slowly creeping towards the style of programming that underlies the ecosystem.
From reading the thread it sounds like some feel that the addition of private members is being accomplished by fiat rather than community consensus.
There's a difference between "community consensus" and "consensus of the people posting comments on this github issue".
let ages = new WeakMap()
export class Person {
constructor(name, age) {
this.name = name
ages.set(this, age)
}
// ages.get(this)
}It can usually (always?) be gotten around though.
What I don't understand is why the TC39 is pushing for classes and classic OOP instead of other more pressing issues. You can accomplish OOP with prototypes.
IMO the lack of type checking is much more problematic. Flow and TypeScript are just adding a new layer of problems to an already convoluted workflow.
Classes shouldn't be a priority.
But thanks to WebAssembly they will be eventually there again.
The point was that the syntactic sugar isn't needed.
The 'class' sugar actually obscures the power of prototypal inheritance, and imho, discourages people from learning it - to their, and the language's, detriment.
Yes it is. The success of CoffeeSCript is absolutely a validation that developers are more productive with syntactic sugar such as the class keyword, destructuring and many many other features.
Now nobody forces you to use them. So why complain? It doesn't make Javascript harder to read for you, quite the contrary.
So it is your opinion that the community and users shouldn't give feedback on the design and direction of the language they use every day? That's an interesting approach to openness.