Skip to content

Instantly share code, notes, and snippets.

@robotlolita
Forked from BrendanEich/minimalist-classes.js
Created November 2, 2011 01:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robotlolita/1332633 to your computer and use it in GitHub Desktop.
Save robotlolita/1332633 to your computer and use it in GitHub Desktop.
less minimalism, richer leather
// A response to jashkenas's fine proposal for minimalist JavaScript classes.
// Harmony always stipulated classes as sugar, so indeed we are keeping current
// JavaScript prototype semantics, and classes would only add a syntactic form
// that can desugar to ES5. This is mostly the same assumption that Jeremy
// chose, but I've stipulated ES5.
// Where I part company is on reusing the object literal. It is not the syntax
// most classy programmers expect, coming from other languages. It has annoying
// and alien overhead, namely colons and commas. For JS community members who
// don't want classes, either proposal is "bad", so let's focus on the class
// fans who will use this feature.
//
// Therefore I propose giving classes their own new kind of body syntax, which
// is neither an object literal nor a function body. The main advantage is that
// syntax for class elements can be tuned to usability, even as it desugars to
// kernel semantics drawn from ES5 extended by both the super and private names
// proposals.
// Not shown below is the super proposal at
//
// http://wiki.ecmascript.org/doku.php?id=harmony:object_initialiser_super
//
// which composes as expected when 'super' is used in methods defined in a
// class instead of in an object literal.
//
// In contrast, you will see hints of the private name objects proposal at
//
// http://wiki.ecmascript.org/doku.php?id=harmony:private_name_objects
//
// Also visible throughout: the method definition shorthand syntax proposed at
//
// http://wiki.ecmascript.org/doku.php?id=harmony:object_literals#object_literal_property_shorthands
//
// For programmers who want classes as sugar for constructors and prototype, I
// hope this proposal wins.
//
// To be perfectly honest, I personally would be happy with Allen Wirfs-Brock's
// "Exemplars" approach and no classes. But for programmers coming from classy
// languages, I believe "minimalist classes" will tend to be too minimal to be
// usable out of the box. They'll want more.
//
// Brendan Eich, 1-Nov-2011
// First, basic usage from a real-world library (Three.js)
class Color {
constructor(hex) {
...
}
// Prototype data properties, introduced by var or const at class body level.
// Rationale: object literal syntax requires comma separation and has no way
// to define a const that's clearly in Harmony. Classy languages do not need
// no steenkin' commas, but we do want const in ES.next.
//
// An objection even I have made: we should not conflate property definition
// syntax with lexical binding declaration syntax. But the cat's out of the
// bag with 'var' in global code in ES1-5, and it's hard to beat "const" as
// the keyword for initialize-only data properties.
//
// One fair objection: this syntax may mislead people into thinking methods
// lexically close over prototype properties declared this way. A calculated
// risk, what can I say?
// Since, as far as I can see, class bodies are only declarative object
// shape definitions, I think we can safely get rid of `var' for declaring
// public slots in the class, just as we do with methods (or are they plain
// functions?)
r = 1;
g = 1;
b = 1;
// That could be easily coupled with the comma-operator, I guess, but I'm
// not that familiar with the grammar, so feel free to correct me.
r = 1, g = 1, b = 1;
// Or non-initialised slots, just like you have with `private':
r, g, b;
copy(color) {
...
}
setRGB(r, g, b) {
...
}
setHSV(h, s, v) {
...
}
}
// To create a class with its prototype chain set correctly:
class Fox extends Animal {
...
// The special syntax for defining class-level properties and methods uses a
// prefix keyword. The 'static' keyword is reserved and somewhat hated (or at
// least considered a misnomer), but I went with it instead of 'class' to
// avoid confusion in overloading the c-word, and to be more future-friendly
// in case nested classes are wanted (right now I do not want them, since a
// class at class-body level would declare a prototype inner class and not a
// static inner class).
static const CONSTANT = 42;
static classMethod() {
...
}
}
console.log(Fox.CONSTANT);
Fox.classMethod();
// Classes are expression forms as well as declaration forms. As with function
// expressions, the name is optional in a class expression.
const frozenClass = Object.freeze(class {...});
// Named class expressions are supported too. The name is bound only in the
// scope of the class body, as for named function expressions.
animals.push(class Fox {});
// An anonymous class expression with superclass specification, after Jeremy's
// gist, but with an explicit and required body.
var subclass = function(parent) {
return class extends parent {
...
};
};
// Unlike Jeremy's proposal, classes cannot be built up programmatically by
// abutting an expression to a class head. That's too dynamic, it doesn't work
// with the 'super' proposal. So any synthesis of a class body must use eval
// or equivalent. At this time, I do not propose adding a Class constructor
// analogous to Function, but I believe it could be added without issue.
// As in Jeremy's gist, here's the Monster class from the harmony: proposal
// (http://wiki.ecmascript.org/doku.php?id=harmony:classes#the_proposal_in_a_nutshell)
// ... sans unnecessary 'public' keyword prefixing!
class Monster {
constructor(name, health) {
this.name = name;
this.health = health;
}
attack(target) {
log("The monster attacks " + target);
}
isAlive() {
return this.health > 0;
}
setHealth(value) {
if (value < 0) {
throw new Error("Health must be non-negative.");
}
this.health = value;
}
numAttacks = 0;
const attackMessage = "The monster hits you!";
}
// Now there's one more problem to address, which could be deferred, but which
// falls under "batteries included" to some (not all) of the cohorts of classy
// programmers using JS or coming to it fresh in the future. And that is the
// cost of this[kPrivateName] given const kPrivateName = Name.create(...) where
// module Name from "@name" has been declared.
//
// Instead I propose we support private kPrivateName, ...; as a special form
// only in class bodies (for now) that both creates a private name object and
// binds it to a lexical const binding that may be accessed on the right of @
// in methods defined in a class.
//
// For class-private instance variables, obj@foo is supported as well (with no
// LineTerminator to the left of @ in this case, or it's short for this[foo]).
// See the new sameName method for an example of infix and prefix @ in action.
//
// There is no const instance variable declaration. Non-writable instance vars
// (properties to most people) are quite rare. Use ES5's Object.defineProperty
// if you must. This avoids ugly fights about read before constant instance var
// initialization too.
class Monster {
private name, health;
constructor(name, health) {
@name = name;
@health = health;
}
sameName(other) {
return @name === other@name;
}
attack(target) {
log("The monster attacks " + target);
}
isAlive() {
return @health > 0;
}
setHealth(value) {
if (value < 0) {
throw new Error("Health must be non-negative.");
}
@health = value;
}
numAttacks = 0;
const attackMessage = "The monster hits you!";
}
// As in the harmony:classes proposal, 'const class' freezes all of
// 1. the class binding, if named declaration rather than expression,
// 2. the class prototype induced by the const class,
// 3. the constructor method of the const class.
//
// 'const class' also seals instances constructed by the constructor, as if the
// last line of the constructor method were Object.seal(this), called using the
// original values of Object and Object.seal.
const class Frosty {
}
@arv
Copy link

arv commented Nov 2, 2011

The main problem with skipping var and initializer (which we allow in Traceur) is that empty, non initialized properties look out of place:

class Point {
  x;
  y;
  ...
}

and it gets even worse without the optional semicolons:

class Point {
  x
  ...
}

I also think var fits nicely with const.

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