Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
A minimal class proposal for ECMAScript.next
// Roughly: a combination of Jeremy’s and Allen’s ideas, updated with the results of recent discussions
// Guidelines:
// - Don’t use the same syntax as for object literals, but stay close.
// - Rationale: Some object literal features are forbidden in class declarations => don’t confuse people
// - Rationale: Comma separation is a bit tricky.
// - Keep new features at a minimum
// - Don’t obscure the fact that private names are actually name objects.
// => They can also be imported from somewhere else – a use case that needs to be supported.
// - In order to minimize confusion, keep module syntax and class declaration syntax similar.
// Rules:
// - Use @ to refer to name objects
// - there is no syntactic sugar for `this.`
// Name objects:
// - Rough rule for what @foo means: “insert arbitrary identifier here”.
// - Used for property access and to name methods.
// - foo.@bar is syntactic sugar for foo[bar]
// - Rationale: immediately obvious that it’s property access
class Monster extends Being {
// Only methods are allowed here (because these are the prototype’s properties)
// Create name objects that only exist within the declaration’s scope
private age, health, incAge;
// Constructor
// Alternative: use the name "new". Caveat: super.new(name) looks a bit weird.
constructor(name, this.weight, this.@age, this.@health) {
super.constructor(name);
}
getNameObjectForAge() {
return age;
}
// private method
@incAge() {
this.@age++;
}
// getter
get strength() {
// stronger with increasing age...
return super.strength * this.@age;
}
}
module name from "@name";
// Alternatives: use <| operator, use a do-block
let Monster = function () {
let age = name.create();
let health = name.create();
let incAge = name.create();
function Monster(name, weight, age, health) {
super.constructor(name);
this.weight = weight;
this[age] = age;
this[health] = health;
}
Monster.prototype = Object.create(Being.prototype);
Monster.prototype.constructor = Monster;
Monster.prototype.getNameObjectForAge = function () {
return age;
};
Monster.prototype[incAge] = function () {
this[age]++;
}
Object.defineProperty(Monster.prototype, "strength", {
get: function () {
// stronger with increasing age...
return super.strength * this[age];
}
});
return Monster;
}();

allenwb commented Nov 3, 2011

I still don't like "foo.@bar is syntactic sugar for foo[bar]". If we have explicit .@ syntax we don't need to force that equivalence.

I would (in brief) define foo.@bar as
Lexically resolve the identifier "bar" and let key be its value.
If key is not a private name object, throw ReferenceError
return foo.[Get]

It would remain the case that using existing semantics
foo.@bar
and
foo[bar]
would evaluate to the same thing. But we don't need to say that or imply this would always be the case if we decided to allow user overrides of [ ] access.

Owner

rauschma commented Nov 3, 2011

Agreed. But if that happens then [] completely changes its meaning from “property access" to “collection element access”. And we need Object.getProperty() and Object.setProperty(). I’ll leave the comment as is at the moment (because it works well as an explanation). Later, it can be changed to use the Object.*Property() methods.

Owner

rauschma commented Mar 21, 2012

@khs4473: I think the objection to a static keyword used in this manner was that it adds a lot of noise. But with static being rare, it could work well.

Note, though, that the parallel with private properties is not very strong, because private members and static members end up in different objects.

@ghost

ghost commented Mar 21, 2012

I think we will have super expressions in ES.next (separately from classes), so calling super.constructor is (imho) unnecessary (call super(...)) and brittle (they may have changed .constructor, it's property like others).

Raynos commented Mar 21, 2012

Can we have an example of the entire thing desugared?

I attempted to desugar it : https://gist.github.com/2151045

Raynos commented Mar 21, 2012

You forgot the return statement in the desugared section

domenic commented Mar 21, 2012

Is it

Monster.prototype = Object.create(Being);

or

Monster.prototype = Object.create(Being.prototype);

?

Owner

rauschma commented Mar 21, 2012

@Raynos: Fixed, thanks.

Owner

rauschma commented Mar 21, 2012

@domenic: You are absolutely right. Fixed. Error-prone, looking forward to syntactic sugar...

@ghost

ghost commented Mar 21, 2012

I think you have problem with the desugaring because all methods (but at least the constructor) are non-enumerable, and to match object-initializer, method (not the constructor) should also be non-writable (but configurable), so it would be easier to desugar to one big

Object.create(Being.prototype, {
  ...
})

Not to mention constructor-inheritance, do you plan it as part of this proposal (then you should use <| when defining Monster function),

Raynos commented Mar 21, 2012

@Herby where does it say whether methods should be non-enumerable (only the constructor property should be)

Owner

rauschma commented Mar 21, 2012

@Herby, @Raynos: I expect the decision on whether or not to make methods non-enumerable (which is done by built-ins, but not by most of the userland code that I have seen) to be final fine-tuning. My first solution was a big Object.create() with property descriptors, but then you have to use [] for the private property names (for which there isn’t consensus, yet).

@ghost

ghost commented Mar 21, 2012

@Raynos: In draft ES6 spec, it says about object initializers that when short method syntax is used, they are created with enumerable = false, configurable = true and writable = false.

domenic commented Mar 22, 2012

writable false, hmmm, that seems to go against even built-ins. I imagine most would be against it, but IMO it'd be a good thing to force something more explicit (i.e. Object.defineProperty) when overriding existing methods.

allenwb commented Mar 22, 2012

@domenic, that's exactly the logic behind those attribute choices for concise methods. If somebody has defined a property as a "method" it should take something more than an assignment to change it.

However, many people find the configurable: true & writable: false combination baffling so it is going to take some effort to keep it in the spec.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment