ES6 transpilation via babel

We can still do things just as well with ES6 classes. It might even force you write code that’s more readable and understandable, which is good in my opinion. For example, instead of using new or the lack of new to symbolize something, why not just use a module variable or a class method? F.e., here’s something in ES5:

new Something
Something() // does something else without the use of `new`.

And here’s something in Es6, which is more readable and easy to understand:

new Something
Something.create() // We have a factory

We can also have a self-referencing Pool class (in theory, haven’t ran this code):

// an ES6 pool class.

let pool = null // to contain a single pool, in the module scope.

// give the class to the user.
export default Pool
class Pool {
  constructor(initialCount) {
      if (pool === null) {
          pool = new WeakMap()
          pool.set(this, {
              instances: [],
              create() {
                  return new Pool()
              },
          })

          for (let i=0; i<initialCount; i+=1) {
              pool.get()
              let newInstance = pool.get(this).create()
              pool.get(this).instances.push(newInstance)
          }
      }

      let newInstance
      if (pool.get(this).instances.length < 100) {
          newInstance = pool.get(this).create()
          pool.get(this).instances.push(newInstance)
      }
      else {
          newInstance = pool.get(this).instances.pop()
      }

      return newInstance
  }

  recycle(trashItem) {
      if (pool.get(this).instances.length >= 100)
          pool.get(this).instances.shift() // recycling is full, empty some out first.

      pool.get(this).instances.push(trashItem) // put the new item in the bin.
  }
}
Pool.threshold = 100 // maximum size of the pool.

// create the pool during module load, before app code (before the UI) runs.
new Pool()

And here’s a class extending it (extending is only one possible pattern to use):

// Some possible way to use the pool.
export default
class Chocolate extends Pool {
    constructor() {
        let chocolateBar = this.super() // returns an instance, which is a reference to the new object created by the super constructor call.

        // does it work?
        console.log(this === chocolateBar)

        this.flavor = "milk chocolate caramel"
        return chocolateBar
    }

    isInMouth() { return true /* for now */ }
    turnToLiquid() { console.log('chocolate melted.') }

    melt() {
        if (isInMouth())
            this.turnToLiquid()
    }
}

@gadicc and @joe, I think “classical inheritance” is not where we should head when we can and should be looking into breaking out of that thinking and moving to “Prototypal OO”. I don’t think we should be trying to create a factory by using the class. Let’s just do it the way it should be done using closures and prototypical inheritance if we are going to do it at all. Some of the Willyboat devs tried to introduce it, but it needed to be adopted from the base code to really work well and create a good API.

Kyle Simpson had a great 3 part article series about objects with regards to javascript and constructors.

But this does not mean we abandon ES6. We only abandon ES6 class!

We can and should expose these methods to the consumers of the library (API) when it will benefit them.

Aren’t Classes just sugar for Prototypal inheritance? In cases where we are going to write

    function ES5_Base(data) {
      this.data = data;
    }

    ES5_Base.prototype.getData = function() {
      return this.data;
    }

    function ES5_Sub(data) {
      ES5_Base.call(this, data);
    }

    ES5_Sub.prototype = Object.create(ES5_Base.prototype);
    ES5_Sub.prototype.constructor = ES5_Sub;

we may as well write that in ES6 (when it’s native), since it’s basically the same thing. I don’t think we should entirely abandon ES6 class, as it’s a much beter and readable syntax for the same thing (with very minor implementation details, and suitable in most cases).

If we want to supply a factory function to the user, it might be better to just lowercase it:

imprt {createChocolateBar} from 'somewhere'

let chosoBar = createChocolateBar() // the factory internals can be stored in the module from where the factory function came from,

and not use the new keyword at all. We can totally do that, and it would be easy to read. We definitely don’t have to use classes for everything, but when we do, the class cases should be as readable and clear as possible. It’s cool we have the ability to use-new-or-not, but IMO that doesn’t mean it’s better. Let’s make our code base easy to understand right away, so that completely new programmers and advanced programmers alike can get started with our libs quickly.

