Skip to content

Instantly share code, notes, and snippets.

@wycats
Created November 5, 2011 07:37
Show Gist options
  • Star 25 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save wycats/f61d290b18c105b33d46 to your computer and use it in GitHub Desktop.
Save wycats/f61d290b18c105b33d46 to your computer and use it in GitHub Desktop.

JavaScript Class Syntax

Let's build a JavaScript class syntax from first principles. For the purpose of this exercise, let's assume that the purpose of the class syntax is to add much-needed sugar to common JavaScript idioms.

Let's start with how JavaScript "classes" work today:

// this is a constructor
Person = function() {
  this // `this` is a new instance of Person
}

// define a list of properties that exist on all instances of Person
Person.prototype = {
  hello: function(text) {
    this // `this` is the current instance of Person
    console.log(text)
  }
}

Let's add some syntax to JavaScript to represent this idiom:

class Person {
  function constructor() {
    // `this` is a new instance of Person
  }
  
  function hello(text) {
    // `this` is the current instance of Person
    console.log(text)
  }
}

The class is simply the constructor function with the remaining functions assigned to its prototype. If no constructor function is supplied, an empty function is used.

For a dash of sugar, we can allow the word function to be left out:

class Person {
  constructor() {
    // `this` is a new instance of Person
  }

  hello(text) {
    // `this` is the current instance of Person
    console.log(text)
  }
}

Now let's look at inheritance:

// this is a constructor
Man = function() {
  Person.apply(this, arguments) // invoke the superclass constructor

  this // `this` is a new instance of Man
}

// subclass Person
Man.prototype = Object.create(Person);

// create a new method called fullName
Man.prototype.fullName = function() {
  return "Mr. " + this.firstName + ' ' + this.lastName;
}

// subclass `hello` and invoke the superclass
Man.prototype.hello = function(text) {
  Person.prototype.hello.call(this, this.fullName() + " says: " + text);
}

Let's add some syntax to JavaScript to represent this idiom:

// same as Man.prototype = Object.create(Person)
class Man extends Person {
  // using ES.next rest arguments syntax
  constructor(...args) {
    super(...args) // same as Person.apply(this, arguments)
  }

  fullName() {
    return "Mr. " + this.firstName + ' ' + this.lastName;
  }

  hello(text) {
    // same as Person.prototype.hello.apply(this, [...args])
    super(this.fullName() + " says: " + text);
  }
}

The super keyword simply invokes a function of the same name on the direct superclass of the current object's constructor, passing along any arguments passed.

Finally, people sometimes add properties directly to the prototype:

Man.prototype.salutation = "Mr.";

Let's make that possible inside of the class body:

class Man extends Person {
  salutation = "Mr.";
}

This syntax alone would be a vast improvement to existing JavaScript syntax, and would be worthy of inclusion without additional improvements.

That said, let's take the opportunity to make two additional improvements while we're at it.

Let's Fix a Common Bug

A common problem with defining properties on a prototype comes when you define a property to be an object, like an Array:

Man.prototype.children = [];

Let's say that instead of evaluating the right hand side of an assignment immediately, and sharing it across all instances, it is evaluated for each new instance:

class Man extends Person {
  children = [];
}

new Man().children === new Man().children // false

When creating a new instance, in addition to invoking the class' constructor, we evaluate the right hand side of each declared property and assign it as a property of the new object.

Extensibility

Existing JavaScript libraries that implement classes perform various actions during the process of creating a new class.

Let's allow classes to define a hook that should be called after they are extended.

Person.extended = function(child) {
  // child is the subclass of Person
  // child.prototype would exist at this point
}

This will allow additional class semantics to be defined by libraries and toolkits.

That's it

Let's try adding a very simple class syntax to JavaScript and see where that takes us.

@wookiehangover
Copy link

+1
...really like the idea of function declarations being used to assign class properties. Would it be possible to set private members inside the class (with getters and setters, obv)? Something like:

class Person {
  var age = 9001;

  getAge() {
    reutrn age;
  }
}

@tj
Copy link

tj commented Nov 10, 2011

meh

@jacobandresen
Copy link

nomnomnom

@amasad
Copy link

amasad commented Nov 10, 2011

"Let's Fix a Common Bug" +1

@nagaozen
Copy link

Hmmm, what's the purpose of this gist?! I don't agree to push pythonic or javaonic syntax to javascript. It's good the way it is now... Furthermore, a language that keeps changing isn't good thing IMHO. What's wrong with the MooTools or Prototype approach to mimic standard classes?!

@tj
Copy link

tj commented Nov 11, 2011

nagaozen but that would mean people have to actually learn javascript!! oh noesss

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