My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Saturday, March 29, 2014

What Books Didn't Tell You About ES5 Descriptors - Part 4

I've left Part 3 feeling guilty about the impression developers have on modern JavaScript, and this comment confirmed this feeling:
Peter StJ
I have read all 3 parts and to tell you the truth it does look bad.
... just looking at that code you have used as example gives headache to everyone I show it to, including JS developers ...
I think Peter is absolutely right, what I've shown 'till now is an explanation of the modern pattern, enriched with all possible problems that this could have with not so outdated browsers ... where, this part is the catch: I gonna propose you a simple way to forget about all these problems and write meaningful code that looks beautiful and dose not need explanation while you read it, as well as telling you even more about how screwed has been in history ECMAScript 5.x, and why now is the right moment to adopt it!

In other words, metaphorically speaking, if anyone would ever tell you what jQuery is actually trying to fix, behind the scene, you'll feel dizzy, hoping nobody had to inform you about how bad is the web development these days!
Regardless, this is why I've chosen such topic for these posts, where the purpose is to enlight, if possible, and warn you it ain't gonna be any better any time soon, cross platform and engine speaking ... so you better understand what's going on ;-)

The Internet Explorer Hysterical ES5's Adoption

In Part 1, I kinda blamed @felixge for some avoided adoptions, specially from node.js developers ... the truth is, that all this new JavaScript stuff has been reluctant for Desktop browsers developers too!
"will it work in IE?" has been probably the most boring, frustrating, and at the same time honest question, and Desktop web developer would ask you ... and the answer has always been kinda, but such word has rarely been considered in softare development ... so here a quick rewind of IE history

Getters & Setters, Since Ever!

Microsoft Developers related pages are great these days, but not so informative 8 years ago. What we missed since IE6 time, is that IE6 had Getters and Setters too but we had to use VBScript in order to obtain such functionality ... in JScript world ... you think that's screwed? Think twice, Dart language these days is offering different behaviors available in JavaScript ... yeah, Dart language is what VBScript has represented ages ago ... before it was cool, IMO!
In any case, the moment I've realized we could have getters and setters in IE too, was already too late, people never used them because at that time Desktop web development was "everything needed in JS world".

Broken ES5 in IE8

When Internet Explorer 8 came out, it came out non standard, supporting only the DOM world, and in a not fully ES5 specd way.
Thanks gosh that was enough to be able to write polyfills like DOM4 so that at least modern HTML5 development, when IE8 support is necessary, even if it current version of IE is 11, is possible without compromising our code style, most modern features, easy custom event dispatching, and everything else that was not present at that IE8 time.
Good part is: IE8 had native JSON object, so that the heavy JavaScript polyfill can be finally dropped, and CSS capabilities were not bad neither plus it still supports conditional HTML or CSS comments! Yaiiii, no other browser will ever be compromised!

Extremely Broken IE9 on Windows Phone 7.X

While version 9 of Internet Explorer finally adopted officially ECMAScript instead of its own JScript pseudo standard, the worst part of this IE 9 story is the fragmentation between Desktop 9, and Mobile 9.
While someone could think and rightly expect these are the same product, there are bugs here and there that differs from Desktop to Mobile in inimaginable ways ...
(function(){'use strict'; // only in strict !!!

  var i = 0; // surprise later on ...

  function Getter() {}
  Object.defineProperty(
    Getter.prototype,
    'fuq',
    {
      configurable: true,
      get: function () {
        // let's get rid of the inherited property
        // and define another one as allowed in ES5
        Object.defineProperty(
          // in IE9 MObile, if it's not a function,
          // it might throw!!!
          this, 'fuq', {value: Math.random}
        );
        // since the property is now ownProperty
        // next time it is accessed will be already there
        // ... right ?
        ++i;
        // WRONG, it will be re-invoked next line
        return this.fuq; // will trigger recursion
                         // unless filtered!
      }
    }
  );

  var da = new Getter;
  alert([
    da.fuq === da.fuq, // false
    i                  // 2 in IE9 Mobile
  ].join('\n'));
}());
Everything that could possibly go wrong in above snippet went wrong, and you can see with your eyes if you have such phone.
First of all, the behavior is not reproducible without use strict when hilariously, IE9 was not even supposed to respect use strict directive.
Secondly, the recursion will kick any of your code/logic badly because while setting the new property, IE9 still triggers the inherited get descriptor.
You don't return this.prop and just the value? That throws!
The only way to prevent recursion is to actually check before setting the descriptor is !this.hasOwnProperty('propertyName') otherwise the app will be basically doomed.
How freaking awesome is all this?