I think of Prototypal OO in JavaScript as the way that JavaScript objects are implemented, whereas Classical OO is just a pattern that we can use withing the Prototypal implementation of objects. Classical a subset of what we can do in JavaScript. The ES6 class syntax is just the constructor pattern with nice new shiny syntax (which I absolutely love). We don’t have to use it for everything. We can still do things like make pure objects that inherit prototypically (no constructors, only factory functions), “concatenate” objects (f.e. using _.extend or the new Object.assign), or both (constructor pattern + mixins onto prototypes). See http://aaditmshah.github.io/why-prototypal-inheritance-matters/#toc_10

Let’s keep all of our options open. I think classes make sense for lots of things, but not necessarily everything (although we can implement everything in classes if we wanted to).

Bottom line: Let’s write easy-to-read, well-documented, simple code.

Yes, but that was just me showing the problem with going down that road of a bad pattern.

Hmmmm, if we use a purely concatenation approach, we’ll have the most speed (no need to look down prototype chains) since all properties are on a single object. I’m starting to think that for our purposes we might like to go down that path. Here’s the component example, with one small change:

// --------------- Component.js - a concatenative class?

export default
Class ('Component', {
    // No constructor here, so it'll have a default one, the same as in ES6.
    someMethod() {
        let something = 'something'
    }
}, 'flat') // a la crockford.

// --------------- SomeComponent.js
import Component from './Component'

export default
Class('SomeComponent') .extends ('Component', {
    SomeComponent() { // constructor
        this.super.call(this)
    },

    aMethod() {},
})

The downsides would be that methods can’t be extended (without introducing internal closures withing the Class().exnteds() calls, which would decrease the performance we’re trying to achieve), but user’s can still call methods from another class manually. Internally, this is how the class would be created:

// -------- lowclass.js
let bodies = new WeakMap()

...

// if called Class().extends()
Object.assign(classBody, superClassBody) // simple concatenation.

// a body stored for each constructor (since we're not using prototypes).
bodies.set(classBody.constructor, body)

classBody.constructor.body = classBody // for convenience to the user.
return classBody.constructor

...

// --------- SomeComponent.js
// child class
import Component from './Component'

export default
Class ('SomeComponent') .extends (Component, {
    constructor() {
        Component.body.constructor.call(this) // call the super constructor (despite having no prototypes)
    }
    someMethod() {
        Component.body.someMethod.call(this) // call the super method (despite having no prototypes).
        ...
    }
})

// We have the classical pattern, but implemented with concatenation!

I’m really liking this idea. We’ve just eliminated prototype lag in Components (which will be updating at 60fps). Concatenative classes will give us the most performance (we simply provide a comvenience though an API (like my Class().extends()) so that users can still do Classical OOP).

@talves By the way, in that article you linked, they use Backbone as an example while describing their debugging problem. That’s not Classical OOP’s fault, that’s Backbone fault. Backbone’s constructor pattern is flawed, convoluted, and fails at one basic thing: naming constructors. They’d have had much better time actually using ES6 classes.

So, the article is flawed in the sense that it’s describing ES6 classes using a flawed ES5 pattern. They are only half correct about having to search up the prototype inheritance, but trust me, debugging Backbone isn’t pleasant. Part of the problem (as he alluded to) also lies in the fact that the team had bad programming practices (working around bugs of a super class in a child class instead of just fixing the problem at the root (and that probably means a lack of proper testing)).

But hey, if we use the concatenative approach (which I’m totally down to do) then we’ve eliminated some room for poor practices! I’m so down. I’m going to update lowclass after Tuesday. I must now disappear until then.

Not sure the article is really flawed in the sense that he is showing why a “classical pattern” is not needed in javascript. Did you read the last article in the series? That is the one that brings it together and has nothing to do with the ES6 vs ES5. It is showing how “functions delegate up their [[Prototype]] chain” and why constructors cause a lot of complexity that does not need to be introduced.

All this being said, I still have to do all the performance testing of any pattern proposed! :stuck_out_tongue:

At first glance, the use of ‘new’ seems to be much faster 4x. :confused:

Yeah, I’m gonna implement the concatenative feature in lowclass soon and benchmark it. :smile:

What were you comparing it against?

What do you guys think about the idea of concatenation-based classes, but with a classical way of writing them? I really like the idea of writing classes using a single syntax/API, yet having the ability to specify what type of classes they are (easy to modify later if needs change).

Maybe we can also have class helpers to make things like pools, etc:

let Dog = Class ('Dog') .extends (Animal, {...}) // normal class

