JavaScript all the things meme

Why JavaScript?

Ubiqutous

Quirky

Fun!

Popular!

A humane programming language (it has flaws)

Where can we use JavaScript?

Browser logos
Node.js logo
Raspberry Pi
Arduino Uno
Leap Motion
PostgreSQL database logo
Any application that can be written in JavaScript, will eventually be written in JavaScript.
Atwood's Law, 2007
Jeff Atwood, Stack Exchange (stackoverflow.com)

Today's agenda

Something boring

Something necessary

Something interesting

Something awesome

Something surprising

Something bad

Today's agenda in detail

Boring: A brief history of JavaScript

Necessary: The type system

Interesting: Truthiness, Prototypes

Awesome: Closures, Currying

Surprising: Scoping, Concurrency

Bad: Global variables, Equals, and more

A brief history of JavaScript

(actually not that boring)

In the beginning...

In 1995, Netscape Navigator was the dominant web browser, but Microsoft was making inroads with Windows 95.

Netscape considered the web browser a new form of distributed OS, but it needed a lightweight scripting language.

They hired Brendan Eich in April 1995 and told him he had ten days to design a programming language.

Brendan created Mocha, later renamed to LiveScript, borrowing ideas from LISP, Smalltalk, Self and C.

Netscape Navigator 2.0 Beta was released in September 1995.

So why is it called JavaScript?

Java had just been released and was getting a lot of attention.

Netscape marketing changed the name from LiveScript to JavaScript in December 1995.

Netscape Navigator 2.0 with JavaScript support launched in March 1996.

Netscape delivered JavaScript to Ecma International for standardisation.

June 1997: First edition of ECMA-262 ECMAScript Language Specification.

ECMA-262: Uneven pace of change

Year Edition Nickname
1997 First published
1998 2nd Edition
1999 3rd Edition es3
2011 5.1 Edition es5
2015 ECMAScript 2015 (6th Edition) es6 or ES2015
2016 ECMAScript 2016 (7th Edition) es7 or ES2016
2017 ECMAScript 2017 (8th Edition) es8 or ES2017

Today in the browser: es5

Today on the server: es7 (a.k.a. ES2016)

Tomorrow: es8 (a.k.a. ES2017)

The type system

