JavaScript for the
Java Developer
TJ VanToll
March 20th, 2012
TJ VanToll
March 20th, 2012
You should probably feel really sorry for them.
ECMAScript was always an unwanted trade name that sounds like a skin disease.
Pretty much the only thing Java and JavaScript share is C style styntax.
//JavaScript for (var i = 0; i < foo.length; i++) { if (bar) { doSomething(); } else { doSomethingElse(); } }
//Java for (int i = 0; i < foo.length; i++) { if (bar) { doSomething(); } else { doSomethingElse(); } }
Java is strongly typed.
Integer foo = 8; String bar = "hello";
JavaScript is loosely typed.
var foo = 8, var bar = 'hello', foo = bar = {};
Special types of Objects
JavaScript uses type coercion whenever it needs to perform an operation on two operands that need to be the same type and are not.
8 == "8" //true
Relying on type coercion is not a good idea.
null == undefined //true false == undefined //false false == "false" //false '' == '0' //false 0 == '' //true 0 == '0' //true
To avoid type coercion use the === and !== operators.
8 === "8" //false null === undefined //false false === undefined //false false === "false" //false '' === '0' //false 0 === '' //false 0 === '0' //false
Numbers in JavaScript are "double-precision 64-bit format IEEE 754 values".
0.1 + 0.2 == 0.3; //false
You can use parseInt to convert Strings to "Integers"
parseInt("20", 10); //20
A special value, NaN ("Not a Number") is returned if the string is non-numeric.
parseInt("ABC", 10) //NaN
Infinity
JavaScript has no character type.
'bar'.replace('a', 'b'); //'bbr' 'bar'.split('a'); //['b', 'r']
JavaScript objects are simply collections of name value pairs.
//JavaScript var foo = {}; foo.bar = 'hi'; console.log(foo.bar); //"hi" console.log(foo['bar']); //"hi"
//Java Map<String, Object> foo = new HashMap<String, Object>(); foo.put("bar", "hi"); System.out.println(foo.get("bar")); //"hi"
You can retrieve members of the object using dot or bracket notation.
var dog = { name: 'Rover', bark: function() { return 'woof'; } }; dog.name; //'Rover' dog['name']; //'Rover' dog.bark(); //'woof' dog['bark'](); //'woof'
You can iterate over JavaScript Objects using for in loops.
var dog = { name: 'Rover', noise: 'bark' }; for (var prop in dog) { console.log("Property: ", prop); console.log("Value", dog[prop]); } //Logs: //Property: name //Value: Rover //Property: noise //Value: bark
Functions are first class citizens in JavaScript.
Meaning, you can store them in variables...
var foo = function() {};
…pass them around as parameters, etc...
setInterval(foo, 2000);
function doSomeMath(x, y) { return x + y / 6 + x; };
Even though you define the number of arguments a function takes, you can pass as many as you'd like. All arguments are made available through an arguments
array-like property.
Currently
function addIt(bar) { bar = bar || 8; return bar + 2; };
ECMAScript 6 Proposed
function addIt(bar = 8) { return bar + 2; };
In JavaScript you can change the invocation context for a function, i.e. you can literally change the value of this
when you invoke a function.
window.name = 'bar'; var foo = function() { console.log(this.name); }; foo(); //'bar' foo.apply( { name: 'lalala' } ); //'lalala
jQuery Event Handlers
$('button').on('click', function() { //this is set to the button that was clicked on. });
The folder structure must match the package structure.
There is no native namespacing mechanism in JavaScript. The only built in way for scripts to communicate is via the global object.
A number of namespacing patterns are used to provide Java-like functionality.
myApp = {}; myApp.builder = { init: function(){}, destroy: function(){} };
Continued nesting:
myApp = myApp || {}; myApp.pages = myApp.pages || {}; myApp.pages.signup = { load: function(){} };
var myApp = myApp || {}; $.extend(myApp, { builder: {}, init: {}, unload: {} });
Define your base level namespace.
<script> dojoConfig = { paths: { 'myNamespace': '/path/to/myNamespace' } }; myNamespace = {}; </script> <script src="//ajax.googleapis.com/ajax/libs/dojo/1.7.2/dojo/dojo.js"></script>
File at /myNamespace named foo.js
dojo.provide("myNamespace.foo"); myNamespace.foo = function() {};
Then to get that file asynchronously and added to the main namespace.
dojo.require("myNamespace.foo");
Everything is based on functions.
var s = 1; var t = function() { var u = 2; var v = function() { var w = 3; }; }; var x = function() {};
Module Pattern
var Foo = function() { var bar = {}; this.getBar = function() { return bar; }; this.setBar = function(bar) { this.bar = bar; } }; var foo = new Foo(); foo.setBar('hi'); foo.getBar('hi'); foo.bar; //Error
Calling public methods from private ones is awkward.
var Foo = function() { var init = function() { this.stuff(); //JS Error }; this.stuff = function() { return 'stuff'; }; init(); };
var Foo = function() { var that = this; var init = function() { that.stuff(); }; this.stuff = function() { return 'stuff'; }; init(); };
var Foo = function() { var init = function() { stuff(); }; var stuff = function() { return 'stuff'; }; init(); return { stuff: stuff }; };
module Foo { var init = function() { stuff(); }; export function stuff() { return 'stuff'; }; };
Both of these are global variables.
var foo = 'foo'; var bar = function() {};
Wrap this with a function and they're no longer global.
function wrapper() { var foo = 'foo'; var bar = function() {}; };
An elegant way of mimicking block scoping in JS.
(function() { var foo = 'foo'; var bar = function() {}; }());
See jQuery
In Java, other than a few primitives everything is an Object and an Object of some Class.
public class Poodle { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
There are no native classes in JavaScript.
Classes are unlikely to be added because the standards committee doesn't want to offer two different inheritance models in the same language.
There is still a chance for class syntax that is simply syntax sugar for valid ES5 JavaScript.
Java has classical single inheritance, every class extends Object.
public class Poodle extends Dog { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
JavaScript uses single prototype inheritance. All objects have a prototype.
A prototype is simply an Object.
Much like all classes in Java have Object at the end of their inheritance chain, all Objects in JavaScript have Object.prototype at the end of their prototype chain.
For example, the valueOf function is defined in Object.prototype, so it can be called on essentially anything in JavaScript.
new String().valueOf(); new Number().valueOf(); new Boolean().valueOf(); new Function().valueOf(); new Object().valueOf(); new Array().valueOf();
The following are examples of some of the prototypes built into the language. You can type these into a JavaScript console with autocomplete (Firebug or WebKit Web Inspector) and see what are in the following.
public class Animal { public String eats() { return "meat"; } public String noise() { return "roar"; } } public class Dog extends Animal { public String noise() { return "bark"; } } new Dog().eats(); //"meat" new Dog().noise(); //"bark"
var Animal = function() {}; Animal.prototype.eats = function() { return 'meat'; }; Animal.prototype.noise = function() { return 'noise'; }; var Dog = function(){}; Dog.prototype = new Animal(); Dog.prototype.noise = function() { return 'bark'; }; new Dog().eats(); //"meat" new Dog().noise(); //"bark"
Functions have a prototype property set to their prototype Object. The only way to view the prototype of an Object before ES5 was to look at the non-standard __proto__ property.
In ES5 you can use Object.getPrototypeOf().
var x = function() {}; x.prototype; var y = {}; Object.getPrototypeOf(y);
You can add methods to the built in prototypes.
For example, IE < 9 doesn't have a trim method in String.prototype, but thanks to JavaScript's dynamic nature you can make one.
if (!String.prototype.trim) { String.prototype.trim = function(value) { //implement a trim here } }
As an example of this feature detection see the trim method in jQuery. However, jQuery doesn't add to the prototype.
You can even change the behavior of core methods.
String.prototype.trim = function() { //Whatever you'd like. };
Can be handy for unit testing.
Until ES5 there was no way of adding to Object.prototype without the property being enumerable in a for in loop.
Object.prototype.foo = function() {}; var anObject = { a: 1, b: 2, c: 3 }; for (var prop in anObject) { console.log(prop + " = " + anObject[prop]); } //Logs: //a = 1 //b = 2 //c = 3 //foo = function() {}
This can be fixed by wrapping everything within the loop in a Object.hasOwnProperty check:
Object.prototype.foo = function() {}; var anObject = { a: 1, b: 2, c: 3 }; for (var prop in anObject) { if (anObject.hasOwnProperty(prop)) { console.log(prop + " = " + anObject[prop]); } }
Object.defineProperty(Object.prototype, 'foo', { value: function(){}, enumerable: false }); var anObject = { a: 1, b: 2, c: 3 }; for (var prop in anObject) { console.log(prop + " = " + anObject[prop]); }
Whether or not the native prototypes are added to is a dividing line amongst popular libraries.
Plenty of people prefer classical inheritance and sought to replicate it in JS.
var Animal = new Class({ noise: function() { console.log('roar'); } }); var Dog = new Class({ Extends: Animal, noise: function() { console.log('bark'); } }); new Animal().noise(); //'roar' new Dog().noise(); //'bark'
require(['dojo/_base/declare'], function(declare) { declare("Animal", null, { noise: function() { console.log('roar'); } }); declare("Dog", Animal, { noise: function() { console.log('bark'); } }); }); new Animal().noise(); //'roar' new Dog().noise(); //'bark'
There are a lot of them. Here is a small sampling, for a more comprehensive list check out Douglas Crockford's JavaScript the Good Parts.
var foo = function() { for (x = 0; x < something.length; x++) { console.log(something[x]); } }; function bar() { //whatever } //x and bar are now global variables
There is none.
{ var x = 2; } console.log(x); //2 for (var y = 0; y < 8; y++) {} console.log(y); //8
Numbers with leading zeros are treated as octal numbers in JavaScript.
010 === 8 //true
This is handled the same in Java. The difference is how Strings are converted into integers.
//Java Integer.parseInt("010"); //10
//JavaScript parseInt("010"); //8 parseInt("010", 10); //10
ECMAScript 5 introduced strict mode, a way of opting in to a restricted variant of JS.
var foo = function() { "use strict"; x = 2; //error }; foo();
ECMAScript 6 contains some proposed major changes to the language. Features under discussion:
The let keyword can be used to offer true block scoping.
for (var x = 0; x < 8; x++) {} console.log(x); //8 for (let y = 0; y < 8; y++) {} console.log(y); //ReferenceError
Most all languages have had something written to compile them into JavaScript. There's a giant list at https://github.com/jashkenas/coffee-script/wiki/List-of-languages-that-compile-to-JS
//CoffeeScript double = (num) -> num * 2
//JavaScript var double = function(num) { return num * 2; };
//CoffeeScript if happy and knowsIt clapsHands() else doSomethingElse()
//JavaScript if (happy && knowsIt) { clapsHands(); } else { doSomethingElse(); }
//CoffeeScript switch day when "Fri", "Sat" if day is sunny go outside when "Sun" then go mall else go work
//JavaScript switch (day) { case "Fri": case "Sat": if (day === sunny) go(outside); break; case "Sun": go(mall); break; default: go(work); }
You can try writing some Dart at http://try.dartlang.org.
"JavaScript is already there. On a couple of billion devices… If you want to work with the web, learn JavaScript. If you don’t want to learn JavaScript, stay the hell away from the web."