Javascript


  • The language of the web, for HTML, for servers, PCs, laptops, tablets, cell phones, and more.

  • Very widely used, and growing, very common to use JavaScript to manipulate the HTML DOM.

  • Developed by Brendan Eich at Netscape in 10 days as a scripting language for Netscape Navigator 2 (more on that later..)

  • Related to Java in name only

  • JS Style guide by AirBNB- https://github.com/airbnb/javascript

Primitive Datatypes

  • Number 
    • 64-bit floating point, similar to Java double and Double 
    • No integer type 
    • Special values NaN  (not a number) and Infinity

  • String 
    • Sequence of zero or more Unicode characters 
    • No separate character type (just strings of length 1)
    • Literal strings using ' or " characters  (must match)

  • Special values 
    • null  and undefined
    • typeof(null) = object;     typeof(undefined)=undefined

  • Boolean 
    • Two values: true and false
    • Empty strings, undefined, null and 0 are all treated as false

Variables

  • No type specified, can be changed dynamically
  • Variables with no value have undefined value
  • Always use var, otherwise the variable is defined on the global scope
  • It's a good practice to define variables at the beginning of a function
var pi = 3.14;                 // pi is a Numbervar person = "John Doe";       // person is a string
var answer = 'Yes I am!'; // Different quotes, answer is a   //string as wellconsole.log(typeof answer); // stringanswer = 10; // answer is now a numberconsole.log(typeof answer); // number
var a; // a is undefined
globalVariable = 'i am a global variable';// one statement, many variables
var lastname = "Doe", age = 30, job = "carpenter";
var lastname; console.log(lastname); // still contains "Doe"

Flow control

  • If... else
if (time < 10) {
    x = "Good morning";
}
else if (time < 20) {
    x = "Good day";
}
else {
    x = "Good evening";
}
  • Switch
var day = new Date().getDay();
switch (day) {
    case 6:
        x = "Today is Saturday";
        break;
    case 0:
        x = "Today is Sunday";
        break;
    default:
        x = "Looking forward to the Weekend";
}

If What?

  • var a = ...;
  • if (a) - ?
if (a) - will evaluate to true if (a) isn't undefined, null, or NOT false.
  • Check if a is actually false:
  • if (a === false)
  • WHAT?! === and !== ?
  • (a === b) - makes sure that a and b are of the same type. Will return false immediately if a and b aren't of the same type. Have better performance then ==. 
  • One liner check and assignment:
  • a = a || 'hello'; // leave a as is, or set it to 'hello' if it  
                      // evaluates to false

Loops

  • for 
var numbers = [1, 2, 3], sum = 0;for (var i = 0; i < numbers.length; i++) {
    sum += numbers[i];
}
console.log(sum);     // 6
  • for.. in (iterate through object keys)
var txt = "";
var person = { fname: "John", lname: "Doe", age: 25 };

for (var x in person) {
    txt = txt + person[x];
}
console.log(txt);     // JohnDoe25
  • While
var i = 0, x = 0;
while (i < 5) { x = x + i; i++; }


Loops- break / continue


for (var i = 0; i <= 5; i++) {
    if (i == 3) continue;
    console.log(i);               // 0, 2, 2, 4, 5
}
for (var i = 0; i <= 5; i++) { if (i == 3) break; console.log(i); // 0, 1, 2 }

Loops - JavaScript style

  • forEach:
var numbers = [1, 2, 3], sum = 0;numbers.forEach(function(item) {   sum += item;});
console.log(sum);     // 6

Exceptions

  • Errors will happen

    • When the JavaScript engine is executing JavaScript code, different errors can occur:

      • It can be syntax errors, typically coding errors or typos made by the programmer.
      • It can be misspelled or missing features in the language (maybe due to browser differences).

    • It can be errors due to wrong input, from a user, or from an Internet server.

    • And, of course, it can be many other unforeseeable things.

Try.. Catch.. Throw


  • Can throw any object as an error

function div(x, y) {
    try {
        if (!x && !y) throw 'please provide x and y';
        if (y === 0) throw 'can not divide by 0';
        console.log(x/y);
    }
    catch(err) {
        console.log('error:', err);
    }
}
div(10, 2);      // 5
div();           // error: please provide x and y
div(10, 0);      // error: can not divide by 0 

Object == DICTIONARY

  • Every object in JS is basically a dictionary

  • Entries can be of any of the main types (object, array, function) or any of the basic types (String, Number, Date, RegExp)
  • Keys are strings (or numbers)

  • Entries can be added  (or removed) dynamically at any time
 

Defining an object

  • Everything non primitive is an object- array , function, ...
  • Properties are values associated with objects
  • Methods are actions that objects can perform
  • objects are data (variables), with properties and methods
  • Objects can be modified with new properties any time 


var person = new Object(); // can also be: var person= {};
person.firstname = "Ami";
person.lastname = "Turgman";
person.age = 37;
person.eyescolor = 'brown';var anotherPerson = {
    firstname: 'John',
    lastname: 'Doe'
};
anotherPerson.age = 50;               // dot notation
anotherPerson['eyescolor'] = 'blue';  // key notation
console.log(person, anotherPerson);delete person.age; console.log(person.age); // will print 'undefined'


A function member

var anotherPerson = {    firstname: 'John',
    lastname: 'Doe',
    getFullName: function () { return this.firstname + ' ' + 
                                      this.lastname; }
};
console.log(anotherPerson.getFullName());    // John Doe

  • this represents the current object the method is running on.
  • In most cases, this  points to the object which has the function as a property.
  • this is resolved dynamically when the method is executed
  • this can be changed explicitely using the call/apply object functions
console.log(anotherPerson.getFullName.call(person));
    • The object person does not have the getFullName method
    • We can invoke any function on any object

Array == List

  • Arrays are just dynamic lists. They serve as a powerful container. Basic functionality of list, queue and stack.

  • Prefer using the [ ] notation to the new Array() directive.

var a = [], b = new Array();  // both equivalent
a[0] = 0, a[1] = 1, a[3] = 3; console.log(a.join(',')); // 0,1,,3 b.push(1, 'a', null, undefined, true); console.log(b); // [1, "a", null, undefined, true]

  • Array API: push, pop, shift, unshift, splice and slice


Functions

  • Always returns a value
    • undefined is used if returned value was not provided
  • Basic types passed by value, objects by reference
  • Can have any number of parameters
    • Caller can call the function with any number of arguments
    • Missing arguments will have the value undefined
    • Extra arguments will be ignored

function myFunc(a, b) {
    console.log(a, b);
}

var result = myFunc(1, 2);  // 1 2
console.log(result);        // undefined
myFunc('a');                // b undefined
    • myFunc is a named function and can be invoked by calling it

Functions returning function

  • Functions can be returned from other functions and be stored in variables
function getAdder(sum) {
    sum = sum || 0;
    return function (num) {
        sum += num;
        return sum;
    }
}

var adderFunc = getAdder(100);
console.log(adderFunc(1));     // 101
console.log(adderFunc(2));     // 103
console.log(adderFunc(3));     // 106
  • The internal adder function is an anonymous function
    • anonymous functions are usually used as callbacks
setTimeout(function(){ console.log('time expired'); }, 100);
  • Even though the adder function is being called from outside of the getAdder() function, it still has access to the sum variable. 
    • this is called closure


Closures

  • Inner functions have access to variables in the outer function.
  • Accessing variables outside of your immediate lexical scope creates a closure.
  • A closure is the local variables for a function- kept alive after the function has returned, or a closure is a stack-frame which is not deallocated when the function returns 
function sayHello(name) {    var text = 'hello ' + name;
    return function () { console.log(text); }
}

var sayHelloFunc = sayHello('ami');
sayHelloFunc(); // hello ami
function say100() {
    // Local variable that ends up within closure
    var num = 100;
    var logNum = function() { console.log(num); }
    num++;
    return logNum;
}
var logNumFunc = say100();
logNumFunc(); // 101

Closures- cont

  • Be careful when passing references to objects
  • You might get unexpected behavior when the objects values are changed

function foo(x) {    var tmp = 2;
    return function (y) {
        console.log(x.val + y + tmp);
    }
}

var obj = { val: 10 };
var bar = foo(obj); // bar is now a closure referencing obj.
bar(3);             // 15
obj.val = 20;
bar(3);             // 25

Self invoking functions

  • A function can be declared and execute itself
    • (function(params) { function body } )(variables);

var u = { a: 1, b: 2 };
var v = { a: 3, b: 4 }; console.log(u, v); // {a: 1, b: 2} {a: 3, b: 4} (function (x, y) { var tempA = x.a, tempB = x.b; //local variables x.a = y.a; x.b = y.b; y.a = tempA; y.b = tempB; })(u, v); console.log(u, v); // {a: 3, b: 4} {a: 1, b: 2} // This works because objects are passed by reference

Scope

  • The environment where variable are contained
  • Scope chain - the current scope plus all parent scopes
  • A variable that was set in a function without defining it previously will be defined in the global scope
  • Outside of a function body the scope is the global scope, in the browser this usually means 'window'
var externalVar = 'external';function myFunc() {
  var myFuncVar = 'myFunc';

  function internalFunction() {
      var internalVar = 'internal';
      console.log(externalVar, myFuncVar, 
                     internalVar, globalVar);  }

  globalVar = 'global';
  internalFunction(); // external myFunc internal global
}

myFunc();
console.log(globalVar);     // global
console.log(myFuncVar);     // myFuncVar is not defined
console.log(internalVar);   // internalVar is not defined

The arguments argument


  • Each function is passed an arguments object
  • The arguments object is an array-like object containing all the parameters that were passed to the function

    • Used usually when there is a variable number of parameters that can be passed to a function

function sumAll() {    var total = 0;
    for (var i = 0; i < arguments.length; i++)
        total += sumAll.arguments[i];
    return total;
}
console.log(sumAll(1, 2, 3, 4, 5));     // 15

The this arugment



  • Each function is passed a reference to this.
  • It will contain a reference to the object that is holding the function. 
  • In case function isn't called "on" an object - behavior might be weird, better use the "this insanceof  ..." pardigm. (more on that later)

function getName() {  return this.name;}
var o = {name : 'Nadav'};o.getName = getName;o.getName(); // returns Nadav;getName(); // hmmm? - this == global

Varialbe number of params

  • It is a common design to send a single object with the parameters value
  • Can be changed dynamically without changing the API

function doWebRequest(options) {    var scheme = options.scheme || 'http';
    var port = options.port || 80;
    
    var url = scheme + '://' + options.host + ':' + port
                              + options.path;
    console.log('calling', url);
}

doWebRequest({ host: 'microsoft.com', path: '/js' });
doWebRequest({ scheme: 'https', port: 441, host: 
                       'microsoft.com', path: '/js' });
// calling http://microsoft.com:80/js // calling https://microsoft.com:441/js

Function as a class - prototypes

  • Using the new keyword to create an instance of a class
  • The convention is to Capitalize constructor names
  • This is called a "prototype"

function Person(name, age) {
this.name = name; this.age = age; this.getDetails = function () { return this.name + ', ' + this.age; }; } var p1 = new Person('nadav', 29); var p2 = new Person('guy', 28); console.log(p1.getDetails()); // nadav, 37 console.log(p2.getDetails()); // guy, 28

Function as a class - contid.

  • You can still call the function without the new keyword - but the reference to *this* won't be set.
  • Best practice is to have it behave as if it was called with the new keyword

function Person(name, age) {

if (!(this instanceof Person)) { return new Person(name,age); }
//...}
var p1 = Person('nadav', 29); // equivalent to new Person


Prototypes - contid.

  • After being invoked with the new keyword, an object will have the "Person" prototype.
  • You can set the object methods to be part of the prototype instead of  each Object separately.
  • You can add properties to an existing prototype using the constructors' "prototype" property.

function Person(name, age) {    //...}// function will be "shared" among instancesPerson.prototype.getDetails = function {   return this.name + ',' + this.age;}

Inheritance

  • Instead of inheritance  - there are "prototype chains"
  • Alternative:  Runtime Inheritance -  Clone objects and add extra properties
  • Some libraries (for example, node.js) provide all kind of 'inheritance-like' functionality


Prototypes Chain

  • Give your prototype a prototype.                                                        

function Person(name, age, profession) {    //...}
Person.prototype.getDetails = function() {...};
function Developer(name, age, lang) { Person.call(this, name, age, 'developer'); this.lang = lang;}
Developer.prototype = new Person();var d = new Developer('Nadav', 29, 'JavaScript');

apply, call and bind

  • You can invoke  a function with the 'apply' method and specify the "this" reference as the first argument (followed by the other arguments as an array) 
  • you can also use 'call' but specify the arguments instead of using an array
function func(b) = { return this.a + b;}var o = {a : 1}func.apply(o, [3]); // will return 4func.call(o, [3]); // will also return 4


bind

  • calling 'bind' on a function will return a new function with fixed this/ parameters.
  • Accepts this, and an array of arguments.
  • Very useful when iterating over loops, or when using async programming.

this.x = 9; var module = { x: 81, getX: function() { return this.x; } }; module.getX(); // 81 var getX = module.getX; getX(); // 9, because in this case, "this" refers to the global object // Create a new function with 'this' bound to module var boundGetX = getX.bind(module); boundGetX(); // 81

Object "utility" methods

  • The Object type has some very useful methods:
    • Object.create(obj) - Will create a new object, setting obj as it's prototype.
    • Object.getPrototypeOf
    • Object.getOwnPropertyNames
    • Object.keys
    • Object.preventExtensions
    • Object.defineProperty


Object.defineProperty

  • Defines a property of an object, but can define "special attributes" for that property.
  • Can be used to define read-only, hidden or non-deletable properties.
  • Can be used to define getter and setter function for the property.

var o = {}; // Creates a new object

// Example of an object property added with defineProperty with a data property descriptor
Object.defineProperty(o, 'a', {
  value: 37,
  writable: true,
  enumerable: true,
  configurable: true
});
  • writable,  enumarable and configurable will be set to false if not explicitly specified!!!

Object.defineProperty

  • Can be used to define getter and setter function for the property.

// Example of an object property added with defineProperty with an // accessor property descriptorvar o = {};
var bValue = 38;
Object.defineProperty(o, 'b', {
  get: function() { return bValue; },
  set: function(newValue) { bValue = newValue; },
  enumerable: true,
  configurable: true
});
o.b; // 38
  • You cannot specify getters /setters and also specify a value

Async Functions

  • Calling an async function with a callback function that will be invoked once it has completed its work
function doWorkAndCallMeWhenDone(callback) {
    console.log('starting work');
    setTimeout(function () {
        console.log('work completed, calling callback');
        callback();
    }, 1000);
}

var onCompleteHandler = function () {
    console.log('work is completed, now i can proceed with 
                 my next task');
};

doWorkAndCallMeWhenDone(onCompleteHandler);



Async error handling

  • A common design is that a callback function will have an error object as its first parameter and then the returned values
  • If there was an error during the async function work, it will invoke the callback with the error as its first value
  • First thing the callback will do is to check if there was an error
function doWorkAndCallMeWhenDone(callback) {    console.log('starting work');
    setTimeout(function () {
        console.log('work completed with error, notifying 
                     caller');
        callback('some error occurred');
    }, 1000);
}

doWorkAndCallMeWhenDone(function (err, result) {
    if (err) return console.error('error doing work:', err);
    console.log('work is completed, now i can proceed with 
                 next task');
});

Async function 


  • checking for errors and getting the result from the async call

function doWorkAndCallMeWhenDone(callback) {    console.log('starting work');
    setTimeout(function () {
        console.log('work completed successfully, notifying 
                     caller');
        callback(null, 'some result object');
    }, 1000);
}

doWorkAndCallMeWhenDone(function (err, result) {
    if (err) return console.error('error doing work:', err);
    console.log('work is completed with result:', result);
});

Christmas tree problem

  • Look for the tree...

function async(x, cb) {
    cb(null, x + 1);
}

function executeAsyncSequence(cb) {
    async(0, function (err, res) {
        async(res, function (err, res) {
            async(res, function (err, res) {
                cb(null, res);
            });
        });
    });
}

executeAsyncSequence(function (err, res) {
    console.log('result:', res);
});

  • There are libraries that makes this look sequentially 

Checking parameters


  • Sometimes you allow you async function to be called without providing parameters or a callback
  • This is a common design

function asyncFun(param, cb) {
    if (typeof param == 'function') cb = param;
    cb = cb || function () {console.log('completed work');};
    // do some async work
    cb();
}

asyncFun('some value', function () {console.log('here!');});
asyncFun(function () { console.log('here!'); });
asyncFun();