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

Saturday, April 27, 2013

Few Modern JavaScript Inconsistencies

While the JavaScript weekly mailing list still points to 90s style articles about the old JavaScript Internet Explorer 5 was supporting too, the current world has different real problems to consider.
Here a quick list of things you might not know about the current status of JavaScript possibilities.

Reserved Words As Properties

Number one of the list is the myth that reserved words cannot be used as properties. Here platforms that cannot:

  1. iOS 4
  2. IE less than 9
  3. Android less than 2.1

So, basically, 99% of mobile browsers support properties such obj.delete(), used in most recent JavaScript specifications, while those jurassic browsers need the obj['delete']() convention.

// exactly same good old ES3 behavior
// using ES5 capabilities
Function.prototype.new = function () {
  var
    // grab the prototype
    p = this.prototype,
    // create from it
    o = Object.create(p),
    // invoke the real constructor
    r = this.apply(o, arguments);
  // if the result is not undefined or null
  // and the returned value is an object/function
  // overwrite the result
  return r != null && (
    typeof r === 'object' ||
    typeof r === 'function'
  ) ? r : o;
};

// you can be Rubyish now ^_^
var instance = MyClass.new(1, 2, 3);

// P.S. if you have problems with JSLint
// and the r != null part in above snippet
// it's just time for you to upgrade to JSHint

Please Do Not Support Too Old Browsers

As easy as that. As a developer, company, software provider, whatever, you are trapping yourself behind problems that will never be fixed in those browsers and you are limiting your customer too, embracing development for such old environment, instead of promoting an update that will benefit them in terms of both potentials, expenses, and security.
Any company that will say no to that should be kindly be abandoned, IMHO, they're already out of web/JS business and they don't realize yet.
I understand some graceful measurement should be taken in order to migrate old users, but as long as they feel confortable, they won't migrate sooner for sure.
Customers or people we'd like to let them access our service, should be informed somehow of new possibilities too.

Apple Drops 3 Years Old Software Too

If Apple not accepting non retina software anymore is not enough as an argument, think how many possibilities you are dropping to your software in order to work the same in those old browsers.
You chose Web technologies, you should catch up with these, end of the story.

Object.defineProperty() *Is* Available

Even my Palm Pre 2 webOS supports Object.defineProperty(), together with Object.defineProperties(), Object.getPrototypeOf() and Object.getOwnPropertyDescriptor()!
If you don't want to deal with all this verbosity but you like the power behind, redefine is really your best friend then!

The most widely adopted list of ES5 features down to Android 2.1 phones and webOS are:

  1. Object.create()
  2. Object.defineProperty()
  3. Object.defineProperties()
  4. Object.getOwnPropertyNames()
  5. Object.getOwnPropertyDescriptor()
  6. Object.getPrototypeOf()

Things like Object.freeze() might have been introduced later on so don't trust them ... but, whenever you wanna try that:

var freeze = Object.freeze || Object;
freeze({}); // frozen where possible

function returnFrozen(object) {
  return (Object.freeze || Object)(object);
}

As it is for "use strict"; and all other things that works best natively, above technique will work with Object.seal(), Object.preventExtensions(), and why not, a shimmable Object.isExtensible()

'isExtensible' in Object || (function(){
  // no way ES3 can prevent extension so ...
  Object.isExtensible = function (object) {
    // ... if an object, it's extensible
    return object != null && (
      typeof object === 'object' ||
      typeof object === 'function'
    )
  };
}());

Function.prototype get caller() {return WTF}

Generally speaking the caller property works since ever but there are cases where it does not and this is iOS5 and lower fault.
What am I talking about? About caller over getters, with or without __defineGetter__ old style approach, the new one fails too ^_^
Bear in mind iOS 5.1 and 6.0+ are just fine so you can still use that magic, if needed.
Note a part, that magic ain't disappearing any time soon so ... go on, use caller until there is an alternative: so far, not a single one ^_^

Function.prototype.bind()

It took literarily ages for WebKit to adopt this method so this is something available in all modern browsers but most likely not available with not so old mobile one: easy shim from callerOf!

(function (P, l) {
  'use strict';
  if (!P.bind) {
    P.bind = function (s) {
      var
        c = this,
        a = l.call(arguments, 1);
      return function bind() {
        return c.apply(s, a.concat(l.call(arguments)));
      };
    };
  }
}(Function.prototype, [].slice));

This is the kind of code that I would like to see in CDN, not just 50K libraries for client sake!

Avoid __proto__

Not only conceptually an error and used only to gain some arguable performance boost, __proto__ is absolutely something that IE 10 and 9 will never have in a consistent way.

If you want to transform a list into an array, just var slice = Function.call.bind([].slice); so that you can slice(whatever, optionalIndex) ALL the things, right? The bind() is there and costs nothing ... just use it!

Better Than Zepto

What this library is doing, except from ignoring IE as a Mobile browser, is a poor/quick&dirty design/convention to obtain a prototype swap instead of initializing things in the right way.
The previously linked code could be represented by exactly the same syntax:

// an IE9 and 10 compatible zero bullshit Zepto core
var emptyArray = [];
zepto.Z = function(dom, selector) {
  var result = Object.create($.fn);
  emptyArray.push.apply(
    result, dom || emptyArray
  );
  result.selector = selector || '';
  return result;
}

While performance might not be that good in some engine, and everybody knows that you should never $('select') twice per collection so actually, considering above snippet goes 400.000 objects per seconds, that's not a big/real deal at all!
If that is, I tell you something else is wrong in the app logic!
In any case, actually, there's some mobile platform there, those Zepto thinks is supporting, that scores more with lower results than with a prototype swap, which is the most common selector case, BlackBerry 10 is 8 thousands operations per seconds there compared with __proto__
Thomas Fuchs has been so nice in his repository I cannot even push/contributes these improvements ... surely he would get this one as an insult too, isn't it?

A Swap Oriented __proto__ Attempt

Assuming you still want to swap runtime classes because you cannot define a proper inheritance upfront, here a broken attempt to do that in IE8 and lower:

object = dunder(object, proto);

what's dunder()

dunder() is my attempt to bring a friendly cross platform way, included older IE, to swap proto at runtime.
It requires an assignment so, back to Zepto example, return dunder(dom || [], $.fn) would be all you need to make it work everywhere.

JSON.stringify(object, *replacer*)

This is Safari specific gotcha, and it's about the replacer.
While specs say that Let newElement be the result of calling the abstract operation Walk, passing val and **ToString**(I), Safari will send to the receiver the number 0 instead of the string '0'.
What's the big deal here? That in JavaScript, 0 == false while '0' == true so ... if you have this kind of check in your receiver thinking that if empty, nothing should be done:

if (key) {
  // parse your value
}

Improve that check with if (key === ''), probably the only place on earth where JSLint would have helped you for real instead of messing up your own code.

And for today, that's all folks!

7 comments:

Gabriele Romanato said...

The problem with old browsers and modern ECMAScript features is that there are still many users out there who are currently surfing the web with one of those relics. When I spoke to Repubblica's developers last October I was shocked by the fact that their stats still showed a significant percentage of IE6's users (about 13%). As a developer I agree with Ian Hickson when he said that we should code to the standards, not to the lowest common denominator (guess what it is?). As a freelance, my problems are still related to what clients consider a priority, namely an impossible cross-browser compatibility that forces us to write rubbish code just to not break some (one?) browsers. :-)

Andrea Giammarchi said...

OK, 13% IE6 is simply insane but indeed this is a problem and I've suggested to make these users aware.

Italy, however, is similar to China, pirated Windows XP is a classic so I still don't buy that.

If all sites start suggesting Chrome, as example, or Chrome Frame, whenever IE is less than 9, users will start get it and will be, indeed, a graceful migration.

If you keep supporting 13% of IE because of that number, those users will never bring you business value plus you spend more and you limit yourself to supporto those users.

This latter concept never worked here in California, but Italy is indeed 36 place about IT and Web progress, right? Well, that's Repubblica and all Web Developers fault too.

Chrome is free, for gosh sake!

Gabriele Romanato said...

God Bless California! :-)

Andrea Giammarchi said...

What I mean, is ttat nobody cares to put a bloody "your flash player is not updated" in any website, but the browser not updated is an ignored problem? I don't like/promote/support that from any point.

If you care about the web, you should care about informig your users they cannot have the web now because they are way behind modern possibilities.

Let them switch, and they will thank you and the day after, forget about old IE ... "they can drag and drop files in the email", can you imagine?

Andrea Giammarchi said...

here the global stats about IE, version 6 does not, and should not, matter anymore right NOW!

Unknown said...

But IE8 is still widely used. And IE8 does not support a lot of the nice ES5 features, even shen shimmed.

So until IE8 is dead too, all the "properties" stuff is unfit for most of us, no matter what Apple does.

Andrea Giammarchi said...

and once again, Please Do Not Support Too Old Browsers paragraph is your best mantra ^_^