JavaScript Has No Class
Prototypes, not classes
Define a "class"
function Food () { // Define an instance variable this.calories = 100; }
Doesn't "say what it means" (looks like a function)
Non-intuitive to newcomers
Compared to:
class Food { }
Define a "method"
VirtualPet.prototype.setCalories = function (newCalories) { }
Requires an understanding of:
Compared to:
class Food { function setCalories (newCalories) { } }
Define an "instance variable"
function Food () { this.calories = 100; }
No central variable definition location
Code sprawl, hard to read, hard to maintain
Compared to:
class Food { var calories = 100; }
Inheritance
Extend a class
SubClass.prototype = new SuperClass();
Compared to:
SubClass extends SuperClass { }
Invoke superclass constructor
function SubClass () { SuperClass.call(this, arg1, arg2); }
Compared to:
function SubClass () { super(arg1, arg2); }
Invoke overridden method
SubClass.prototype.overriddenMethod = function () { SuperClass.prototype.overriddenMethod.call(this); }
Compared to:
override function overriddenMethod () { super.overriddenMethod(); }
Inheritance: Little Problems
SubClass.prototype = new SuperClass();
SubClass.prototype.constructor
SuperClass
, which might have side effectsNew JavaScript programmers:
Inheritance: Workarounds
One solution (based on Google Closure's inherits()
)
function extend (subclass, superclass) { function superclassConstructor () {}; superclassConstructor.prototype = superclass.prototype; subclass.superclass = superclass.prototype; subclass.prototype = new superclassConstructor(); subclass.prototype.constructor = subclass; };
Not readable; shouldn't be required in the first place
Popular libraries offer other OOP utils
Tower of babel problem: JS OOP dialects reduce skills transfer
Which "solution" do you trust?
// Google's original goog.inherits = function(childCtor, parentCtor) { /** @constructor */ function tempCtor() {}; tempCtor.prototype = parentCtor.prototype; childCtor.superClass_ = parentCtor.prototype; childCtor.prototype = new tempCtor(); childCtor.prototype.constructor = childCtor; };
Inheritance Workaround:
Ext.js Example
extend : function(){ // inline overrides var io = function(o){ for(var m in o){ this[m] = o[m]; } }; var oc = Object.prototype.constructor; return function(sb, sp, overrides){ if(typeof sp == 'object'){ overrides = sp; sp = sb; sb = overrides.constructor != oc ? overrides.constructor : function(){sp.apply(this, arguments);}; } var F = function(){}, sbp, spp = sp.prototype; F.prototype = spp; sbp = sb.prototype = new F(); sbp.constructor=sb; sb.superclass=spp; if(spp.constructor == oc){ spp.constructor=sp; } sb.override = function(o){ Ext.override(sb, o); }; sbp.superclass = sbp.supr = (function(){ return spp; }); sbp.override = io; Ext.override(sb, overrides); sb.extend = function(o){return Ext.extend(sb, o);}; return sb; }; }()
Uhh...
Do you really want to read or write code like that? I sure don't...
"Expressiveness"
JavaScript is flexible at runtime
You can make an apple meow:
function Apple () { } function Cat () { } Cat.prototype.meow = function () { alert("Meow!"); }; var apple = new Apple(); apple.__proto__ = Cat.prototype; apple.meow();
But you shouldn't
Mutating Individual Instances
You can add variables and methods to a single object
var fruit = new Food(); fruit.age = 0; fruit.isRipe = function () { return (this.age > 20000); }
But please don't
Everything's a Nail
Who needs classes anyway?
We can just use the "triangle-monocle-mustache class pattern"...
The let-triangle-function-prototype-monocle-mustache-constructor Pattern
const px = Name.create('x'), py = Name.create('y'); let Point = Base <| function (x, y) { super(); this[px] = x, this[py] = y; this.r = function() { return Math.sqrt(x*x + y*y); } }.prototype.{ get x() { return this[px]; }, get y() { return this[py]; }, proto_r() { return Math.sqrt(this[px] * this[px] + this[py] * this[py]); }, equals(p) { return this[px] === p[px] && this[py] === p[py]; } }.constructor.{ allPoints: [] // class "static" property! }
<| means copy object to __proto__
.{ means copy right object's own properties to left object
Say what you mean
"Classes is a clear case of 'Say what you mean!' whereas the let-triangle-function-prototype-monocle-mustache-constructor pattern is more like 'I know how the internals work' which is hardly something we should be pushing for." ~Erik Arvidsson on es-discuss
The Web's Language Has to be Flexible
Programmers need to be able to shape JS to their needs
Future is unknown
Past must be upgradable, fixable, maleable
But developers need more, better high-level structures
No Type Annotations: No Compiler Errors
No compiler, so no compiler error for this:
var apple = new Appl();
Or this:
var apple = new Apple(); apple = new Car();
Bugs go undetected until the code runs
Huge reduction in reliability
Must run every line of code to find all the errors
...YOU MUST RUN EVERY LINE OF CODE to find all the errors
No Compiler Errors, Another Example
Imagine "bar" must be an array:
function foo (bar) { alert(bar[0]) }
Correct input:
foo(["abc"]); // Displays "abc"
Erroneous input without type annotations:
foo("abc"); // No error. Displays "a". VERY HARD to debug.
Erroneous input with type annotations:
foo("abc"); // Error. VERY EASY to debug.
(Type inference won't help: can't determine whether "abc" or ["abc"] is correct)
Typos Suck
Can you spot the problem?
A computer could instantly
No Compiler Errors, Yet Another Example
Define a variable twice:
var DEFAULT_WIDTH = 400; var DEFAULT_WIDTH = 800;
No warning, works even at runtime!
Need I Go On?
Call a superclass's constructor twice:
SomeSuperConstructor.call(this); SomeSuperConstructor.call(this);
No warning, works even at runtime!
vs ActionScript
A super statement cannot occur after a this, super, return, or throw statement.
vs Java
Constructor call must be the first statement in a constructor.
Judge The Language By Its Error Messages
I often say "you can judge a programming language by its error messages"
In many cases, JavaScript doesn't even have them
Would *YOU* Let JavaScript Land Your Plane?
Reliability is not optional in large-scale, mission-critical programs
No Type Annotations: Bad Tooling
No code hinting (aka intellisense, code completion)
No code hyperlinks (e.g., cmd+click on a method reference to jump to its definition)
No automated refactoring (just error-prone search/replace)
Poor auto-generated documentation (jsdoc-toolkit et al try their best, but none are perfect)
Life With Metadata (ActionScript)
public function createRoom (roomID:String, roomSettings:RoomSettings, attributes:XML, modules:RoomModules):Room { messageManager.sendUPC(UPC.CREATE_ROOM, roomID, roomSettings.serialize(), attrArg, modules.serialize()); addCachedRoom(roomID); return getRoom(roomID); }
All parameters are known and ENFORCED EARLY
With Metadata
Early errors
Visual code model
Inline docs
Code completion
Code hints
Life Without Metadata (JavaScript)
net.user1.orbiter.RoomManager.prototype.createRoom = function (roomID, roomSettings, attributes, modules) { msgMan.sendUPC(net.user1.orbiter.UPC.CREATE_ROOM, roomID, roomSettings.serialize(), attrArg, modules.serialize()); this.addCachedRoom(roomID); return this.getRoom(roomID); };
No parameters are known
Argument validation is late, if at all; burden on the developer
How does the developer know what roomSettings is?
Without Metadata
If ActionScript Were an MP3 Player
If JavaScript Were an MP3 Player
The Word According to Carmack
On compile-time error checking:
http://youtu.be/4zgYG-_ha28?t=1h7m20s
"No matter how good you think you are, you are making mistakes all the time and you have to have structures around you to try to help you limit the damage that your mistakes will cause, find them as early as possible so that you can correct them, and the earliest possible time is to find them at compile time."
On dynamic languages:
http://youtu.be/00Q9-ftiPVQ?t=15m23s
"My god how can you write a real program where you're just assigning random shit to other shit and expecting it to work"
The Creator of JavaScript's Response
Discussion with Brendan Eich, creator of JavaScript
Hard to add static typing because "Dynamic code loading can invalidate static type judgments"
"Classes have been challenging to retrofit...but I'm patient and hopeful."
"Developers do not agree on which kind of classes, with what exact semantics"
"There ought to be many variant dialects, analogous to Racket's module-wise language support."
Guards are proposed [ed: seems dead. last updated May 2011.]
"I question static types for JS in light of DoctorJS and hybrid type inference"
"I'm not arguing that static analysis is the same as type checking with type annotations, but we aren't going to have static types in JS soon or easily."
No Dependency Management
No built-in way to manage dependencies between loaded code libraries
Developers must:
Modules finally coming in ECMAScript 6 (2014?)
Global Scope
Ugly hacks required to prevent names from leaking into the global scope:
(function($){ $('body').hide(); })(jQuery);
Cross-browser Compatibility
Multiple runtimes (Chrome, Firefox, Safari, Internet Explorer)
ActionScript...
stage.stageHeight
...versus JavaScript
// From: http://andylangton.co.uk/articles/javascript/get-viewport-size-javascript/ var viewportwidth; var viewportheight; // Standards compliant browsers (mozilla/netscape/opera/IE7) if (typeof window.innerWidth != 'undefined') { viewportwidth = window.innerWidth, viewportheight = window.innerHeight } // IE6 in standards compliant mode (i.e. with a valid doctype // as the first line in the document) else if (typeof document.documentElement != 'undefined' && typeof document.documentElement.clientWidth != 'undefined' && document.documentElement.clientWidth != 0) { viewportwidth = document.documentElement.clientWidth, viewportheight = document.documentElement.clientHeight } // older versions of IE else { viewportwidth = document.getElementsByTagName('body')[0].clientWidth, viewportheight = document.getElementsByTagName('body')[0].clientHeight }
Cross-browser Compatibility Costs
Added complexity to:
Libraries to the Rescue?
Libraries solve (some of) JavaScript's problems (see jQuery, protoype, mootools, dojo, raphael, easel.js, excanvas, ie7.js, sencha, etc, etc)
But:
JS + HTML5 is Costly to Develop
Can anything be done?
Classes and guards in JS eventually
While we wait for TC39...
Start Over: Google Dart
"Javascript has fundamental flaws that cannot be fixed merely by evolving the language"
According to Google: "Web Programming has huge disadvantages"
Hence, http://www.dartlang.org
Native in Chrome, compiles to JavaScript
Dart Sample
class Greeter { String prefix = 'Hello,'; greet (String name) { print('$prefix $name'); // String interpolation } } void main () { Greeter greeter = new Greeter(); greeter.greet('types'); }
Responses to Dart
Brendan Eich (creator of JavaScript)'s reaction on Hacker News
More opinions by...
Cross Compile: Google Closure
http://code.google.com/closure
Compiles "JavaScript to better JavaScript"
Annotate in comments
Gmail, Maps, Docs
/** * Some class, initialized with an optional value. * @param {Object=} opt_value Some value (optional). * @constructor */ function SomeClass(opt_value) { /** * Some value. * @type {Object|undefined} */ this.myValue = opt_value; } /** * Sets the object's creation time. * Considered protected and final. * @param {Date} date The creation time. * @protected */ SomeClass.prototype.setCreationTime = function (date) { // ... };
Cross Compile: Google GWT
http://code.google.com/webtoolkit/
Compiles Java to JavaScript
Adwords, Orkut, Wave
Cross Compile: CoffeeScript
Compiles "cleaner JavaScript" to JavaScript
class Animal constructor: (@name) -> move: (meters) -> alert @name + " moved #{meters}m." class Snake extends Animal move: -> alert "Slithering..." super 5 class Horse extends Animal move: -> alert "Galloping..." super 45 sam = new Snake "Sammy the Python" tom = new Horse "Tommy the Palomino" sam.move() tom.move()
Cross Compile, OOP Libraries
Jangaroo (ActionScript 3.0 subset to JavaScript)
Haxe (ActionScript 3.0-superset JavaScript or ActionScript 3.0)
Ext JS (free for open-source projects)
Which One's Right?
Dart, Closure, GWT, CoffeeScript, Jangaroo, Haxe, Ext, etc
Stick with plain old JS?
Coping With JavaScript:
Small Projects
On small projects (under 1000 lines):
Coping With JavaScript:
Large Projects
On large projects (over 10,000 lines):
Union AS3 -> JS Port
Union: Development platform for creating connected applications
Orbiter, Union JavaScript library
20,000 lines of code
Coping With JavaScript:
Toolchain
Aptana 2 (NOT 3; 3's code assist is flaky)
Subclipse/Subversion (Git/Github would work too)
YUICompressor (reduce file size)
Apache Ant (combine many .js files into one long file)
Unfuddle (bug tracking/project management)
Why It's Worth It
Who would be sadistic enough to code in JavaScript?
Why JS #10: You Have No Choice
There is no other native language in the browser (yet)
Why JS #8: Huge Community
Tons of support, libraries, examples
4th most popular topic on stack overflow
Why JS #7: Searchable
Plain text, Google can index it
Why JS #6: Source Included
App always includes source
Never "stuck" with a binary
Incredible learning tool
Why JS #5: Forward-compatibility
Only platform committed to indefinite forwards-compatibility
Windows, Mac, Java, Flash all deprecate ("End of life")
Preserves web as a historical record
Imagine viewing web pages by ancient Romans
Forward-compatibility Example:
<blink>: invented in 1994, shipped in Netscape 1.0
Non-standard
Considered harmful, widely hated
Still works to this day in Firefox
If Firefox didn't support <blink>, you could add it
Compare with desktop
Compare with Flash
Why JS #4: Hotfixable
Language itself can be "polyfilled" (updated, patched) by the developer
Array.prototype.toString = function () { return "A list of values"; }; var a = [1, 2, 3]; alert(a.toString());
Make new apps run in old browsers
All can contribute/expand/build/fix
Why JS #3: Open Platform
Entire platform based on standards not owned by a single corporation
Every individual has the power to control the content renderer
Why JS #2: Do It for Brendan
Why JS #1: It works on iPad ; )