Finally IE10

At least there is an happy ending, IE10 Desktop and Mobile is fully compliant with ES 5.1 specifications so eventually, after 16 years, we have a very good and standard browser working in MS platform.

Not Only IE

The amount of inconsistent behaviors across browsers and platforms when it comes to ES5.1 standard is embarrassing, right today I've filed 3 push request to sanitize Opera Mini with es5-shim project, because of some extra weirdness behind this browser that has only Object.defineProperty from ES5 but it can work with some little effort.

Good news is, I've solved all these problems for you today ;-)

prototypal

I've realized myself the previously discussed Class snippet was way too close to the metal and not good looking for most common cases or patterns, so here I am with a revisited and fully tested, covered, and cross browser and platform version of that Class via the prototypal package, already in npm and bower.
Here an example of how it works:
var Rectangle = Class({
  constructor: function (width, height) {
    this.width = width;
    this.height = height;
  },
  toString: function () {
    return '[object Rectangle]';
  },

  // default properties
  width: 0,
  height: 0,

  // an explicit getter to retrieve the area
  area: Class.descriptor({
    get: function () {
      return this.width * this.height;
    }
  })
});

// extending
var Square = Class( Rectangle, {
  constructor: function (size) {
    Rectangle.call(this, size, size);
  },
  toString: function () {
    return '[object Square]';
  }
});

var s = new Square(3);
s.area; // 9
'' + s; // [object Square]
As extra useful features the lazy property definition patter has been solved in core and tested again everywhere plus another bound feature based internally of the lazy pattern:
var MouseHandler = Class({
  onClick: Class.bound(function (evt) {
    evt.preventDefault();
    alert(this instanceof MouseHandler); // true
  })
});

var mh = new MouseHandler;
document.body.addEventListener(
  'click', mh.onClick
);
Defined via Class.bound utlity, every instance of MouseHandler will have a bound method called onClick but only when accessed instead of at runtime during instance creation.
I think architectures possibilities with these few simple utilities that are fully tested are many, but if there's any very pecific pattern you think would be handy to put in prototypal.Clas package, this is the right time to speak ;-)
Last, but not least, I can humbly say I've done many other attempts in my past to find a good, simple, performant, and as reasonably cross browser/platform as possible without too many surprising behaviors while working on some Classical Inheritance like project and this prototypal feels and look the best, and most likely the last, attempt I could made to date, and it fits in around 700 bytes minzipped, nothing at all considering how much it resolve consistently cross platform.

A Typed Experiment

The old redefine.js and the new born protoypal are successful attempt to dominate ES5 descriptors with ease.
However, once we've mastered how these works, and even on top of any of these utilities, we could find descriptors as the most natural place, ECMAScript speaking, to described properties type or method argumsnt and returned type, maybe enabling overloads and multiple returns check, and in a completely zero performance impact on production code.
Well, defineStrictProperties aim is to enrich native methods in an unobtrusive way bringing types in JavaScript in a way that integrates properly with current specifications, is backward compatible, and it could be used after es5-shim, if necessary, with zero code size and performance problem once in production: you just don't include the file and you are good to go!
The undesired effect? Well, if combined with prototypal.Class, when types are needed/meant, properties should be described via Class.descriptor({ .. object .. }) ... maybe not such a big deal?

// just as shortcut
var as = Class.descriptor;


var Rectangle = Class({
  constructor: function (width, height) {
    this.width = width;
    this.height = height;
  },
  toString: function () {
    return '[object Rectangle]';
  },

  // default typed properties
  width:  as({type: 'number',
              value: 0}),
  height: as({type: 'number',
              value: 0}),

  // a getter
  area: as({type: 'number',
            get: function () {
              return this.width *
                     this.height;
            }
          })

});
Well, as I've said this is just en experiment that has nothing to do with prototypal but it could be easily integrated in your code without effort, since all types are completely optional.

The End

I am not sure if it was worth writing all this about descriptors, but I feel relieved now that everything you could say about ES5 and 5.1 descriptors is described in one of these 4 posts ... of course, unless I've forgotten something, which if the case, please let me know, and I'll update!

Thanks for your patience reading all this!

Friday, March 28, 2014

What Books Didn't Tell You About ES5 Descriptors - Part 3

Once you've read, Part 4 is out ;-)
In Part 2, we have seen few advantages about using descriptors to define classes, let's see the example again:
// basic ES5 class definition
// the constructor
function Rectangle(width, height) {
  this.width = width;
  this.height = height;
}
Object.defineProperties(
  // enriching instead of replacing
  Rectangle.prototype, {
  // a generic method
  toString: {
    value: function () {
      return '[object Rectangle]';
    }
  },
  // a generic getter
  area: {
    get: function () {
      return this.width * this.height;
    }
  }
});

// quick example
var ret = new Rectangle(3, 2);
'' + ret; // [object Rectangle]
ret.area; // 6
So far so good, right? Aren't we missing something? Uh right ...

Extending Via Descriptors

In an ideal world, which is hopefully coming soon thanks to ES6, we could simply relink a generic prototype object instead of replacing it, something like:
// basic ES6 class extend
// the constructor
function Square(size) {
  Rectangle.call(this, size, size);
}
Object.defineProperties(
  // swap inheritance instead of
  // loosing the original prototype
  Object.setPrototypeOf(
    Square.prototype,
    Rectangle.prototype
  ), {
  // extra properties/overrides if needed
  toString: {
    value: function () {
      return '[object Square]';
    }
  }
});

// quick example
var sqr = new Square(3);
'' + sqr; // [object Square]
sqr.area; // 9
Unfortunately there is no Object.setPrototypeOf in ES5, while __proto__ does not appear even once in the specifications.
This is why 5.1 compatible engines like duktape, as example, will not work using the dirty __proto__ and cannot then hot-swap prototypes at runtime at all ... this was ES5.1

Extending the ES5 Way

In the previous post, Matías Quezada commented pointing out a couple of things, where the first one is about the need to reassign the prototype when it comes to extend.
However, what comes natural with ES5 is to redefine the original constructor as not enumerable, which is at least the closest behavior we would expect from a class.
// basic ES5 class extend
// the constructor
function Square(size) {
  Rectangle.call(this, size, size);
}
// the prototype redefined
Square.prototype = Object.create(
  Rectangle.prototype, {
  // but with the right constructor
  // and in a non enumerable way
  constructor: {
    value: Square
  },
  // extra properties/overrides if needed
  toString: {
    value: function () {
      return '[object Square]';
    }
  }
});
Understanding the difference between these patterns is essential, but as developers, we also would like to simplify the task and here I am.

A Basic ES5 Class Utility

The most simple and basic utility we could think of will probably look like the following one:
// simplifying the repeated pattern - v1
function Class(proto, descriptors) {'use strict';
  var extending = descriptors != null,
      d = extending ? descriptors : proto,
      constructor = (
        d.hasOwnProperty('constructor') ?
        d.constructor :
        d.constructor = {value: function Class(){}}
      ).value;
  return (extending ?
    (constructor.prototype = Object.create(
      typeof proto === 'function' ?
        proto.prototype : proto, d)) :
    Object.defineProperties(constructor.prototype, d)
  ).constructor;
}
Above code is quite compact and it provides the ability to rewrite our two example classes in this way:
var Rectangle = Class({
  constructor: {
    value: function (width, height) {
      this.width = width;
      this.height = height;
    }
  },
  toString: {
    value: function () {
      return '[object Rectangle]';
    }
  },
  area: {
    get: function () {
      return this.width * this.height;
    }
  }
});

// two arguments to extend
var Square = Class(
  Rectangle, {
  constructor: {
    value: function Square(size) {
      Rectangle.call(this, size, size);
    }
  },
  toString: {
    value: function () {
      return '[object Square]';
    }
  }
});
At least now we have a single way to define classes through descriptors ... what else could we do?

Descriptors VS Readability

The second point that Matías Quezada made in his comment was indeed about descriptor verbosity, which I believe goes back to the very first part of these posts:

@WebReflection @antirez property descriptors are terrible :( ...

— TJ Holowaychuk (@tjholowaychuk) March 20, 2014
If we believe these are good assumptions:
  1. we still want the ability to define getters and setters
  2. properties, if specified, are there to be marked as immutable, like constants, rather than defaults
  3. we want to use code that is less ambiguous as possible
  4. accordingly, properties with a shared default needs to be explicitly flagged as writable
  5. but function, for a class, will always be considered method of the class
Wondering what is this about? Let's see how we can make the code less verbose, still elegant, and never ambiguous:
var Rectangle = Class({
  constructor: function (width, height) {
    this.width = width;
    this.height = height;
  },
  toString: function () {
    return '[object Rectangle]';
  },
  area: {
    get: function () {
      return this.width * this.height;
    }
  }
});

var Square = Class(
  Rectangle, {
  constructor: function (size) {
    Rectangle.call(this, size, size);
  },
  toString: function () {
    return '[object Square]';
  }
});
In order to reach above improved state, the original Class required some make up:
// simplifying the repeated pattern - v2
var Class = (function(){'use strict';
  function descriptify(d, k) {
    return  typeof d[k] === 'function' &&
            (d[k] = {value: d[k]}), d;
  }
  function Class(proto, descriptors) {
    var d, extending = descriptors != null,
        d = Object.keys(
          d = extending ? descriptors : proto
        ).reduce(descriptify, d),
        constructor = (
          d.hasOwnProperty('constructor') ?
          d.constructor :
          d.constructor = {value: function Class(){}}
        ).value;
    return (extending ?
      (constructor.prototype = Object.create(
        typeof proto === 'function' ?
          proto.prototype : proto, d)) :
      Object.defineProperties(constructor.prototype, d)
    ).constructor;
  }
  return Class;
}());
We are still around 238 bytes minzipped so it's not a big deal and probably the simplest Class you can play around defining methods or, when it's necessary and understanding how, shared properties through descriptors.
If this is not enough, and we'd like to have a complete solution entirely based on ES5 and with extra patterns described in these posts too such the lazy reassignment, redefine.js is a good playground too, and you can see few examples and compare with above proposal which once again is very minimalistic, not so fancy in features, but surely fast, reliable and efficient, most likely all we need for our projects.

About Non Standard Behaviors

Warning
From now on, everything we'll explore will NOT be what we need to write on daily basis. Actually, most of the following alchemies are patterns that we'll never need in our life as coders.
However, being these post about telling you all things that books forgot to mention, I could not help myself going deeper in most dark and obscure details, proposing patters yu probably never cared about as the lazy assignment could be ... take the rest of this post as extra details, and keep thinking about what ES5 offers remembering this snippet

It has already been explained in Part 1 that writable properties cannot be directly redefined.
var Person = Class({
  name: { // as default
    value: 'anonymous'
  }
});

var me = new Person();
me.name = 'ag';
me.name; // anonymous
What I haven't told you yet, and this is still an active discussion/concern in es-discuss, is that in V8 and current node.js, constructors have super powers
var Person = Class({
  constructor: function (name) {
    // here it's writable anyway
    this.name = name;
  },
  name: { // as default
    value: 'anonymous'
  }
});

var me = new Person('ag');
me.name; // ag
Why we might think that's cool and ideal, not only this behavior is not adopted by other JS engines, is also kinda pointless since usually properties set in the constructor don't need a default, these will be replaced in there anyway so ... how about we just don't specify the default if we set it during initialization anyway?
var Person = Class({
  constructor: function (name) {
    this.name = name || 'anonymous';
  }
});

var me = new Person('ag');
me.name; // ag
If we really need a default, the better thing we can do is to specify the property as writable.
var Person = Class({
  constructor: function (name) {
    if (name) {
      this.name = name;
    }
  },
  name: {
    writable: true,
    value: 'anonymous'
  }
});

var me = new Person('ag');
me.name; // ag

The Dirty V8 Behavior

When I've talked about super powers, I didn't mention the super shenanigans too. As soon as something external "touches the freshly baked instance", the magical [[Set]] behavior breaks again.
var WTF1 = Class({
  constructor: function (tf) {
    this.what = tf;
  },
  what: {value:'nope'}
});

var WTF2 = Class({
  constructor: function (tf) {
    this.what = tf;
    // so far, so good, right ? ... now
    // this is a perfectly legit operation
    Object.getOwnPropertyDescriptor(this, 'what');
  },
  what: {value:'nope'}
});

var wtf1 = new WTF1('WTF'),
    wtf2 = new WTF2('WTF');

wtf1.what; // WTF
wtf2.what; // nope
I do hope that this madness will be standardized somehow ... I mean, even just passing the instance to Object will break as well ... anyway ...

Getters And Setters Bug

Forget V8 and most modern node.js, this time it's Androind 2.x and webOS time!
While the latter one is basically disappeared from the web scene, and I personally think it's a pity since my Pre 2 is almost fully ES5.1 spec compliant, better than any IE < 10, the first one is still widely used around the world, also quite cheap so usually a preferred choice in emerging markets.



As we can see in the dashboard, 20% or more is still a big amount of mobile Android platform users, and here the bug they'll be dealing with in ES5:
var hasConfigurableBug = !!function(O,d){
  try {
    O.create(O[d]({},d,{get:function(){
      O[d](this,d,{value:d})
    }}))[d];
  } catch(e) {
    return true;
  }
}(Object, 'defineProperty');
If hasConfigurableBug is false, it means that a lazily assigned property can be reconfigured without problems:
var Unconfigurable = Class({
  lazy: {
    get: function () {
      // here do amazing things
      // then reconfigure once the property
      // this.lazy = value; won't work
      // because we are inheriting get/set behavior
      // the only option is this one:
      Object.defineProperty(this, 'lazy', {value:
        // the assigned once property
        Math.random()
      });
      return this.lazy;
    }
  }
});

var rand = new Unconfigurable;

rand.lazy; // 0.5108735030516982
rand.lazy; // still same value:
           // 0.5108735030516982
However, if hasConfigurableBug is true, that operation will throw an error saying that is not possible to configure a property that has a getter or setter.
In few words, the bug is about defineProperty, for some reason unable to reconfigure what has been inherited, if this contains either a getter or a setter, or both.
Unfortunately, this bug has been a widely adopted in old mobile WebKit, so back to the initial feature detection, here is how we could obtain the same behavior in these browsers too:
var Unconfigurable = Class({
  lazy: {
    // needs eventually to be deleted later on
    configurable: hasConfigurableBug,
    get: function () {
      if (hasConfigurableBug) {
        var descriptor = Object.getOwnPropertyDescriptor(
          Unconfigurable.prototype, 'lazy');
        // remove it ...
        delete Unconfigurable.prototype.lazy;
      }
      // ... so that this won't fail
      Object.defineProperty(this, 'lazy',
        {value:Math.random()});
      if (hasConfigurableBug) {
        // "first time ever var makes sense" ^_^
        Object.defineProperty(
          Unconfigurable.prototype, 'lazy', descriptor);
      }
      return this.lazy;
    }
  }
});

var rand = new Unconfigurable;

rand.lazy; // 0.5108735030516982
rand.lazy; // still same value:
           // 0.5108735030516982
I know, this one is tough to digest, and that's why I've mentioned before redefine.js, however it's clear now how eventually solve the problem in a tiny feature detection that won't compromise performance.
There is still something we might want to do, partially to make latest snippet portable without accessing manually to a known prototype, partially to complete this 3rd post on descriptors ...

getPropertyDescriptor

Usually inheritance examples comes with 2 basic levels, but what if we inherited a behavior 3 or 4 levels up?
Here a very simple utility that will return undefined, or an ancestor descriptor with a object property that will point to the ancestor itself, property borrowed from Object.observe current proposal.
function getPropertyDescriptor(object, key) {
  do {
    // get the decriptor, if any
    var descriptor = Object.getOwnPropertyDescriptor(
      object, key);
    // otherwise if there is inheritance ... try again
  } while(!descriptor && (
    object = Object.getPrototypeOf(object)
  ));
  if (descriptor) {
    // set the target object
    descriptor.object = object;
  }
  return descriptor;
}
With this utility, the previous snippet of code would look slightly better:
var Unconfigurable = Class({
  lazy: {
    configurable: hasConfigurableBug,
    get: function () {
      if (hasConfigurableBug) {
        var descriptor = getPropertyDescriptor(
          this, 'lazy');
      }
      Object.defineProperty(this, 'lazy',
        {value:Math.random()});
      if (hasConfigurableBug) {
        Object.defineProperty(
          descriptor.object, 'lazy', descriptor);
      }
      return this.lazy;
    }
  }
});
To be brutally honest, in case we have multiple getters behavior redefined up the chain, above code won't solve much since there could be another descriptor up there ... oh well, I hope we'll never find ourself redefining lazy properties, instead of simple getters, more than once per inheritance chain.
In any case, for completeness, here the plural version of the function, where all descriptors are returned at once with all object references.
function getPropertyDescriptors(object) {
  var descriptors = {},
      has = descriptors.hasOwnProperty;
  function assign(name) {
    if (!has.call(descriptors, name)) {
      descriptors[name] =
        getPropertyDescriptor(object, name);
    }
  }
  do {
    (Object.getOwnPropertyNames ||
      Object.keys)(object).forEach(assign);
  } while(object = Object.getPrototypeOf(object));
  return descriptors;
}

Almost Completed

The Part 4 will come too and it will be about IE8 possibilities and some extra possibility we could have through descriptors. However, it was really essential to understand all potentials, pros and some weird cons, so that we can appreciate even more the next and final part of these posts.