News for hackers. Stuff that matters.
Re:Classes are Expressions by munificent on Thursday June 04, 2015 @05:29PM (#9662205)
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] == otherPerson[firstNameProperty];
        }
      };
    )();
Now try implementing sameFirstName() using closures. Good luck! :)
Re:Classes are Expressions by Cushman on Thursday June 04, 2015 @06:09PM (#9662462)
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 Person = (function() {
      var names = [];
      function Person(name) {
        this.key = names.length;
        names[this.key] = name;
      }
      Person.prototype.sameName = function(other) {
        return names[other.key] === names[this.key];
      };
      return Person;
    })();
Which is the ES3 generated by this CoffeeScript:

    class Person
      names = []
      constructor: (name)->
        @key = names.length
        names[@key] = name
      sameName: (other)->
        names[other.key] is names[@key]
Of course these implementations do something dramatically different behind the scenes, and I emphatically don't think people should follow this example, but the interface is the same. Edit: Except that I left `.key` as an editable property, which is problematic. We'll leave that bit as an exercise, though.

Whether `names[this.key]` is as "nice" to work with as `this[nameKey]` is, uh, left up to the reader; personally, count me in the camp that doing either of those things is nuttypants compared to `this._name`.

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

Re:Classes are Expressions by munificent on Thursday June 04, 2015 @07:00PM (#9662759)
> [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 by Cushman on Thursday June 04, 2015 @07:17PM (#9662839)
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 by gcb0 on Friday June 12, 2015 @04:36PM (#9708358)
so, it is exactly the same as (assume the keys are in a closure i'm too lazy to type it all)

function Person(first, last){ var firstNameProperty = {}, // unique snowflake obj lastNameProperty = {} // yet another unique obj this[firstNameProperty] = first; this[lastNameProperty] = last; }

ES6 should be saving me from typing all the scoping hacks such as closure. not giving me syntax sugar for things i could do already without odd language constructs (now i have to run TWO loops to get my obj properties. which is what everyone is going to do anyway, since you can iterate on all the symbol properties anyway)

Re:Classes are Expressions by rmrfrmrf on Thursday June 04, 2015 @05:57PM (#9662406)
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 = function() {
                return privateVars.firstName;
            };

            this.setFirstName = function(firstName) {
                privateVars.firstName = firstName;
                return this;
            };

            this.getLastName = function() {
                return privateVars.lastName;
            }

            this.setLastName = function(lastName) {
                privateVars.lastName = lastName;
                return this;
            };

            this.sameFirstName = function(otherPerson) {
                return this.getFirstName() == otherPerson.getFirstName();
            }

            this.setFirstName(firstName);
            this.setLastName(lastName);
            Object.freeze(this);
            return this;
        }
    }());

    var p = new Person("John", "Doe");
    var q = new Person("John", "Smith");
    var r = new Person("Jane", "Doe");

    p.sameFirstName(q); // true
    p.sameFirstName(r); // false
Re:Classes are Expressions by Cushman on Thursday June 04, 2015 @06:17PM (#9662514)
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 by rmrfrmrf on Thursday June 04, 2015 @06:28PM (#9662584)
Oh wow, I totally missed that. Thanks for clarifying!
Re:Classes are Expressions by Cushman on Thursday June 04, 2015 @06:49PM (#9662717)
Np! Mind it is technically possible to do, check out my sibling comment if you haven't already.