let Dog = Class ('Dog') .extends (Animal, {...}).pool(100) // pooled class, 100 initialized initially
// or
let Dog = Class ('Dog') .extends (Animal, {...}) // normal class
let PooledDog = Dog.pool(100) // pooled class, 100 initialized initially

The key is to look at extending (inheritance) performance most of all. There really is a huge hit using class and extends over Object.assign where one level is 50% slower in my first tests.

Oh, sorry. I compared it against Object.create().

:+1: Yes. We could use a library like [Stampit][1] to do this. It is an awesome library and follows a standard pattern. More benchmarking!

OK Discourse, you are right! :blush: Very important to me!

[1]: https://github.com/stampit-org/stampit

Ok, here is a jsperf test (only chrome) using the following methods. Unless I did something really wrong here, Stampit has some crazy problems with instantiation performance. I even threw in a clone factory to see if it would perform better. Nope, worse, but it should be the way the clone is just another wrap of a stampit call.

Elliot, the creator of Stampit speaks to the use of Object.create not being faster in this issue. Not sure I agree with him 100% on everything he is saying.

Here are the tests:

/* Foo */
    
    function Foo(name) {
      this.name = name;
    }
    
    Foo.prototype.identify = function() {
      return "This is " + this.name;
    }
    
    /* Foo2 */
    var Foo2 = {
      identify: function() {
        return "This is " + this.name;
      }
    }
    
    /* Foo3 */
    var Cloneable = stampit().init(function(_refs) {
      _refs.instance.clone = function() {return _refs.stamp(_refs.instance)};
    });
    
    var BaseFoo3 = stampit().init(function(_refs) {
      var name = _refs.instance && _refs.instance.name ? _refs.instance.name : 'default'; // private
    
      _refs.instance.identify = function identify() {
        return "This is " + name;;
      };
    }).compose(Cloneable);
    
    var Foo3 = BaseFoo3({name:'foo'});
    
    function assert(condition) {
        if (!condition) {
            if (typeof Error !== "undefined") {
                throw new Error("Test Flawed");
            }
            throw message; // Fallback
        }
    }
    
  };

Exactly, that’s what the concatenative approach solves. Although we might say something like Class ('Foo') .extends ('Bar') with lowclass, behind the scenes the extending is done with Object.assign to create a single-level object with no prototypes.

That already looks too heavyweight. It’s going to definitely have closures around your code in order to implement things like “private” and “protected” properties, and each level of that will incur memory and CPU usage. And then we’ll be allowing devs to write code that doesn’t translate to pure ES5 or ES6 classes.

I got the same message. x}

Sorry, I have to disagree about it being too heavy. So we are going to be able to do this with less than 191 lines of executable javascript better than Stampit? I have not gone through lowclass, but will take a look. BTW, stampit supports and builds on concatenative inheritance.

Yep closures. What did you have in mind to use? Are you planning to do it without closures?
Memory and CPU usage are not determined by closures as much as the incorrect use of them. A well written library is going to help build away from those issues.

Please elaborate, I am not sure I know what you mean.

The concatenative implementation isn’t there yet, I’ll add it soon. The only bad performance hit that it has is during class definition, with the use of Object.getOwnPropertyDescriptor and Object.defineProperty, which is a little heavier than direct assignment, but other than that it just uses the constructor pattern with no closures, so it’s exactly like writing the constructor pattern manually.

Indeed, that is cool! I believe I can borrow some ideas for lowclass. stampit().enclose() is interesting. So basically what that’s doing is letting you specify multiple constructor functions instead of just a single one. So constructor/class C can be the combination of constructors/classes A and B (composed). The downside compared to the prototype constructor pattern is that each instance has a new copy of all the methods, so if you have 100 instances, you’d be using 100 times more memory to store the methods than in 100 instances defined with the constructor pattern. F.e., in this example

var availability = stampit().enclose(function () {
  var isOpen = false; // private
 
  return stampit.extend(this, {
    open: function open() {
      isOpen = true;
      return this;
    },
    close: function close() {
      isOpen = false;
      return this;
    },
    isOpen: function isOpenMethod() {
      return isOpen;
    }
  });
});

each instance will contain new definitions of the same methods, defined during construction rather than on the prototype. It’s nice what benefits we get in terms of “private” vars, but with the huge expense of memory.

