Skip to content

Instantly share code, notes, and snippets.

@allenwb
Forked from BrendanEich/minimalist-classes.js
Created November 2, 2011 03:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save allenwb/1332734 to your computer and use it in GitHub Desktop.
Save allenwb/1332734 to your computer and use it in GitHub Desktop.
less minimalism, richer leather
//work in progress
// A response to jashkenas's fine proposal for minimalist JavaScript classes.
// and BrendanEich's Rich Corinthian Leather alternative proposal
//intro and justifications still to come
// 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 and used a few accepted ES.next extensions.
// 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 = {
// The body of a class declaration is an object literal. However the value
// that is bound to the "class name" is the constructor method defined by
// by the object literal rather than the object itself. = is used to indicate that
// this is a non-hoisted initializing declaration just like let and const. The = has
// other significance that will become apparent in subsequent examples.
//
// The object described by the literal is the prototype object that is the value of the constructor's
// "prototype" data property. If a constructor method is not explicitly specified
// one is automatically generated.
constructor(hex) {
...
}
// Prototype data properties, are just object literal property definitions, seperated
// by commas. Readonly properties are designated by preceding the property definition
// with a # (this was proposed at the last TC39 meeting).
// Rationale: object literal syntax already exists in JS as a way to describe the properties
// of objects. Having a separate syntax for describe properties in a class definition just as
// unnecessary redundancy and confusion.
//
* // 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?
r : 1, g : 1, b : 1,
copy(color) {
...
}
// comma after method bodies are optional
setRGB(r, g, b) {
...
}
setHSV(h, s, v) {
...
}
}
// To create a class with its prototype chain set correctly:
class Fox = Animal <| {
...
// The same "set literal [[Prototype]] operator" that is described in
// http://wiki.ecmascript.org/doku.php?id=harmony:proto_operator
// (whether ultimately written as <|, <:, beget, proto, or something else)
// is used to specify the superclass of the new class.
//
// The object reference on the LHS of <| identifiers the superclass. It may be
// function, an non-function object, or a class (a class is really this a function).
// The [[Prototype]] of the class object (the constructor) and the class prototype
// are set appropriately depending upon whether the "superclass" is specified as
// a function (with a prototype property), an object with a prototype property, or
// an object with a constructor property.
// 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).
//
// Class-side properties inherit, just as constructor-side properties do with
// the <| proposal:
//
// http://wiki.ecmascript.org/doku.php?id=harmony:object_literals#individual_extension_summary
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;
}
var 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;
}
var 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 {
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment