toogle sidebar button

Basis

This

this binds by default to the given current object:

var person = {
  name: "Nicholas",
  sayName: function() {
    console.log(this.name);
  }
};

person.sayName(); // outputs "Nicholas"

But the context can be different:

function sayNameForAll() {
  console.log(this.name);
}

var person1 = {
  name: "Nicholas",
  sayName: sayNameForAll
};

var person2 = {
  name: "Greg",
  sayName: sayNameForAll
};

var name = "Michael";
person1.sayName(); // outputs "Nicholas"
person2.sayName(); // outputs "Greg"

sayNameForAll(); // outputs "Michael"

Rebinding this

Using call

function sayNameForAll(label) {
  console.log(label + ":" + this.name);
}

var person1 = {
  name: "Nicholas"
};

sayNameForAll.call(person1, "person1"); // outputs "person1:Nicholas"

It accepts multiple arguments and replaces this.

Using apply

function sayNameForAll(label) {
  console.log(label + ":" + this.name);
}

var person1 = {
  name: "Nicholas"
};

sayNameForAll.apply(person1, ["person1"]); // outputs "person1:Nicholas"

It accepts arguments as an array and replaces this.

Using bind

function sayNameForAll(label) {
  console.log(label + ":" + this.name);
}

var person1 = {
  name: "Nicholas"
};

var person2 = {
  name: "Greg"
};

// create a function just for person1
u var sayNameForPerson1 = sayNameForAll.bind(person1);
sayNameForPerson1("person1"); // outputs "person1:Nicholas"

// create a function just for person2
v var sayNameForPerson2 = sayNameForAll.bind(person2, "person2");
sayNameForPerson2(); // outputs "person2:Greg"

// attaching a method to an object doesn't change 'this'
w person2.sayName = sayNameForPerson1;
person2.sayName("person2"); // outputs "person2:Nicholas"

It creates proxy for the given method which replaces this.

Protecting properties

Preventing extensions - can’t add new properties

var person1 = {
  name: "Nicholas"
};

console.log(Object.isExtensible(person1)); // true
Object.preventExtensions(person1);

console.log(Object.isExtensible(person1)); // false

person1.sayName = function() {
  console.log(this.name);
};

console.log("sayName" in person1); // false

Sealing - can’t add and remove properties

var person1 = {
  name: "Nicholas"
};

console.log(Object.isExtensible(person1)); // true
console.log(Object.isSealed(person1)); // false

Object.seal(person1);
console.log(Object.isExtensible(person1)); // false
console.log(Object.isSealed(person1)); // true
person1.sayName = function() {
  console.log(this.name);
};

console.log("sayName" in person1); // false
person1.name = "Greg";
console.log(person1.name); // "Greg"
delete person1.name;

console.log("name" in person1); // true
console.log(person1.name); // "Greg"

var descriptor = Object.getOwnPropertyDescriptor(person1, "name");
console.log(descriptor.configurable); // false

Freezing Objects - can’t add, remove properties and change properties; they are read only.

TBD

Object Constructing

Factory pattern


function createPerson(name) {
  var person = new Object();
  person.name = name;
  
  return person;
}

// objectcts of the class
var person1 = createPerson('Piotr');
var person2 = createPerson('Paulina');

// the type is defined
console.log(person1.name); // Piotr
console.log(person2.name); // Paulina

Constructor Pattern


// the Person constructor 
function Person() {
  this.name = 'Piotr';
  this.greeting = function(){
    return 'Hello, ' + this.name;
  }
}

// objectcts of the class
var person1 = new Person();
var person2 = new Person();

// the type is defined
console.log(person1 instanceof Person); // true
console.log(person1.name); // Piotr
console.log(person1.greeting()); // Hello, Piotr
console.log(person2 instanceof Person); // true
console.log(person2.name); // Piotr
console.log(person2.greeting()); // Hello, Piotr

Pros:

  • very simple

Const:

  • not effectively share methods across multiple objects

Constructor Pattern (with parameter)

function Person(name) {
  this.name = name;
  this.sayName = function() {
    console.log(this.name);
  };
}

var person1 = new Person("Nicholas");
var person2 = new Person("Greg");

console.log(person1.name); // "Nicholas"
console.log(person2.name); // "Greg"

person1.sayName(); // outputs "Nicholas"
person2.sayName(); // outputs "Greg"

Constructor Pattern (with private members)

function Person(name) {
  // define a variable only accessible inside of the Person constructor
  var age = 25;
  this.name = name;
  
  this.getAge = function() {
    return age;
  };
  this.growOlder = function() {
    age++;
  };
}

var person = new Person("Nicholas");
console.log(person.name); // "Nicholas"
console.log(person.getAge()); // 25

person.age = 100;
console.log(person.getAge()); // 25

person.growOlder();
console.log(person.getAge()); // 26

Encapsulation

Object literals


var age = 25;

var person1 = {
  getName: 'Piotr',
  getAge: function(){
    return age;
  }
}

age = 30;

var person2 = {
  getName: 'Paweł',
  getAge: function(){
    return age;
  }
}

Module pattern

var person = (function() {

  var age = 25;

  return {
    name: "Nicholas",
    getAge: function() {
      return age;
    },
    growOlder: function() {
      age++;
    }
  };

}());

console.log(person.name); // "Nicholas"
console.log(person.getAge()); // 25

person.age = 100;
console.log(person.getAge()); // 25

person.growOlder();
console.log(person.getAge()); // 26

Pros:

  • Aggregates API in one place
  • Expose only public members
  • Singleton implementation – through immediate invoke functions

Const:

  • Functions are duplicated across objects in memory
  • Not easy to extend and debug

Revealing Module Pattern

var person = (function() {

  var age = 25;

  function getAge() {
    return age;
  }

  function growOlder() {
    age++;
  }

  return {
    name: "Nicholas",
    getAge: getAge,
    growOlder: growOlder
  };

}());

Augmentation Module Pattern

var person = (function() {

  var age = 25;
  
  var api = {};
  api.name = "Nicholas";
  api.getAge = function() {
      return age;
  };
  api.growOlder = function() {
      age++;
  };

  return api;

}());

console.log(person.name); // "Nicholas"
console.log(person.getAge()); // 25

person.age = 100;
console.log(person.getAge()); // 25

person.growOlder();
console.log(person.getAge()); // 26

Immediately Invoked Function Expressions (IIFE)

Allow to mimic block scope and create private variables.

(function () {
	// ... all vars and functions are in this scope only
	// still maintains access to all globals
}());

Global Import

An example demonstrating injecting dependencies to IIFE.

(function ($, YAHOO) {
	// now have access to globals jQuery (as $) and YAHOO in this code
}(jQuery, YAHOO));

Module export

An example demonstrating basic module export using IIFE.

var MODULE = (function () {
	var my = {},
		privateVariable = 1;

	function privateMethod() {
		// ...
	}

	my.moduleProperty = 1;
	my.moduleMethod = function () {
		// ...
	};

	return my;
}());

Inheritance

Prototype pattern - add common functionality by modifying prototype

function Person(name) {
  this.name = name;
}

Person.prototype.sayName = function() {
  console.log(this.name);
};

var person1 = new Person("Nicholas");
var person2 = new Person("Greg");

console.log(person1.name); // "Nicholas"
console.log(person2.name); // "Greg"

person1.sayName(); // outputs "Nicholas"
person2.sayName(); // outputs "Greg"

Prototype pattern - add common functionality by replacing prototype

function Person(name) {
  this.name = name;
}

Person.prototype = {
  constructor: Person, // need to declare back the constructor as it would become an object
  sayName: function() {
    console.log(this.name);
  },
  toString: function() {
    return "[Person " + this.name + "]";
  }
};

var person1 = new Person("Nicholas");
var person2 = new Person("Greg");

console.log(person1 instanceof Person); // true
console.log(person1.constructor === Person); // true

console.log(person1.constructor === Object); // false
console.log(person2 instanceof Person); // true

console.log(person2.constructor === Person); // true
console.log(person2.constructor === Object); // false

Parasitic Combination Inheritance Pattern (prototype inheritance)

'use strict'

console.clear();

var Figure = (function(){
    function Figure(){
        console.log('Figure constructor called');
    }

    // all methods must go to prototype in order to effectively share them across multiple objects
    // those methods can operate onnly on the public properties that are inherited and that subtypes overrides
    Figure.prototype.getArea = function(){
        throw 'Not implemented'
    }

    Figure.prototype.toString = function(){
        return 'Figure';
    }    

    return Figure;
})();

var Rectangle = (function(FigureConstructor){
    function Rectangle(width, height){

        // constructor stealing
        FigureConstructor.call(this);

        //private variables
        var width = width;
        var height = height;

        //getters without setters make such object immutable and perfectly encapsulated
        //if setWitdh and setHeight would be created and exposed 
        //then the Square class should shadow both of them
        this.getWidth = function(){
            return width;
        }

        this.getHeight = function(){
            return height;
        }


        console.log('Rectangle constructor called');
    }

    //Class.prototype = FigureConstructor.prototype; - wrong because modify prototype of the Figure
    //Class.prototype = Object.create(new FigureConstructor()); wrong as it calls default constructor and creates
    //                                                          instance which state is shared among Rectangles and 
    //                                                          can be altered. If stealing also constructor then it's
    //                                                          called twice!!
    
    //inherits prototype Figure API but must remember to execute inherited constructor as well
    //this creates prototype chain Rectangle.prototype === Object -> Object.prototype === Figure.prototype
    Rectangle.prototype = Object.create(FigureConstructor.prototype) 
    Rectangle.prototype.constructor = Rectangle; //pointing back constructor
    
    //shadows but no replcaes Figure getArea
    Rectangle.prototype.getArea = function(){
        return this.getWidth() * this.getHeight();
    }

    Rectangle.prototype.toString = function(){
        return 'Rectangle';
    }    
    

    return Rectangle;
})(Figure);

var Square = (function(RectangleConstructor){
    function Square(size){
        RectangleConstructor.call(this, size, size);
        console.log('Square constructor called');
    }

    Square.prototype = Object.create(RectangleConstructor.prototype);
    Square.prototype.constructor = Square; //pointing back constructor
    
    Square.prototype.toString = function(){
        return 'Square';
    }    

    return Square;
})(Rectangle);



console.log('===== creating Figure');
var figure = new Figure('other');
console.log('figure name: ' + figure.toString());
console.log('figure instance Figure: ' + (figure instanceof Figure));
//console.log(f.getArea()); - exception

console.log('===== creating Rectangle')
var rectangle = new Rectangle(2,3);
console.log('rectangle name: ' + rectangle.toString());
console.log('rectangle area: ' + rectangle.getArea());
console.log('rectangle instance Rectangle: ' + (rectangle instanceof Rectangle));
console.log('rectangle instance Figure: ' + (rectangle instanceof Figure));


console.log('===== creating Square')
var square = new Square(4);
console.log('square name: ' + square.toString());
console.log('square area: ' + square.getArea());
console.log('square instance Square: ' + (square instanceof Square));
console.log('square instance Rectangle: ' + (square instanceof Rectangle));
console.log('square instance Figure: ' + (square instanceof Figure));

Output:

===== creating Figure
Figure constructor called
figure name: Figure
figure instance Figure: true
===== creating Rectangle
Figure constructor called
Rectangle constructor called
rectangle name: Rectangle
rectangle area: 6
rectangle instance Rectangle: true
rectangle instance Figure: true
===== creating Square
Figure constructor called
Rectangle constructor called
Square constructor called
square name: Square
square area: 16
square instance Square: true
square instance Rectangle: true
square instance Figure: true

Inheritance

Extensions

Mixins

Mixins occur when one object acquires the properties of another without modifying the prototype chain. The first object (a receiver) actually receives the properties of the second object (the supplier) by copying those properties directly.

function mixin(receiver, supplier) {
  for (var property in supplier) {
    if (supplier.hasOwnProperty(property)) {
      receiver[property] = supplier[property]
    }
  }
  return receiver;

}

JSON

  • JSON.stringify(value[, replacer [, space]]) - serialize JS object to JSON Examples:
JSON.stringify({});                  // '{}'
JSON.stringify(true);                // 'true'
JSON.stringify("foo");               // '"foo"'
JSON.stringify([1, "false", false]); // '[1,"false",false]'
JSON.stringify({ x: 5 });            // '{"x":5}'
JSON.stringify({x: 5, y: 6});        // '{"x":5,"y":6}' or '{"y":6,"x":5}'
JSON.stringify({ uno: 1, dos : 2 }, null, '\t')
// returns the string:
// '{            \
//     "uno": 1, \
//     "dos": 2  \
// }'
  • JSON.parse(text[, reviver]) - deserialize JS object from string Examples:
try {
  JSON.parse('{}');              // {}
  JSON.parse('true');            // true
  JSON.parse('"foo"');           // "foo"
  JSON.parse('[1, 5, "false"]'); // [1, 5, "false"]
  JSON.parse('null');            // null
} catch (e) {
  console.error("Parsing error:", e);
}

Tips and tricks

  • debugger; - stops debugger while execution this line of the code
  • document.designMode = 'on' - make the whole page editable

Ajax

Sample vanilla post call


xmlhttp=new XMLHttpRequest();
xmlhttp.onreadystatechange=function()
  {
  if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
    console.log("got reply: "+xmlhttp.responseText);
    }
  }
xmlhttp.open("POST","/api/costumes",true); xmlhttp.send();

Resources

https://addyosmani.com/resources/essentialjsdesignpatterns/book/