Well, i guess that’s just me being opinionated, and basically promoting the use of classical OO (f.e. Class ('Foo') .extends(Bar)). The lack of private vars in the ES5 constructor pattern and ES6 class syntax (which is what I mean by “pure ES5 or ES6 classes”) will reduce memory by sharing a single method instance across all object instances as well as save some CPU because instance methods won’t need to be re-defined during every instance construction, hence is probably the reason that

Side note! Since we’re supporting IE 11 and up, we can have private vars in the ES5 constructor pattern or with ES6 classes now with WeakMap, but it won’t work with IE10-. Here’s an example of ES6 classes with private vars (the same applies to ES5 constructor pattern):

let privates = new WeakMap() // keys in the WeakMap must be Objects.

class Foo {
    constructor() {

        // private properties are stored in the weakmap.
        privates.set(this, {
            foo: 'foo'
        })
    }

    someMethod() {
        // we can access the private properties in another *prototype* method,
        // which isn't possible with the closure pattern of stampit.enclose,
        // etc.
        console.log(privates.get(this).foo)
    }
}

I like classical OO, but I believe we can still take advantage of things like mixins/compose. I had added a .mixin method to lowclass, but removed it before publishing, but it looked like this:

Class ('Foo') .compose (Bar, Baz) .extends (Lorem, {
  ...
})

What that would do is just mix Bar and Baz onto the new class’ prototype before finally mixing in the supplied body literal onto the new class’ prototype created from Lorem.

The interesting thing about WeakMaps and private vars would be that when you mixin different classes defined in different modules, each class would have it’s own private collection, which may not behave as intended.

F.e. Suppose the following class extends the Foo class from my previous example:


let privates = new WeakMap()
Class ('Bar') .extends (Foo, {
    constructor() {
        super()

        // private properties are stored in a second weakmap, not the same one
        // as Foo!  So the private weakmap from the Foo module is not
        // inherited.
        privates.set(this, {
            foo: 'bar'
        })
    }
    someMethod() {
        super.someMethod()

        // not the same `foo`!
        console.log(privates.get(this).foo)

        // output shows "foo" and "bar".
    }
})

It’s per-instance privates, but on each level of the class hierarchy. We could fix this problem by storing a reference to some hierarchy-wide WeakMap in a separate module and importing it, if that’s what we want, which would be like “protected” members in Java.

@talves I’m not claiming that this classical pattern with private variables in WeakMaps is better, but just putting it out there so we can weigh our options. I’m just jotting down ideas. :smile:

Here’s a thought: if we store a single WeakMap in some module, and import it into every class, that’d essentially be like “private” members for the top-level object of every instance anywhere in an app that uses such a library, except that a user could also import that WeakMap reference and get the “privates” of any object, but it just might be better than prefixing local proeprties with underscores (f.e. this._somePrivateProperty). A use would need to import the WeakMap, then privates.get(someInstance).somePrivateProperty which is at least a little more warning-ful that danger may ensue from messing with the privates.

What can we gleen from that? This: “protected members” are still not possible in ES6 without leaking information to the user.

Okay, less writing, more coding! I’m going to make my prototype using mostly classical OO (using concatenative classes wherever possible, falling back to constructor pattern prototypes as minimally as possible or when memory is exhausted), with optional mixins/compositions.

There is a solution for prototype assignments of methods to make sure you are not using the methods over and over for each instance. :smile: It is also very easy to do, once you understand how it works. It is covered in the advanced examples.

I was always a proponent of classical until the last few months digging deep into the real benefits of prototypal OO. I understand you being opinionated about it, BUT there is really no reason to do a mix of the two in my opnion :smile: . I think there is really no advantage to chaining classes using a constructor method. :smile:

I know :wink: Sometimes statements I make, I wish I would have stated that it was just an idea.

FYI .enclose() is deprecated and is now .init() in Stampit now.

I am going to make my design with Prototypal OO, because I know I can easily create a parallel library using classical later and benchmark an app using each. :stuck_out_tongue:

Still using ES6 and taking advantage of modules. :wink:

1 Like

Babel also supports JSX format, and you can plug in different backends to it.

ie: use JSX to build render trees, even without needing to use react.

reference: https://github.com/babel/babel/issues/841

1 Like

@AdrianRossouw I love react for building render trees. I read somewhere (but forgot where) that we can make alternate rendering backends for React, so instead of rendering DOM, we can render just a scene graph.