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:

  • Constructor functions
  • Functions creating objects
  • Prototype object
  • Prototype chain
  • 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();

  • Overwrites SubClass.prototype.constructor
  • Invokes SuperClass, which might have side effects
  • New JavaScript programmers:

  • Don't know about these issues
  • Don't know how to fix them (requires expert knowledge)
  • Forces language users to implement low-level language features
  • 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)

  • Reduced productivity
  • No code hyperlinks (e.g., cmd+click on a method reference to jump to its definition)

  • Reduced productivity
  • No automated refactoring (just error-prone search/replace)

  • Reduced productivity
  • Poor auto-generated documentation (jsdoc-toolkit et al try their best, but none are perfect)

  • Reduced developer support
  • 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?

  • Look it up in the docs; hope they're right
  • Read the source if available
  • 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."

  • TC39 is taking yet another stab at minimal classes
  • "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:

  • Research a module framework (e.g., Require.JS, Common.JS)
  • Or build their own module framework (days or weeks to get right)
  • 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);

  • Doesn't "say what it means"
  • Purpose is completely unintelligible to newcomers
  • Undocumented (not mentioned on mozilla.org)
  • 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:

  • Research (what works where?)
  • Development (more code)
  • Testing/QA (8+ test targets across mac/win)
  • Maintenance (browser API changes)
  • 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:

  • Library selection takes research time
  • Suitable library might not exist (research/production dead ends)
  • Larger download
  • Library bugs
  • Library abandonment
  • Unknown performance impact
  • Unknown reliabilty
  • Possible cross-library interoperability failures
  • Externalizes developer code-familiarity
  • Introduces "library lock" ("stuck with it": can't easily switch technology)
  • Increases testing surface
  • Tower of babel problem: library dialects reduce skills transfer
  • JS + HTML5 is Costly to Develop

    Can anything be done?

    Classes and guards in JS eventually

  • But still no concensus
  • While we wait for TC39...

    Start Over: Google Dart

    "Javascript has fundamental flaws that cannot be fixed merely by evolving the language"

  • Leaked memo by Google's Mark Miller
  • According to Google: "Web Programming has huge disadvantages"

  • Lack of program structure
  • Unpredictable performance
  • Infinite Install (every run is an install) => slow startup
  • Poor tooling
  • Very poorly suited for software engineering
  • Lack of standard libraries - collections, MVC, Low-level client/server communication
  • Very hard to create large applications
  • 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'); }

    "Dart vs JavaScript" syntax examples

    Responses to Dart

    Brendan Eich (creator of JavaScript)'s reaction on Hacker News

  • Proprietary language threatens open web ("Works only in Chrome")
  • More Hacker News reaction

    Lambda the Ultimate reaction

    More opinions by...

  • Peter Bright
  • Jesper Andersen
  • 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

    http://coffeescript.org/

    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

  • Which will last?
  • Which will become the dominant technology?
  • Stick with plain old JS?

  • Most future proof
  • Safest skills investment
  • Perfect for small projects
  • Most costly for programming in the large
  • Coping With JavaScript:
    Small Projects

    On small projects (under 1000 lines):

  • Use a "Bag of Functions" (one .js file with a bunch of vars and functions)
  • Flat or no class hierarchy
  • Widgets as .html files in iframes
  • jQuery for DOM access and effects
  • Coping With JavaScript:
    Large Projects

    On large projects (over 10,000 lines):

  • Think in "classes", not functions
  • One "class" per .js file
  • Join .js files together with a build tool (e.g., Ant)
  • Always consider writing your own libraries focussed on your exact needs
  • Write unit tests
  • 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)

    JSDoc-toolkit

    Unfuddle (bug tracking/project management)

  • Other free options: Trac, Redmine
  • 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 #9: Good Free Tools

    Aptana

    Komodo Edit

    Netbeans

    JSDT

    JSHint

    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>

    <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

  • Windows 7 won't run a Win 95 app
  • OSX won't run an OS9 app
  • Compare with Flash

  • This presentation viewer doesn't work in Flash Player 8 or higher
  • Flash Player 7 Standalone doesn't work on Lion
  • 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

  • IE7.js
  • excanvas
  • 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 ; )