Live data from Hacker News

Classes are Expressions

raganwald.com

31–40 of 46 posts

Re: Classes are Expressions

#31

Earlier quoted context omitted.

Closures give you object privacy. Symbols give you class (or whatever other scope you want) privacy. Take a look at this symbol example: let Person = (() = > { let firstNameProperty = Symbol('firstName'), lastNameProperty = Symbol('lastName'); return class Person { constructor (first, last) { this[firstNameProperty] = first; this[lastNameProperty] = last; } sameFirstName (otherPerson) { return this[firstNameProperty]…

sure :P (EDIT: Just kidding; the parent's point totally flew over my head). I've actually created classes this way in the past when I worked with a team of enterprise Java developers lol. You could also make magic getter/setter methods if you really wanted to get crazy with it. var Person = (function() { return function(firstName, lastName) { var privateVars = { firstName: "", lastName: "" }; this.getFirstName = func…

This isn't quite the same; the parent is pointing out that you can implement `sameFirstName` without exposing `firstName` publicly at all, not even as a getter.

Re: Classes are Expressions

#32
post #31

Earlier quoted context omitted.

sure :P (EDIT: Just kidding; the parent's point totally flew over my head). I've actually created classes this way in the past when I worked with a team of enterprise Java developers lol. You could also make magic getter/setter methods if you really wanted to get crazy with it. var Person = (function() { return function(firstName, lastName) { var privateVars = { firstName: "", lastName: "" }; this.getFirstName = func…

This isn't quite the same; the parent is pointing out that you can implement `sameFirstName` without exposing `firstName` publicly at all , not even as a getter.

Oh wow, I totally missed that. Thanks for clarifying!

Re: Classes are Expressions

#33
post #31

Earlier quoted context omitted.

This isn't quite the same; the parent is pointing out that you can implement `sameFirstName` without exposing `firstName` publicly at all , not even as a getter.

Oh wow, I totally missed that. Thanks for clarifying!

Np! Mind it is technically possible to do, check out my sibling comment if you haven't already.

Re: Classes are Expressions

#34
post #30

Earlier quoted context omitted.

Closures give you object privacy. Symbols give you class (or whatever other scope you want) privacy. Take a look at this symbol example: let Person = (() = > { let firstNameProperty = Symbol('firstName'), lastNameProperty = Symbol('lastName'); return class Person { constructor (first, last) { this[firstNameProperty] = first; this[lastNameProperty] = last; } sameFirstName (otherPerson) { return this[firstNameProperty]…

Er, to be clear, your example uses a closure. Your real question is, can this be done without `Symbol`? And it can, since the symbol is just a secret stored in a closure and there are other sorts of secrets. Aside from the "Math.random() + {enumerable: false}" suggestion found elsewhere, you can reproduce the precise[0] semantics of your example using something like: // Please do not actually write code like this var…

> [0]: Assuming that `otherPerson` in your constructor is a typo, of course ;P

Oops, yup. Fixed.

Your solution is pretty clever, though it leaks memory like a sieve. To get around that, you end up needing something like weak maps.

Once you have those, when you do the pattern you suggest, you are basically doing metaprogramming and reinventing the very idea of an "object" yourself from scratch.

That can be fun to do, but to me it starts to feel less like expressing "the same thing" and more like implementing a new language that lets you express the same thing. Just because I can implement a GC in C, that doesn't mean C has a GC. :)

Re: Classes are Expressions

#35
post #30

Earlier quoted context omitted.

Er, to be clear, your example uses a closure. Your real question is, can this be done without `Symbol`? And it can, since the symbol is just a secret stored in a closure and there are other sorts of secrets. Aside from the "Math.random() + {enumerable: false}" suggestion found elsewhere, you can reproduce the precise[0] semantics of your example using something like: // Please do not actually write code like this var…

> [0]: Assuming that `otherPerson` in your constructor is a typo, of course ;P Oops, yup. Fixed. Your solution is pretty clever, though it leaks memory like a sieve. To get around that, you end up needing something like weak maps. Once you have those, when you do the pattern you suggest, you are basically doing metaprogramming and reinventing the very idea of an "object" yourself from scratch. That can be fun to do,…

In ES3 you'd need destructors, I think. But yes, agreed on all points :)

And yet-- is the Symbol approach not metaprogramming? It's clearly a much saner way to shoehorn this feature in, but it's still a shoehorn. And the underlying magic in both cases is in fact simple closure scope, which is really what I wanted to demonstrate.

Edit: And I guess my broader point, to make this more constructive, is that in JS these kinds of idioms live on a continuum of metaprogramming, and for the sanity of your co-contributors you usually want to be doing as little of that as necessary. Like others, I'm wary of presenting something like your example as a "design pattern" without that context.

Edit 2: To illustrate what I mean, I swear to you that if this pattern is adopted widely you will find code like this in the wild:

    ... = > {
    let accountNumberKey = Symbol('accountNumber');
    return class Account {
      constructor (accountNumber) {
          this[accountNumberKey] = accountNumber;
          this.accountNumberKey = accountNumberKey; // ??? but transaction log works now

    ...

Re: Classes are Expressions

#36

Just looks like more JavaScript antipatterns, this time with some ES6 flavour. We know how to "solve" the problem of encapsulation with JavaScript. It's not with closures, Symbols, or some other arbitrary hack. We do it by enforcing specific idioms that only require a developer to recognize intent instead of learning 80 different ways to do the same thing. You simply put a single or double underscore in front of the…

You can't check that the convention is being broken which makes refactoring more difficult. There will always be a temptation for some developers in some situations to mess with the internal state of objects.

In general, js is more about good practices and craftsmanship, and less about finding ways for the language to force people to do things.

Re: Classes are Expressions

#37

I guess I need to be schooled by why not just use closures? var Person = function(first last) { this.fullName = function() { return first + " " + last; }; this.rename = function(newFirst, newLast) { first = newFirst; last = newLast; }; }; Problem solved, you can't access `first` and `last` outside the class. You might retort "they're bigger" or something but just like everything else in JS they just need the right pe…

> I don't get the relucatance to use the language's features rather than hacks Symbol is a language feature. As is accessing a property with `[` and `]`. Using the language's features as they were designed is not a hack. And I think we may see your browser's tooling improve when it natively supports ECMAScript 2015, instead of whatever is transpiled today. Now: Why do you think that properties named by symbols have t…

I guess I don't see obfusticating my own code as a good pattern. I gave the debugging example. Similarly serialization? With the closure solution I just do

    var Person = function(first, last) {
      var serializable = {
        first: first,
        last: last,
      };
      ...
With Symbol? I guess I just don't see making things more complicated as a positive pattern.

Re: Classes are Expressions

#38

Earlier quoted context omitted.

> I don't get the relucatance to use the language's features rather than hacks Symbol is a language feature. As is accessing a property with `[` and `]`. Using the language's features as they were designed is not a hack. And I think we may see your browser's tooling improve when it natively supports ECMAScript 2015, instead of whatever is transpiled today. Now: Why do you think that properties named by symbols have t…

I guess I don't see obfusticating my own code as a good pattern. I gave the debugging example. Similarly serialization? With the closure solution I just do var Person = function(first, last) { var serializable = { first: first, last: last, }; ... With Symbol? I guess I just don't see making things more complicated as a positive pattern.

If you consider the use of symbols as “obfuscating your own code," use closures with my blessing.

What, exactly, is the problem here? I don’t recall the article saying that symbols are the only way to do this, or the best way to do this out of all the options. I recall people saying similar things about closures for a while, until it caught on that closures and lexical scope are a natural part of JavaScript.

TFA’s point is that ECMAScript 2015’s choice to keep classes as first-class functions with prototypes that are objects is what allows us to use all of the techniques we already have for first-class values, including returning them from functions.

As I keep saying, using a closure (if that’s what you want to do) demonstrates the exact same thing, it works because a method is just a function.

Now that we have established that I am not claiming that symbols are better than closures, I will warn you about an edge case: When you use compact method syntax, you do get something that an ordinary function does not provide, the `super` keyword.

I do not know how to make that work in the case where you assign a function to an instance of an object. So if you want to override a function while calling the super-class’s implementation, it might be tricky to use the closure solution.

That being said... I wouldn’t stay up late worrying about it.

Re: Classes are Expressions

#39

Just looks like more JavaScript antipatterns, this time with some ES6 flavour. We know how to "solve" the problem of encapsulation with JavaScript. It's not with closures, Symbols, or some other arbitrary hack. We do it by enforcing specific idioms that only require a developer to recognize intent instead of learning 80 different ways to do the same thing. You simply put a single or double underscore in front of the…

What are the reasons for keeping components of a 'thing' private? I go back and forth on this.

1. expose a clean API surface so users don't care how it works (as long as it works :)

2. make it easier to change the innards without changing its public behavior

3. keep malicious programmers from delving into the innards of our precious code.

It seems like a lot of the discussions on encapsulation (over many years) focuses on #3. Especially in the C++ and Java world. But worrying about that leads to various forms of obfuscation that make the code worse. And who cares about folks who will hack our objects? That's their problem.

Re: Classes are Expressions

#40
post #36

Earlier quoted context omitted.

You can't check that the convention is being broken which makes refactoring more difficult. There will always be a temptation for some developers in some situations to mess with the internal state of objects.

In general, js is more about good practices and craftsmanship, and less about finding ways for the language to force people to do things.

I find this argument to be lacking; if you're using good practices and craftsmanship then nothing is being forced on you as you would not encounter an error for doing something improper.

I prefer to mark up my code as private/protected/public for my own purposes both as built-in documentation and to have one less thing to think about after it's done. Plus underscores are ugly.

Post reply on HN