Ubiqutous
Quirky
Fun!
Popular!
A humane programming language (it has flaws)
Any application that can be written in JavaScript, will eventually be written in JavaScript.
Something boring
Something necessary
Something interesting
Something awesome
Something surprising
Something bad
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
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.
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.
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 |
Number
String
Boolean (true
and false
)
Null (null
)
Undefined (undefined
)
Object (a few types)
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).
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'
Arrays
Functions
Regular expressions
Objects (duh!)
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 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}`);
function createFunction() { return () => { console.log('Hello World'); } }
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]
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)
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'
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!') }
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.
Value | Type |
---|---|
0 (zero) |
Number |
NaN (not a number) |
Number |
'' (empty string) |
String |
false |
Boolean |
null |
Object |
undefined |
Undefined |
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
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
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
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; } }
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] }
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'
let counter = () => { let value = 0; return { getValue: () => { return value; }, increment: () => { value++; } }; } let myCounter = counter(); // create an object
Encapsulation: value
can't be directly manipulated.
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');
var
was the only way to create variables prior to es6
es6/ES2015 introduced two new keywords:
let
for variables that can be reassignedconst
for variables that can't be reassignedIf you're writing es6 or later, don't use var
Why?
var
declarations are hoisted to the top of the functionlet
and const
variables are block scopedvar
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.
() => { if (condition) { var myString = 'hello'; } // myString = 'hello' } // myString is no longer in scope
() => { if (condition) { let myString = 'hello'; } // myString is no longer in scope }
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
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?
setTimeout()
and friendssetTimeout(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
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
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
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 !==