(it's quite simple)

Types

Number

String

Boolean (true and false)

Null (null)

Undefined (undefined)

Object (a few types)

Number usage

let aNumber = 1;
let anotherNumber = 0.1;
1 / 0;
>> Infinity
Math.sqrt(-1);
>> NaN  // not a number
0.1 + 0.2;
>> 0.30000000000000004

JavaScript uses binary floating point arithmetic (IEEE 754).

String usage

let empty = '';
let helloWorld = "Hello World!";
let escaping = 'It\'s a glorious day!';
'abc' + "def";
>> 'abcdef'
1 + '2';
>> '12'
let name = 'Alice';
`Hello ${name}`; // es6 and later, using backtick (`), not quote (')
>> 'Hello Alice'

Objects

Arrays

Functions

Regular expressions

Objects (duh!)

Arrays

Create:

let empty = [];
let numbers = [ 'zero', 'one' ];

Mutate:

numbers.push('two');   // [ 'zero', 'one', 'two' ]
numbers[0] = 'zilch';  // [ 'zilch', 'one', 'two' ]

Query:

numbers[1];
>> 'one'
numbers.length;
>> 3

Functions

Functions statement:

function hello(name) {
    console.log(`Hello ${name}`);
};

Function expression:

let hello = function(name) {
    console.log(`Hello ${name}`);
};

es6 introduced the arrow syntax:

let hello = (name) => console.log(`Hello ${name}`);

Functions as return types

function createFunction() {
    return () => {
        console.log('Hello World');
    }
}

Functions as arguments

function map(numbersArray, callbackFunction) {
    let result = [];
    for (let number of numbersArray) {
        result.push(callbackFunction(number));
    };
    return result;
};

Usage:

map([1, 2], (num) => { return num * num; );
>> [1, 4]

Objects

Containers of properties that are name/value pairs

Names can be any String

Values can be any expression

Mutable

Strings and Numbers are not objects (they are object-like but immutable)

Object syntax

Create:

let empty = {};
let simple = { 'name1': 'value1', 'name2': 'value2' };

Mutate:

simple['name1'] = 'changed';
simple['another name'] = 'another value';

Query:

simple['name'];  // or simple.name;
>> 'value'

Object literals

let myArray = [1, 2, 3];

let person = {
    'firstName': 'John',
    'lastName':  'Smith',
};

let nestedExample = {
    'firstName': 'Jane',
    'lastName':  'Smith',
    'address': {
        'country': 'Australia',
        'city':    'Sydney'
    },
    'greeting': () => console.log('Hello World!')
}

Interesting parts

Truthiness, Prototypes

Truthy and falsy

All expressions are either truthy or falsy.

This can be used in conditional expressions:

if (someVariable) {
    console.log('this only executes if someVariable is truthy');
}

Can be used as a simple check for null, undefined, NaN and more.

Falsy values

Value Type
0 (zero) Number
NaN (not a number) Number
'' (empty string) String
false Boolean
null Object
undefined Undefined

Prototypes

All objects have a prototype attribute (also known as a prototype object), which is the object's "lineage"

Access an object's prototype using myobject.__proto__ (non-standard) or Object.getPrototypeOf(myobject) (standard in es6 and later)

let o = { x: 1 }; // o's prototype is Object.prototype
let a = [ 1, 2 ]; // a's prototype is Array.prototype
let f = () => {}; // f's prototype is Function.prototype

Prototype inheritance

A new object can inherit properties of an existing object.

Example: Arrays automatically inherit properties of the Array prototype object, such as length

let a = [ 1, 2 ];
a.length; // length is a property of Array.prototype
>> 2

Prototypes form a chain to Object.prototype, and then null

a.__proto__;
>> [] // this is Array.prototype
a.__proto__.__proto__;
>> {} // this is Object.prototype
a.__proto__.__proto__.__proto__;
>> null

Prototype inheritance using new

function Shape(name) { // constructor function
    this.name = name;
}
Shape.prototype.describe = function() {
    console.log(`I am a ${this.name}`);
}
let shape = new Shape('rectangle');
shape.color = 'red';
shape.describe();
>> 'I am a rectangle'
shape.__proto__
>> Shape { describe: [Function] }
shape.__proto__.__proto__
>> {} // this is Object.prototype
Prototypal inheritance graph

Prototypes vs classes

function Person(name) {
    this.name = name;
}
Person.prototype.getName = function() {
    return this.name;
}

class Person {  // es6 and later only
    constructor(name) {
        this.name = name;
    }
    getName() {
        return this.name;
    }
}

Inheritance using Object.create()

The first argument to Object.create() will be the prototype object of the newly created object.

let parent = {
    'name': 'Alice',
    'greet': () => console.log('Hello')
};
let child = Object.create(parent);
child.name = 'Bob';
child.greet();
>> 'Hello'
child.name;
>> 'Bob'
Object.getPrototypeOf(child);
>> { name: 'Alice', greet: [Function: greet] }

Awesome parts

Closures, Currying

Closures

Inner functions can access variables of the functions they are defined within.

let outer = () => {
    let outerVariable = 'hello';

    return {
        inner: () => { console.log(outerVariable); }
    };
};
// outerVariable is no longer in scope

outer().inner();
>> 'hello'

Function closures as pseudo-classes

let counter = () => {
    let value = 0;

    return {
        getValue: () => { return value; },
        increment: () => { value++; }
    };
}

let myCounter = counter();  // create an object

Encapsulation: value can't be directly manipulated.

Currying

Create a new function by supplying some arguments of an another function.

function describe(thing, adjective) {
  console.log(`{thing} is ${adjective}`);
}
function describeWithCurrying(adjective) {
  return (thing) => console.log(`${thing} is ${adjective}`);
}

let isAwesome = describeWithCurrying('awesome');
let isEvenBetter = describeWithCurrying('even better');

isAwesome('Melbourne');
isEvenBetter('Sydney');

Surprising parts

Scoping, Concurrency

Brief detour: Variable declarations

var was the only way to create variables prior to es6

es6/ES2015 introduced two new keywords:

  • let for variables that can be reassigned
  • const for variables that can't be reassigned

If you're writing es6 or later, don't use var

Why?

  • var declarations are hoisted to the top of the function
  • let and const variables are block scoped

Scoping with var

JavaScript uses C's block syntax (curly braces {}), but there's no block scope for var.

var has only two scopes: global and function.

A variable defined without var is global. Never do this!

A variable defined using var outside any function is global.

A variable defined in a function only exists in that function.

var vs let

() => {
    if (condition) {
        var myString = 'hello';
    }
    // myString = 'hello'
}
// myString is no longer in scope
() => {
    if (condition) {
        let myString = 'hello';
    }
    // myString is no longer in scope
}

Scoping test with var

let first = 0;
let scopeTest = () => {
  let first = 1;
  console.log(`first=${first}`);
  if (true) {
      var second = 2;
  }
  console.log(`second=${second}`);
};
scopeTest();
console.log(`first=${first}`);
console.log(`second=${second}`);
>> first=1
   second=2
   first=0
   ReferenceError: second is not defined

Concurrency: the event loop

while (queue.waitForEventMessage()) {
  queue.processNextEventMessage();
}

Each event message is processed completely before any other message is processed.

No multi-threading support, as in C, Java, others.

How are event messages added to the queue?

  • In a browser: When a user interface event happens and an event listener is is attached.
  • Elsewhere: setTimeout() and friends

Event loop test

setTimeout(callback, delayMillis)

Adds callback as a message to the event queue, with a delay.

(function() {
    for (var i = 1; i <= 3; i++) {
        setTimeout(function() {console.log(i); }, 0);
    }
})();

Solution:

>> 4
   4
   4

Event loop test using let

setTimeout(callback, delayMillis)

Adds callback as a message to the event queue, with a delay.

(function() {
    for (let i = 1; i <= 3; i++) {
        setTimeout(() => console.log(i), 0);
    }
})();

Solution:

>> 1
   2
   3

Bad parts

Global variables, Equals, and more

Global variables

It's far too easy to create global variables!

function myFunction() {
    global1 = 1;
    var nonGlobal = 'I only exist inside myFunction';
}
var global2 = 2;
window.global3 = 3;

global1 is global because we forgot let/const/var

global2 is global because we declared it as a var outside of a function or object

global3 is global because we added it to the global web browser window object

Equals

JavaScript has both == and === (as well as != and !==)

== performs type coercion when the arguments are not of the same type, using strange rules:

0 == ''            // true
'0' == ''          // true
'' == '0'          // false

false == '0'       // true
null == undefined  // true

' \t\r\n ' == 0    // true

Lesson: Use === and !==

Where do I start?

JavaScript: The Good Parts book cover
Mozilla Developer Network: JavaScript
Node.js logo

Thank you!

References