JavaScript technologies

Advanced language features of JavaScript

Horváth, Győző
senior lecturer
horvath.gyozo@inf.elte.hu

Financed from the financial support ELTE won from the Higher Education Restructuring Fund of the Hungarian Government

Table of Content

  • Prerequisites
  • The basic essentials of the JavaScript Object Model
  • Object creator patterns
  • Arrays
  • Functions

Prerequisites

Language features

  • dynamically typed
  • interpreted
  • script language

(and more...)

Literals

//Boolean
true, false

//Numbers
12, 56.432

//Strings
'apple', "pear"

//Special
undefined, null

//Arrays
[1, 'two', true]

//Objects
{
    prop1: 'value1',
    prop2: 'value2'
}

Variable assignment

//Variable with initial value
var name = 'Winnie the Pooh';
  
//Variable without initial value
var another;  //undefined
  
//Multiple variable definitions
var alma = 'piros',
    korte = 'sárga';

C-like syntax

  • Operators
  • Control structures
//The === operator
'1' == 1    //true
'1' === 1   //false
'1' !== 1   //true

//Conditional controls
if (cond) {
  statements
}
  
if (cond) {
  statements
} else {
  statements
}

//Iterative controls
while (cond) {
  statements
}
  
do {
  statements
} while (cond);
  
for (var i = 1; i <= n; i++) {
  statements
}
  
for (var prop in obj) {
  statements
}

Modeling data structures

  • arrays
  • structs or records
  • nested structures
  • JSON

Functions

  • declaring functions
  • variable scope
//Global variable
var glob = 42;

//Function declaration
function funcName(par1, par2) {
    var localVar = 12;
    statements;
    return returnValue;
}

The basic essentials of the JavaScript Object Model

Object properties

  • collection of name-value pairs
    • properties (data members)
    • methods
var obj = {
    property: 1,
    'this is a property too': 2,
    method: function () {
        console.log(this.property);
    }
};

obj.property
obj['this is a property too']
obj.method()

The two pillars of JavaScript

  • dynamic objects
  • prototype object

Dynamic objects

//Creating an object
var obj = {
    a: 1,
    b: 2
};

//New property
obj.c = 3;      

//Accessing property (read)
obj.a    === 1
obj['a'] === 1  
obj.d    === undefined

//Modifying the value of a property (write)
obj.b = 42;   

//Deleting a property
delete obj.c;

Prototype object

Prototype chain

Setting and getting the prototype object

//Setting up the prototype chain
var obj2 = Object.create(obj1);

//Getting the prototype object
obj1.isPrototypeOf(obj2)                //obj1 is in the prototype chain of obj2
Object.getPrototypeOf(obj2) === obj1    //obj2 is the prototype object of obj1

Reading and writing on an object

  • Read: looking up the prototype chain
  • Write: always on the given object

Object creator patterns

  • Basic encapsulation with object literal
  • Object generator functions
  • Object constructor functions
  • Inheritance
  • Module pattern
  • ECMAScript 2015 syntax

Object literal

  • Basic encapsulation
  • Singleton
var uniqueObject = {
    name: 'Peter',
    describe: function() {
        return 'Person called '+this.name;
    }
};

uniqueObject.describe();  //Person called Peter

Object generators

function personGenerator(name) {
    return {
        name: name,
        describe: function() {
            return 'Person called '+this.name;
        }
    };    
}

var peter = personGenerator('Peter');
var julia = personGenerator('Julia');

(We do not follow the object generator path below, instead we focus on constructor functions...)

Object constructors

function Person(name) {
    this.name = name;
    this.describe = function () {
        return 'Person called '+this.name;
    };
}

var jim = new Person('Jim');
jim.name        //'Jim'
jim.describe()  //'Person called Jim'

Object constructors using prototype objects

Putting methods in the prototype is more efficient

function Person(name) {
    this.name = name;
}
Person.prototype.describe = function () {
    return 'Person called '+this.name;
};

var jim = new Person('Jim');
jim.name        //'Jim'
jim.describe()  //'Person called Jim'

Person and Employee examples are from Speaking JavaScript from Axel Rauschmayer

Inheritance

function Employee(name, title) {
    Person.call(this, name);
    this.title = title;
}
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
Employee.prototype.describe = function () {
    return Person.prototype.describe.call(this)+' ('+this.title+')';
};

var jane = new Employee('Jane', 'CTO');
jane.describe()     //'Person called Jane (CTO)'

Inheritance with helper function

function subclasses(SubC, SuperC) {
    var subProto = Object.create(SuperC.prototype);
    // Save `constructor` and, possibly, other methods
    copyOwnPropertiesFrom(subProto, SubC.prototype);
    SubC.prototype = subProto;
    SubC._super = SuperC.prototype;
};

function Employee(name, title) {
    Employee._super.constructor.call(this, name);
    this.title = title;
}
Employee.prototype.describe = function () {
    return Employee._super.describe.call(this)+' ('+this.title+')';
};
subclasses(Employee, Person);

Wrapping into module pattern

//Basic constructor
var Person = (function () {
    function Person(name) {
        this.name = name;
    }
    Person.prototype.describe = function () {
        return 'Person called '+this.name;
    };
    return Person;
})();

//Inheritance
var Employee = (function (Parent) {
    function Employee(name, title) {
        Parent.call(this, name);
        this.title = title;
    }
    Employee.prototype.describe = function () {
        return Employee._super.describe.call(this)+' ('+this.title+')';
    };
    subclasses(Employee, Parent);
})(Person);

ECMAScript 2015 class syntax

class Person {
    constructor(name) {
        this.name = name;
    }

    describe() {
        return 'Person called '+this.name;
    }
}

//Inheritance
class Employee extends Person {
    constructor(name, title) {
        super(name);
        this.title = title;
    }

    describe() {
        return super.describe() + ' (' + this.title + ')';
    };
}

Arrays

Array

  • an easily iterated list-like object
  • zero-based indices for accessing elements
var emptyArray = [];
var myArr = [12, 'apple', true];
myArr[0];        // => 12;
myArr[1];        // => 'apple';
myArr[2];        // => true;
myArr.length     // => 3

//Modification
myArr[0] = 13;

//Expansion
myArr[myArr.length] = 'new';
myArr[100] = 'far away';
myArr.length;    // => 101
myArr[99];       // => undefined

//Deletion
delete myArr[1];
myArr[1];        // => undefined
myArr.length;    // => 101

Array methods

  • pop(), push(e), shift(e), unshift(): modifying both ends
  • reverse()
  • splice(from, count): cutting (and deleting) the inner part of the array
  • join(separator): gluing together the elements with the separator string.

Collection methods

  • forEach: iterating
  • map: copying
  • filter: filtering
  • every: optimistic decision
  • some: decision
  • reduce: summing up
var numbers = [1, 2, 3];
var oddNumbers = numbers.filter(function (item) {
    return item % 2 !== 0;
});
oddNumbers;     //[1, 3]

Iterating

var fruits = [
  'apple',
  'pear',
  'plum'
];

//Writing the fruits to the console
for (var i = 0; i < fruits.length; i++) {
  console.log(fruits[i]);
}

//or
fruits.forEach(function (fruit) {
    console.log(fruit);
});

//or
for (var f of fruits) {
    console.log(f);
}

Functions

Function definitions

//Declaration
function osszead(a, b) {
    return a + b;
}

//With function expression
var osszead = function(a, b) {
    return a + b;
}

//ES6 fat arrow syntax
var osszead = (a, b) => a + b;

Function literal

function [name]([param [, param [..., param]]]) {
   statements
}

Function as a first class object

  • a special object with callable code segment
  • can be in any expression
    • on the right side of an assignment (e.g. creating a function)
    • as a property of an object (method)
    • as a function parameter
    • as a return value of a function (function generator)

Function as a parameter

function linearSearch(x, T) {
    var i = 0;
    while (i < x.length && !T(x[i])) {
        i++;
    }
    return {
        isExist: i < x.length,
        offset: i
    };
}

function isNegative(p) {
    return p < 0;
}
  
var myArr = [1, 3, -2, 8];
linearSearch(myArr, isNegative);

Function as a return value

function operationGenerator(op) {
    if (op === '+') {
        return function (a, b) {
            return a + b;
        };
    }
    else if (op === '*') {
        return function (a, b) {
            return a * b;
        };
    }
}
  
//Generating a summing function
var operation = operationGenerator('+');
operation(10, 32);    //42
  
//Generating a multiplier function
var operation = operationGenerator('*');
operation(10, 32);    //320

Closure

Every function remain in connection with the containing function, even if the external function ends.

function createIncrementor(start) {
    return function () {  // (1)
        start++;
        return start;
    }
}

var inc = createIncrementor(5);
inc()   // 6
inc()   // 7
inc()   // 8

The context of this

//Global
var name = 'Peter';
function hello() {
    return this.name;  // this === global (window)
}
hello();

//Method call
var peter = {
    name: 'Peter',
    describe: function () {
        return this.name;   // this === peter
    }
}
peter.describe();

//Constructor call
var Person = function(name) {
    this.name = name;   // this === instance object
}
var peter = new Person('Peter');

//Setting the context of this with call and apply
var peter = {
    name: 'Peter',
    hello: function () {
        return this.name;
    }
};
var julia = {
    name: 'Julia',
    hello: function () {
        return this.name;
    }
};
peter.hello.call(julia); // "Julia"

Loosing the context in inner functions

var peter = {
    name: 'Peter',
    age: 42,
    describe: function () {
        function getAge() {
            return this.age;  // this === global (window)
        }
        return this.name + ':' + getAge();
    }
}
peter.describe();  // "Peter:undefined"

Recovering the context of this in inner functions

//With call and apply
var peter = {
    name: 'Peter',
    age: 42,
    describe: function () {
        function getAge() {
            return this.age;  // this depends on the call
        }
        return this.name + ':' + getAge.call(this);
    }
}
peter.describe();  // "Peter:42"

//ES5 bind()
var peter = {
    name: 'Peter',
    age: 42,
    describe: function () {
        var getAge = (function () {
            return this.age;  // inner this is always outer this
        }).bind(this);
        return this.name + ':' + getAge();
    }
}
peter.describe();  // "Peter:42"

//ES6 fat arrow syntax
var peter = {
    name: 'Peter',
    age: 42,
    describe: function () {
        var getAge = () => this.age;  
        return this.name + ':' + getAge();
    }
}
peter.describe();  // "Peter:42"

ECMAScript 6

ECMAScript 6 (ECMAScript 2015)