Mosh OOP Javascript course notes

Disclaimer: This content are my personal notes, and in no way endorsed by Mosh. I personally recommend Mosh's courses, without any reward from his part. I've been following along Mosh Hamedani's Object Oriented Javascript Class which I strongly recommend to quickly refresh or learn basic Javascript concepts that are the foundation for any more advanced programming with javascript.

CREATING OBJECTS

Factory function

function createCircle(radius) {
    return {
        radius,
        draw : function () {'Draw'},
    };
}

const circle = createCircle(1);
circle.draw();

Constructor function (calling a function with "new")

function Circle(radius) {
    // if called with new : this = an empty object {} with constructor Circle
    // if called without new : this = Window object or global if nodejs
    console.log('this : ', this);
    this.radius = radius;
    this.draw = function() {
        console.log('draw')
    };
    console.log(this);
}

The new Operator creates en Empty object and this references that empty object created by the constructor Circle().

const withNew = new Circle(2);

after calling a few this.radius = 2, this.etc = ..., inside the constructor, the object created with the function Circle() starts filling up. The Circle() function is actually a constructor, manly because it uses the keyword this in its body.

console.log('withNew:', withNew); // Circle {radius: 2, draw: f}

notice that the object is returned automatically, no need to return this;

Normal Function call, this is the global object: Window (in Browser) or Global (in nodejs)

const withoutNew = Circle(4);

withoutNew is undefined because there is no return statement and no new operator, so nothing is assigned to the withoutNew constant

console.log('withoutNew:', withoutNew); // undefined no return statement

the calls to this.radius = 2 etc. inside the body of the function Circle when called without the new operator, will actually create a global variable named radius and throw an error, letting you know that this is probably an error from the programmer.

console.log(radius); // it is defined and = 4

Factory vs. Constructor : religious debate

Should you be using the Factory pattern or the Constructor pattern? First, to distinguish between one and the other, the main difference is the use of a return statement to return a manually created object inside the Factory function, and the use of new operator to tell javascript to create a new object with constructor function without return.

If you forget the new operator when calling a function that uses this.someProp: javascript will throw. This is to avoid assigning unintentionally to the Window object in the browser or the Global object in nodejs.

Objects have a reference to their constructor

Every object in javascript has a reference to the function constructor that was used to instantiate it

console.log('withNew.constructor: ', withNew.constructor); // f Circle(radius) {...}

console.log('window.constructor', window.constructor); // f Window() { [native code] }

console.log('circle.constructor', circle.constructor); // f Object() { [native code] }

console.log('true.constructor', true.constructor) // f Boolean() { [native code] }
// or
console.log((new Boolean()).constructor); // f Boolean() { [native code] }

console.log(``.constructor); // f String() { [native code] }
// or
console.log((new String()).constructor); // f Boolean() { [native code] }

Even functions are objects

console.log(Circle.constructor); // f Function {[native code]}

const Circle1 = new Function('radius', `
    // if called with new : this = an empty object {} with constructor Circle
    // if called without new : this = Window object or global if nodejs
    console.log('this : ', this);
    this.radius = radius;
    this.draw = function() {
        console.log('draw')
    };
    console.log(this);
`);
const anotherCircle = new Circle1(11);
console.log(anotherCircle.constructor) // f anonymous(radius) {...}

Notice the difference, here the constructor is anonymous because we created the constructor function Circle1 using the builtin new Function (radius, ...). There is another variant which produces another similar result:

const Circle3 = function (radius) {
    // if called with new : this = an empty object {} with constructor Circle
    // if called without new : this = Window object or global if nodejs
    console.log('this : ', this);
    this.radius = radius;
    this.draw = function() {
        console.log('draw')
    };
    console.log(this);
}
const yetAnotherCircle = new Circle3(11);
See this is an unnamed function but not called anonymous
console.log(yetAnotherCircle.constructor) // f (radius) {...}

Functions are objects

Circle.call({}, 13); // first param specifies the target of this inside circle
// is equivalent to
new Circle(13);
// or equivalent to
Circle.apply({}, [13]); // the difference is that the second param is an array of the actual function parameters

so you can call apply if you have an array of params ready to pass to the constructor instead of destructuring the array, and passing manually each param as a parameter to the constructor.

Value types (primitives) & Reference Types

Value types

  • Number
  • String
  • Boolean
  • Symbol
  • undefined
  • null

Reference Types

  • Object
  • Function
  • Array

Difference between Value types and Reference types

What is the key difference between Value types and Reference types?

In the next example x and y are assigned Value types, so their content is absolutely independent, meaning that each have a different memory space to store their contents:

let x = 10;
let y = x;
x = 20;
// now assign Reference types
let v = {myProp : 10};
let w = v;
v.myProp = 30;
console.log(w.myProp); // 30
// See a more interesting example
let number = 20;
function incr(number) {
    number++;
}
incr(number); // does incr actually increase the global number variable?
console.log(number); // what is the logged value of number?

number is still 20, because Value types are passed by value. So inside the function, we only have access to the local number function parameter variable. Thus, our function increases the parameter, but not the outer variable number.

Would it be different if we changed the parameter name? Like:

function incre(someNum) {
    number++;
}
incre(10); // the parameter variable is not incremented, but is the global number variable?
console.log(number); // 21,

The global variable has been incremented, because there is no parameter of a closer scope, overriding the declaration of the globally scoped number var (and inside the block any outer scope variable is accessible).

Let me rephrase this last statement:

A function has access to the calling scope. In the example above, the function incre will only have access to number variable if it is accessible to the scope incre is being called from. In this example it is the global scope which has the particularity of being accessible everywhere. Note: you should avoid defining variables in the global scope. It's called global scope pollution, because you contaminate a common good to all developers and risk overriding unintended variables.

Lets rollback and keep the same example, but this time instead of passing a Value type parameter, we will pass a Reference type parameter and see what happens:

let obj = {amount:20};
function incrObjectAmount(obj) {
    obj.amount++;
}
incrObjectAmount(obj);
console.log(obj.amount); //21

see that amount has been increased on the global object? This is because it is passed by reference, and so obj parameter is actually a reference the same object that the global var obj is referencing, so any change is directly affected into the object. Both point to the same memory space.

To make things a bit clearer, here is the same example with same result but with different parameter name. The parameter name does not change anything.

let obj = {amount:20};
function incrObjectAmount(asdf) {
    asdf.amount++;
}
incrObjectAmount(obj);
console.log(obj.amount); //21

Object Properties

Here is an object:

const myCirc = new Circle(10);

we can dynamically add properties to already existing objects, there is no need to redefine the "class" like in more traditionally object oriented languages like Java, PHP, Python, etc.

myCirc.location = {x:1, y:2};

we can access properties with bracket notation, when the name of the property is not known in advance or has funny characters

let propName = 'location a 8 !';
myCirc[propName] = '';
console.log(myCirc); // has all the newly added properties

we can also delete properties, for example when we don't want to return all the data to the client that we got from db

delete myCirc[propName];
delete myCirc.location; // dot notation works as well
console.log(myCirc); // has two properties less

ES6 inheritance with classes

class Shape {
  move() {
    console.log('move');
  }
}

class Circle extends Shape {
  draw() {
    console.log('draw');
  }
}

const c = new Circle();

amazing, we no longer need to mingle with prototype and the prototype constructor in order to do proper inheritance.

Now lets create a more interesting example

Lets say we want to have a width for every shape

class ShapeBis {
  constructor(width) {
    this.width = width;
  }

  move() {
    console.log('move');
  }
}

class CircleBis extends ShapeBis {
  // we are not required to have a constructor
  // if we are not adding any constructor
  // functionality
  draw() {
    console.log('draw');
  }
}

const cb = new CircleBis();
console.log(cb); // CircleBis {width: undefined}
const cb2 = new CircleBis(23);
console.log(cb2); // CircleBis {width: 23}

class CircleTris extends ShapeBis {
  constructor(width, other) {
    // if we do add a constructor to the child
    // but we don't call super() from within the child
    // constructor, then we get a Uncaught Reference error
    // 'Must call super constructor in derived class
    // bofore accessing 'this' or returning from derived
    // constructor'
    // so to avoid Ref error we do:
    super(width); // removes the Ref Error

    // now that super() was called we can start using
    // the 'this' keyword from within the derived constructor:
    // NOTICE that we are using the this.width which is
    // actuall set inside the parent's constructor
    this.area = 2*Math.PI*Math.pow(this.width/2, 2);

    this.somethingElse = other;
  }

  draw() {
    console.log('draw');
  }
}

const ct = new CircleTris();
console.log(ct); // CircleBis {width: undefined}
const ct2 = new CircleTris(23, 'hey');
console.log(ct2); // CircleBis {width: 23, area: 830.951, somethingElse: 'hey'}
console.log(ct2.area);

------------------------------------------------------;

Method Overriding

class CircleFroth extends ShapeBis {
  // We can easily override a parent method by
  // defining it again inside the child
  move() {
    super.move(); // we can (but are not required) call the parent move
    console.log('circles move and do more stuff');
  }
}

const cf = new CircleFroth(12);

Why does this work?

cf.move();

BECAUSE remember that methods declared outside of the class constructor, are actually set on the prototype. So in the above code, the move() method in the child class is actually in the cf.__proto__.move(), and since it is a child class, cf.__proto__ has a __proto__ too: cf.__proto__.__proto__.move() is the parent move method. Everything works perfectly.

5. Private Members Using Symbols

The point of having private members is to enforce Abstraction, which states that objects should expose only the workable interface. Remember the DVD player only shows the play pause buttons.

Symbol() function returns a value of type symbol. The Symbol() function has static properties that can query the global symbols registry. The Symbol function resembles a built-in object class but is incomplete as a constructor because it does not support 'new' operator.

Every symbol value returned by Symbol() is unique symbol data type value's sole purpose is to be used as object property identifiers.

Symbol('foo') !== Symbol('foo') // true

const _radiusSym = Symbol(); // a unique symbol
const _myMethodSym = Symbol();

class Circle {
  constructor(radius) {
    // instead of using these publicly accessible props
    this.radius = radius;
    this['radius'] = radius;
    // conventional privacy which is BAD
    this._radius = radius; // BAD publicly accessible and modifiable
    // we can use symbols to 'fake' privacy by limiting accessibility
    this[_radiusSym] = radius; // will not appear on iteration
  }

  [_myMethodSym]() {
    console.log('using bracket notation to declare a method using a symbol');
  }
}

const c = new Circle(11);
c.radius; // accessible so not private
c._radius; // also accessible so not private, relies on convention

The symbol will not show up in

Object.getOwnPropertyNames(c); // ['radius', '_radius'] // there is no Symbol

We can still see the symbol in console.log but we cannot access it

console.log(c); // Circle {radius: 11, _radius:11, Symbol(): 11}

except with this hack

const mySymProps = Object.getOwnPropertySymbols(c);
console.log(mySymProps); // [Symbol()]
console.log(c[mySymProps[0]]); // 11

but this should not be used outside of a reflection library

Why don't we see the _myMethodSym in the symbols properties list of the object?

REMEMBER : methods defined in the body of the class are actually defined in the PROTOTYPE!!

const myPrototypeSymProps = Object.getOwnPropertySymbols(Object.getPrototypeOf(c));
console.log(myPrototypeSymProps); // [Symbol()]
console.log(c[myPrototypeSymProps[0]]); // f [_myMethodSym] { console .... }

---------------------------------------------------;

6. Private Members using WeakMaps

We want to turn the radius property into a private property

WeakMaps are named like so, because the keys are weak, meaning that if there are no references to the objects used as keys, then the entries for those keys in the map are deleted.

You can already see that we can create a weakmap for which we use this as key and map some value

const myObj = {hi:1};
const myFunc = function() {};
const myWM = new WeakMap();
myWM.set(myObj, 'some value'); // any value
myWM.set(myFunc, {h:[1,2,], f: function() {},}); // any value

//myWM.set(1, {}); //BAD -> only objects as keys otherwise can never garbage collect

const aNumberObject = new Number('123'); // aNumberObject !== 123
myWM.set(aNumberObject, 123); // works because aNumberObject is not the number 123

aNumberObject it's an object. When the object is deleted, so does the weakmap key

const _radius = new WeakMap();
const _myBadMeth = new WeakMap();
const _myOkMeth = new WeakMap();

class Square {
  constructor(radius) {
    // we can easily access _radius.get(this) to retrieve the radius
    // from within this class. But from the outside, if there is no reference
    // to const _radius, then it becomes inaccessible
    _radius.set(this, radius);

    // potentially BAD
    _myBadMeth.set(this, function(){
      return this;
    });

    // if called here it is also BAD, because 'this' inside the called function
    // references undefined because the function is not called with 'new'
    // nor called with the dot obj.func(), so 'this' is undefined due to 'strict mode'
    // enforced in classes. If we used < ES6 classes syntax, 'this' would reference
    // the global object instead of undefined if no strict mode was enforced
    console.log('myBadMeth inside constructor', _myBadMeth.get(this)());

    // Solution is to use ARROW FUNCTIONS which
    // have the particularity of using the 'this' value
    // of the enclosing function (here the enclosing is
    // the constructor function
    _myOkMeth.set(this, () => {
      return this;
    });
    console.log('myOkMeth inside constructor', _myOkMeth.get(this)());
  }

  draw() {
    // OK
    console.log(_radius.get(this)); // 13 works

    // BAD at call time, we will be able to get the method stored
    // in the weakmap in constructor, but we will get an undefined
    // when called, because in console.log(this), the 'this' is undefined
    // REMEMBER the rule, 'this' has the proper value on construction with 'new'
    // and when the method is called on the object like: obj.meth()
    console.log('myBadMeth outside constr', _myBadMeth.get(this)());
    // here we are getting the function
    let funcWithAThisInside = _myBadMeth.get(this);
    // then we call it lonely
    funcWithAThisInside(); // 'this' is undefined

    // OK at call time, the arrow function will inherit the
    // enclosing function's 'this' reference (here the constructor)
    // so 'this' will reference this object
    console.log('myOkMeth outside constr', _myOkMeth.get(this)());
  }
}

Technically we can access the private property if you have a reference to _radius but we can hide the reference to const _radius by using modules and only exporting the class and not the variables of the module

const s = new Square(13);
s.draw();

Using a SINGLE WEAK MAP

If we did not use distinct WeakMaps, we could only have a single entry in the WeakMap, so we should create an object to which we add all priv props

const _privWM = new WeakMap();
const _privProps = {};
class Hello {
  constructor(name, gender) {
    // Initialize the object used to store private properties
    _privWM.set(this, {});

    // let's store a few private props
    _privWM.get(this).name = name;

    _privWM.get(this).gender = gender;

    _privWM.get(this).myPrivMeth = () => {
      return this;
    };
  }

  sayIt() {
    // OK works
    console.log(`Hello ${_privWM.get(this).name}`, _privWM.get(this).myPrivMeth());
  }
}

ALTERNATIVELY a seemingly cleaner way MAJOR DRAWBACK, forgetting to store the _props into the WeakMap USELESS?, since we have a reference to _props, the WeakMap advantage of garbage collecting the pairs whose key's reference is lost, that is to say when the main object ref is killed we will not not have the benefit removing the object from memory?

const _privWm = new WeakMap();
const _props = {};

class HelloBis {
  constructor(name, gender) {
    // Initialize the object used to store private properties
    // tells that uses private properties
    _privWM.set(this, _props);

    // let's store a few private props
    _props.name = name;

    _props.gender = gender;

    _props.myPrivMeth = () => {
      return this;
    };
  }

  sayIt() {
    // OK works
    console.log(`Hello ${_props.name}`, _props.myPrivMeth())
  }
}

const h1 = new Hello('john', 'male');
h1.sayIt(); // Works perfectly
const h2 = new HelloBis('fanny', 'female');
h2.sayIt(); // Works perfectly

7. Getters and Setters

OLD

const _priv = new WeakMap();

Using Object.defineProperty(,,) and Shorthand Method Names

function VehicleOld(wheels) {
  _priv.set(this, {});
  // set a initial value from param
  _priv.get(this).wheels = wheels;
  // Using old syntax to define getters and setter for prop
  Object.defineProperty(this, 'wheels', {
    get: function() {
      return _priv.get(this).wheels;
    },
    set: function(number) { // when having a setter no need for 'writable'
      _priv.get(this).wheels = number;
    },
    enumerable: false,
    configurable: true,
  });
}
const vo = new VehicleOld(4);
console.log('initial vehicle', vo);
console.log('initial val should b 4:', vo.wheels); //4
vo.wheels = 2;
console.log('set wheels to 2, value should be 2:', vo.wheels); //2

OLDISH

Using Object.defineProperty(,,) and Shorthand Method Names

class VehicleSemiOld {
  constructor(wheels) {
    _priv.set(this, {});
    // set a initial value from param
    _priv.get(this).wheels = wheels;
    // Using new ES6 'shorthand method names' syntax
    Object.defineProperty(this, 'wheels', {
      get() { // shorthand method name
        return _priv.get(this).wheels;
      },
      set(number) { // shorthand method name
        _priv.get(this).wheels = number;
      },
      enumerable: false,
      configurable: true,
    });
  }
}
const vso = new VehicleSemiOld(6);
console.log('initial val should b 6:', vso.wheels); //4
vso.wheels = 2;
console.log('set wheels to 2, value should be 2:', vso.wheels); //2

Of course these two examples above could have been achieved by a simple this.wheels in the constructor, except that here we have set the 'enumerable' to false, which would not be the case for a simple this.wheels

Getters and Setters with ES6 syntax

ES6 has much less convoluted syntax than above, see below

const _priv = new WeakMap(); // already created above

class VehicleES6 {
  constructor(number) {
    // create a reference for this object's private props
    _priv.set(this, {});
    _priv.get(this).wheels = number;
  }

  get wheels() { // ES6 getter
    return _priv.get(this).wheels;
  }
  set wheels(number) { // ES6 setter
    if (number <= 0) throw new Error('Flying vehicles are not allowed');
    _priv.get(this).wheels = number;
  }
}

const vn = new VehicleES6(8);
console.log('initial val should b 8:', vn.wheels); //8
vn.wheels = 1;
console.log('set wheels to 1, value should be 1:', vn.wheels); //1

Class #9

Enumerating properties of an object

function Circle(radius) {
  this.radius = radius;
  this.draw = function () {
    console.log('draw');
  };
}

const circle = new Circle(10);

this will fetch all the properties and methods of the actual object (not the prototype)

for (let key in circle) {
  console.log(key, circle[key])
}

we can then filter the type of the key with the typeof operator

for (let key in circle) {
  if (typeof circle[key] !== 'function') {
    console.log(key, circle[key])
  }
}

to get the keys as a list we can do

let manualKeys = [];
for (let key in circle) {
  // and within the loop we can then filter and process
  // stuff depending on the key
  manualKeys.push(key);
}
console.log(manualKeys);

but if what you really want is just the full list of object keys (properties and methods) without inspecting or processing, then the builtin Object.keys(myObject) method is much faster

const keys = Object.keys(circle);
console.log(keys);

if ('radius' in circle) {
  console.log('there is a key named radius in object circle');
}

Summary, use:

  • for in to enumerate and do stuff on object keys
  • Builtin Object.keys(myObject) to just get the full list
  • in operator to test whether there is a certain key in the object

Class #10

Abstraction

In OOP, "abstraction" means that you should hide the inner workings of an object, and only expose a "safe" public interface. You don't want anyone to be mingling/altering/overriding your object properties creating unexpected behavior

function Circle2(radius) {
  this.radius = radius;

  this.defaultLocation = {x: 0, y: 0,};
  this.location = {};

  this.computeOptimalLocation = function() {
    for(let k in this.defaultLocation) {
      this.location[k] = this.defaultLocation[k];
    }
    this.location.x++;
    this.location.y--;
  };

  this.draw = function() {
    this.computeOptimalLocation();
    console.log('draw', this.location);
  };
}

const circle2 = new Circle2(10);

we don't want anyone calling this

circle2.computeOptimalLocation();

only when the draw() method is called do we want the this.computeOptimalLocation() to be called by our code

How to implement Abstraction in javascript? So how do we hide the details? So far we have been using this.asdf in our Circle function But what happens if we use normal let variables?

function Circle3(radius) {
  this.radius = radius;
  let privateVariable;
}

const circle3 = new Circle3(10);

we cannot access the local let variables from outside of the Circle function

console.log('privateVariable' in circle3); // false

So if we want to hide this.defaultLocation we do:

function CirclePriv(radius) {
  this.radius = radius;

  let defaultLocation = {x: 0, y: 0,};
  this.location = {};

  let computeOptimalLocation = function() {
    for(let k in this.defaultLocation) {
      this.location[k] = this.defaultLocation[k];
    }
    this.location.x++;
    this.location.y--;
  };

  this.draw = function() {
    computeOptimalLocation(); // notice that we removed the "this"
    console.log('draw', this.location);
  };
}
const circleWithPriv = new CirclePriv(10);

in the example above, do you think that when we call

circleWithPriv.draw(); // draw {}

the draw method will have access to computeOptimalLocation()?

Yes, and the the answer lies in Closures! But more on that later, because we have already covered a topic that allows us to see a problem in the code.

Why does

circleWithPriv.draw(); // draw {}

output an empty object for this.location?

Where is the this.location altered?

First it is defined inside the scope of the constructor function

This is why we can safely do:

console.log(circleWithPriv.location); // {}

And we get the location property with it's initial value which is {} But then why does the call to computeOptimalLocation() not change the contents of this.location?

function CirclePrivBis(radius) {
  this.radius = radius;

  this.defaultLocation = {x: 0, y: 0,};
  this.location = {};

  let computeOptimalLocation = function() {
    console.log(this.defaultLocation);
    console.log(this.location);
    for(let k in this.defaultLocation) {
      this.location[k] = this.defaultLocation[k];
    }
    this.location.x++;
    this.location.y--;
  };

  this.draw = function() {
    computeOptimalLocation(); // notice that we removed the "this"
  };
}

const circlePrivBis = new CirclePrivBis(10);
circlePrivBis.draw();

will print:

this.defaultLocation === undefined
this.location === Location {replace: f, assign : f, href: "http://t.js/mosh-oopjs", ...}

See what is going on?

Because the function computeOptimalLocation uses this and we are calling it without the new operator, the this keyword references the window object, and therefore this.location is actually equivalent to window.location

NOTE: if we run the script in "strict mode" with 'use strict'; we would get an: "Uncaught TypeError: Cannot read property 'defaultLocation' of undefined". This means that strict mode will set this to undefined instead of window which is convenient to avoid mistakes like this one.

How can we solve this issue? By not using the this keyword outside of object methods like this:

function CirclePrivSolved(radius) {
  this.radius = radius;

  // change to let instead of "this" all vars that
  // will be accessed in Constructor inner functions that
  // are not part of the constructed object methods
  let defaultLocation = {x: 0, y: 0,};
  let location = {};

  let computeOptimalLocation = function() {
    'use strict';
    // remove all "this" keywords
    for(let k in defaultLocation) {
      location[k] = defaultLocation[k];
    }
    location.x++;
    location.y--;
  };

  this.draw = function() {
    computeOptimalLocation();
    console.log('draw:', location);
  };
}

const circlePrivSolved = new CirclePrivSolved(10);
circlePrivSolved.draw(); // draw {x: 1, y: -1}

Closure

First off, the scope of a function is all the variables that have been defined within the function. The scope is created and killed every time the function is called. On the other hand a closure is a function defined within another function. The inner function has access to the parent function's variables and contrary to the scope, these parent variables stay in memory throughout the whole life of the inner function.

NOTE: the parent variables are within the scope of the parent function, and they are within the closure of the inner function The main difference between the SCOPE and the CLOSURE is that the SCOPE is temporary, it dies after the function execution, but the CLOSURE stays alive.

Getters and Setters

What if we want to be able to access defaultLocation variable from outside of the constructor function? Like: circlePrivSolved.defaultLocation Of course now we can't because it is not an object property, just a local variable to the constructor function. In an object oriented perspective we can actually call this a private property that we could expose like:

function CirclePrivExposed(radius) {
  //...
  let defaultLocation = {x: 0, y: 0,};
  //...
  this.getDefaultLocation = function() {
    return defaultLocation;
  };
}
const circle5 = new CirclePrivExposed(10);
console.log(circle5.getDefaultLocation());

'use strict';

But there is a nicer way that allows calling it like a normal property yet forbidding users to reset it

READ-ONLY property

function CirclePrivReadOnly(radius) {
  //...
  let defaultLocation = {x: 0, y: 0,};
  //...
  Object.defineProperty(this, 'defaultLocation', {
    get: function () {
      return defaultLocation;
    },
  });
}
const circle6 = new CirclePrivReadOnly(10);
console.log('circle6:', circle6.defaultLocation); // {x: 0, y: 0}
circle6.defaultLocation = 1; // changes nothing because there is no setter
console.log('circle6:', circle6.defaultLocation); //still {x: 0, y: 0}

READ-WRITE property

Let's create a Setter

function CirclePrivReadWrite(radius) {
  //...
  let defaultLocation = {x: 0, y: 0,};
  //...
  Object.defineProperty(this, 'defaultLocation', {
    get: function () {
      return defaultLocation;
    },
    set: function (value) {
      defaultLocation = value;
    }
  });
}
const circle7 = new CirclePrivReadWrite(10);
console.log('circle7:', circle7.defaultLocation); // {x: 0, y: 0}
circle7.defaultLocation = 1; // changes nothing because there is no setter
console.log('circle7:', circle7.defaultLocation); // 1

We are now allowing anyone to change the default location but most of the times we want to control the input, and that can be done through the the setter method by throwing an error like this:

function CirclePrivReadWriteControlled(radius) {
  //...
  let defaultLocation = {x: 0, y: 0,};
  //...
  Object.defineProperty(this, 'defaultLocation', {
    get: function () {
      return defaultLocation;
    },
    set: function (value) {
      console.log(value);
      if (typeof value !== 'object' || !('x' in value) || !('y' in value)) {
        // Error is a constructor for error
        throw new Error('value must have x and y properties');
      }
      defaultLocation = value;
    }
  });
}
const circle8 = new CirclePrivReadWriteControlled(10);
console.log('circle8:', circle8.defaultLocation); // {x: 0, y: 0}
circle8.defaultLocation = 1; // Uncaught Error: value must have x and y properties

every object has a reference to the constructor that was used to instantiate it

function Shape() {
}

Shape.prototype.duplicate = function () {
  console.log('duplicate');
}

function Circle(radius) {
  this.radius = radius;
}

---------------------------------------------;

--- Object.create() vs new FunctionName() ---

Object.create() creates a new object which inherits directly from PARAM

let PARAM = {};
let a = Object.create(PARAM);

new FuncName() inherits from FuncName.prototype

let b = new Shape();

To make both behave the same you can:

a = Object.create(Shape.prototype);
b = new Shape();
console.log(a, b); // same because no constructor content

This creates two objects which inherit (have a __proto__ === Shape.prototype so new X <=> Object.create(X.prototype) except that new X, will run the constructor function and Object.create() won't.

By running the constructor function of X, new X allows the constructor to set instance properties, and to return the actual object for the new expression

b = new Shape(); // Shape method is run and

its return value can be used as the return of new instead of the default this when there is no return

Ex :

function Hello() {
  this.hi = 'some content set on construction';
}
let c = Object.create(Hello.prototype);
let d = new Hello();
console.log(c, d); // different

c is an empty Hello (because no constructor was run) d is a Hello with hi property set to 'some content...'


Now that we know the difference between new and Object.create we understand why using Object.create is preferable in our case: Because we only want to get a specific proto; we don't want run the Shape constructor. Why?

we are resetting the prototype of Circle to an object whose prototype will be Shape.prototype

Circle.prototype = Object.create(Shape.prototype);

Once we reset Circle.prototype to be an object whose __proto__ is the desired parent (here Shape). Now we can start adding Circle specific stuff to the actual Circle.prototype whose __proto__ is Shape

Circle.prototype.draw = function() {
  console.log('we could draw something from the circle')
}

The new opertator will run the Circle function as a constructor

let e = new Circle(1);

but if we inspect the e object, we notice that there is no constructor in its direct prototype,

console.log(e.constructor === Circle);// false

and the only one available is actually further up in the inheritance chain in the Shape.prototype which is Circle.prototype.__proto__

console.log(e);
console.log(e.constructor); // f Shape

Q: Why is it that the new operator on Circle function did not store a constructor in e.__proto__

A: Very simple, the new operator is not in charge for that. The Hello.prototype was not altered like Circle's one, and therefore it should have a constructor property:

console.log(Hello.prototype.constructor); // f Hello

When we create the Hello function, a constructor property is directly added to its prototype property, and because we overwrite the Circle.prototype with an empty object whose __proto__ is Shape.prototype, any object instantiated from Circle, will not have a constructor property on its __proto__ which is Circle.prototype that we reassigned to Object.create(Shape.prototype)

How do we solve this lack of constructor?

Circle.prototype.constructor = Circle;

This will effectively create a property named constructor on Circle.prototype pointing to Circle function

Note: the problem is not that the new operator does not call Circle for construction. The problem is that even though Circle function is used as constructor, we loose the reference to it on the constructed object's __proto__ because we override the Circle's default prototype usually containing it.

That is why we simply reset it.

let f = new Circle(2);
console.log(f.constructor === Circle);// true

Summary

function Square(side) {
  this.side = side;
}
Square.prototype = Object.create(Shape.prototype);
Square.prototype.constructor = Square;
Square.prototype.getArea = function () {
  console.log(this.side*2);
};
let g = new Square(2);
g.getArea();
'use strict';

trying to use prototype chain for methods

function Stopwatch() {
  this.duration = 0;
  this.startTimeMs;
  this.started = false;
}

Stopwatch.prototype.throwIfRepeatedAction = function(action) {
  if (('start' === action && this.started)
    || ('stop' === action && !this.started)
    || ('reset' === action && this.duration === 0)) {
    throw new Error(`Cannot call ${action} twice in a row.`);
  }
};

Stopwatch.prototype.start = function() {
  this.throwIfRepeatedAction('start');
  this.startTimeMs = (new Date()).getTime();
  this.started = true;
}

Stopwatch.prototype.stop = function() {
  this.throwIfRepeatedAction('stop');
  this.started = false;
  this.duration += ((new Date()).getTime() - this.startTimeMs)/1000;
};

Stopwatch.prototype.reset = function() {
  this.throwIfRepeatedAction('reset');
  this.duration = 0;
}

const sw = new Stopwatch();

This prototypal implementation has actually created a few problems:

  1. We have exposed to the public 3 members (duration, startTimeMs, started)
    • violating the "clean interface principle": only expose the minimal necessary members required to interact!
  2. We have made 3 members WRITABLE!!
    • violating the "valid state principle": your objects should never lie about their state! Now one can directly alter sw.duration = 23 which would be a lie!

SOLUTIONS?

There are no solutions to this, since the prototype can only access publicly accessible members of their children?

LESSON

Sometimes we want to optimize something (that actually does not need an optimization, because probably only a few stop watch instances were going to be created, so trying to move methods to prototype was not going to reduce memory so much. However, by doing so we end up creating more problems than we solved.

don't solve what ain't broken! PREMATURE OPTIMIZATION IS THE ROOT OF ALL EVILS

'use strict';

function Stopwatch() {
  let duration = 0;
  let interval;
  let started = false;
  let increaseCounter = function () {
    duration++;
  };

  this.throwIfRepeatedAction = function(action) {
    if (('start' === action && started)
      || ('stop' === action && !started)
      || ('reset' === action && duration === 0)) {
      throw new Error(`Cannot call ${action} twice in a row.`);
    }
  };


  this.start = function() {
    this.throwIfRepeatedAction('start');
    interval = setInterval(increaseCounter, 1);
    started = true;
  }

  this.stop = function() {
    this.throwIfRepeatedAction('stop');
    started = false;
    clearInterval(interval);
  };

  this.reset = function() {
    this.throwIfRepeatedAction('reset');
    duration = 0;
  }

  Object.defineProperty(this, 'duration', {
    get: function() {
      return duration/1000;
    }
  });
}

const sw = new Stopwatch();

The implementation above is actually really overkill, or simply bad. This is because I am using a interval that calls a function every millisecond. A much smarter approach would have been to get the Date time at the start, and Date time at the end, and compute the difference.

start();

let startTime = new Date();

stop()

let endTime = new Date();
const seconds = (endTime.getTime() - startTime.getTime()) / 1000;

3 . Calling the Super Constructor

function Shape(color) {
  this.color = color;
}

Shape.prototype.duplicate = function() {
  console.log('duplicate');
}

function Circle(radius) {
  this.radius = radius;
}

Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;
Circle.prototype.draw = function() {
  console.log('draw');
}

const s = new Shape('green'); // color property is set to green
const c = new Circle(2); // color property not set, only radius

notice that we have added a color parameter to Shape's constructor but if we inspect c there is no reference to the color. It makes some sense since only Circle's constructor is called with the new operator.

Quick review of 'new' operator

When calling new operator, 3 things happen

  1. a new empty object is created
  2. this inside the constructor is assigned to that object
  3. the object is returned on the constructor

Note : if a constructor is called without the new operator, then this is assigned to the global object

How do we need to call Shape(color) from the Circle constructor in order make it work on the object created by the new operator called on Circle?

function CircleBad(radius, color) {
  Shape(color); // bad
  this.radius = radius;
}

const cb = new CircleBad(2, 'red');
console.log(cb.color); // undefined

the color property has been set on window obejct

console.log(window.color); // red...

------------------------------------------------;

THE RIGHT WAY to call the Super Constructor

function CircleGood(radius, color) {
  Shape.call(this, color);
  this.radius = radius;
}
CircleGood.prototype = Object.create(Shape.prototype);
CircleGood.prototype.constructor = Circle;
CircleGood.prototype.draw = function() {
  console.log('draw');
}
const cc = new CircleGood(2, 'green');
console.log(cc.color); // green! good

MyFunc.call(usAsThis, MyFuncParam1, ...) The call method takes the object that should be used as the 'this' and the other params are the function's parameters

--------------------------------------------------;

4. Intermediate Function Inheritance

We want to create an extend() method to avoid having to type:

Square.prototype = Object.create(Shape.prototype);
Square.prototype.constructor = Square;
function extend(Child, Parent) {
  Child.prototype = Object.create(Parent.prototype);
  Child.prototype.constructor = Child;
}

Let's create a square that inherits from Shape

function Square(size) {
  this.size = size;
}

extend(Square, Shape);

const sq = new Square(3);
console.log(sq.duplicate); // works, points to parent's method

--------------------------------------------------;

5. Let's consider method overriding, that means

we want to override a method from the parent that does not perfectly fit the needs of the child Here let's assume that Shape's duplicate method does not fit the Circle's needed behavior, so we are going to override it Really simple: just create that property on child's prototype after having "extended" the parent (reset the child's prototype with an object whose proto is the desired parent

Circle.prototype.duplicate = function() {
  console.log(`overriding the parent's duplicate method`);
}

const cd = new Circle(2);

cd.duplicate(); //`overriding the parent's duplicate method`

Notice that even though we did not overWRITE the parent's 'duplicate' method, Javascript will look at the prototypical chain for a property named 'duplicate' and will call the first one it finds starting from child and going up to the parents

Sometimes you want to call the implementation on the parents as well

Circle.prototype.duplicate = function() {
  // if not using 'this' in the parent method, then you can:
  // Shape.prototype.duplicate();
  // otherwise if using 'this' inside the parent method, then you have to
  Shape.prototype.duplicate.call(this);

  console.log(`overriding the parent's duplicate method`);
}

will execute parent and child duplicate methods

cd.duplicate(); // 'duplicate' and `overriding the parent's duplicate method`

--------------------------------------------------;

6. Polymorphism : "many forms"

Square.prototype.duplicate = function() {
  console.log('duplicating a square');
}

Now we have many objects extending the Shape object We have Circle and Square, and both have their own version of the 'duplicate' method. So we have many forms of the same method. This is what is called Polymorphism

This is very powerful because now we do not need to add an if statement to check whether the object is of a given type to call one or the other method, it is all builtin and javascript will know which method to call for what type of object. From this:

let myShapes = [sq, cd];
for (let shape of myShapes) {
  if (shape.constructor.name === 'Circle') {
    // CircleDuplicate(shape)
  } else if (shape.constructor.name === 'Square') {
    // SquareDuplicate(shape)
  } else {
    // duplicate for this kind not implemented
  }
}

Instead thanks to oop now we can simply

for (let shape of myShapes) {
  shape.duplicate();
}

This is pretty cool and is what we call polymorphism in action

--------------------------------------------------;

7. When to use Inheritance?

While inheritance is pretty powerful, be careful when you use it because it can make your code complex and fragile The base principle is : Keep it simple STUPID See Strategy pattern to see when to use something else called Composition

Avoid creating more than one layer of inheritance, Grandparents are way too old for anything By the Strategy pattern we should use Composition instead and this in javascript is done through Mixins

--------------------------------------------------;

8. Mixins : a way to apply the strategy pattern through composition

const canEat = {
  hunger: 10,
  eat: function() {
    this.hunger--;
    console.log('eating');
    return this.hunger;
  },
};
const canWalk = {
  walk: function() {
    console.log('walking');
  }
};
const canSwim = {
  swim: function() {
    console.log('swimming');
  }
};

function Person() {
}

Object.assign(Person.prototype, canEat, canWalk);

const p = new Person(10);
console.log(p.eat());

Now we can create a fish

function Fish() {
}

Instead of calling the Mixin manually, lets abstract it in a function the '...' is the 'rest' operator in this case, it will create a list from a set of parameters passed after the first

function mixin(target, ...sources) {
  // the '...' is the 'spread' operator in this case,
  // it will spread the list as if they were individual paramters
  Object.assign(target, ...sources);
}

Don't forget to pass .prototype

mixin(Fish.prototype, canEat, canSwim);

const f = new Fish(5);
console.log(f.eat(), f.swim()); // 'eating' 4 and 'swimming'

The Object.assign(target, ...sources) will actually assign all the 'source' object's methods to the 'target' object as if they had been defined on the target object itself.

One limitation we can see is: what happens if you want to set different levels on hunger depending on the kind of object? For example a person starts with a hunger of 10, whereas a Fish would start with a hunger of 5? We could already modify our initial mixins to be constructors

function CanEat(hunger) {
  this.hunger = hunger;
  this.eat = function() {
    this.hunger--;
    console.log('eating differently');
    return this.hunger;
  }
}

function GoldFish() {
}

function AmazingPerson() {
}

mixin(GoldFish.prototype, new CanEat(5), canSwim);
mixin(AmazingPerson.prototype, new CanEat(20), canWalk);

const gf = new GoldFish();
const ap = new AmazingPerson();

WORKS!

console.log(gf.eat()); // 'eating differently' 4
console.log(ap.eat()); // 'eating differently' 19

Now what happens if we want to set the hunger level on construction of the Fish or Person? (maybe there is no point for it, but let's ignore that for a moment) We could directly implement the 'this.hunger' inside the constructors like:

function MagicFish(hunger) {
  this.hunger = hunger;
}

mixin(MagicFish.prototype, canEat, canSwim);

const mf = new MagicFish(33);
console.log(mf.eat()); // 'eating' 32'

function SpecialPerson(hunger) {
  this.hunger = hunger;
}

mixin(SpecialPerson.prototype, new CanEat(12), canSwim);

The initial hunger = 12 gets overriden by 44

const sp = new SpecialPerson(44);
console.log(sp.eat()); // 'eating' 43

1. Modules

Modules are good because they allow:

Maintainability Reuse Abstraction : for example when using WeakMap, modules allow to hide the const

How to use modules?

In ES5 Module definition types:

  1. AMD (Asynchronous Module Definition) : used in Browser
  2. CommonJs : used in Node.js
  3. UMD (Universal module definition): used in Browser / Node.js

In ES6 4. ES6 Modules : used in Browser

We will only cover: 2. CommonJs for Node.js 4. ES6 Modules for Browser

THere is no need for the other two unless you are maintaining a legacy lib

NodeJs--------------------------------------------------

2. CommonJs Modules which are used in Node.js

Everything you define in a module is considered to be private To create a module you should move code to a separate file.js Place the code, and then at the end you should call: module.exports.Circle = Circle; module.exports.Square = Square; // or if you export a single thing you can do: // module.exports = Circle;

If inside the module file, we only export the Circle, then from within the importing file at the top do: // const Circle = require('./circle'); // This has the benefit of hiding everything else so we have abstraction in practice

Browser--------------------------------------------------

4. ES6 Modules

Create a file ./circle.js add the Circle class code by prefixing it with 'export' Then to import the module do :

import {Circle} from './circle';

IMPORTANT the code above will throw a : SyntaxError: Unexpected token {

There is a PROPER WAY to fix this issue using WebPack

But here we will use a workaround which consists of adding a type="module" to the <script tag see index.html This will make the browser treat main.js as a module and by treating main.js as a module, it can understand the {Circle} expression

If we reload we stil get a HTTP GET error becausee the browser is trying to GET ./cirlce instead of ./circle.js So if we change the code to:

import {Circle} from './circle.js'

Then it should work

--------------------------------------------------;

ES6 TOOLING

Transpiler : Translator + Compiler

Converts Modern ES6 (through BABEL) to ES5 that all browsers understand

Bundler Combines all javascript files into a single file (through WEBPACK) and uglifies the code

--------------------------------------------------;

BABEL

In order to use BABEL we need Node.js

Once we have node installed, We create a project folder

Then we need to initialize a node project in the folder:

>npm init --yes

This will create a file named package.json in the project folder

package.json: Is an identification of our application

Now we need to install Babel in the project

>npm i babel-cli@6.26.0 babel-core@6.26.0 babel-preset@1.6.1

Where: babel-cli: is the command with which we interact. We will pass our js files and babel-cli will send it to babel-core for transpilation to the correct js

babel-core: is the core of babel handling the transpilation

babel-preset-env: is a combination of all the plugins, so we don't have to worry that we have imported the correct plugin for the specific ES6 functionality

--save-dev: tells babel that all these will be used in the development environment and should not be deployed to production

Now once we have created some code inside main.js withing the project folder we can edit package.json

//package.json
{
  "name": "my-proj-name",
  //...
  "scripts": {
    "babel": "babel --presets env main.js -o build/main.js"
  },
  //..
}

This tells babel to transpile main.js and save it into the build/ folder so make sure to have created a ./build folder within the project directory

Now from the terminal within the project dir, we do:

>npm run babel

This will run the command keyed 'babel' inside the package.json "scripts"

WARNING this will only transpile a single file named main.js, but in a real world application we have thousands of files SO HOW DO WE TRANSPILE ALL FILES? By using Webpack

--------------------------------------------------;

WEBPACK

Installing webpack globally so we can use it in every project

>npm i -g webpack-cli@2.0.14

Now within our project folder we need to init Webpack

>webpack-cli init

It will ask you a few question, like

'using many bundles?' say No
'which module will be the first to enter the application?' [ex: ./src/index]
you should move index.js into a folder named src
'which folder will your generated bundles be in? [def: dist]' accept
'Are you going to use this in production? say No'
'Will you be using ES2015?' say Yes -> this will automatically install babel to

transpile our code into ES5

This is the cool thing about Webpack-cli: it will help generate the conf files and also install all the other libraries and tools that we need

Let's modify package.json to take make full use of webpack Notice that webpack-cli has added "dependencies" in it they are dev dependencies so they should be probably stored inside devDependencies, but this will probably change in future versions

//package.json
{
  "name": "my-proj-name",
  //...
  "scripts": {
    "build": "webpack"
  },
  //..
}

Now let's bundle our application:

>npm run build

See that webpack created a bundle named './build/main.bundle.js'

This is the file we will be serving in index.html So we need to change the file we request:

<script src="dist/main.bundle.js"></script>

IMPORTANT : To avoid having to call 'npm run build' every time we make a change to a js file, we can alter a slightly bit our package.json and add the -w option:

{
  "name": "my-proj-name",
  //...
  "scripts": {
    "build": "webpack -w"
  },
  //..
}

This will make webpack watch our files for any changes and rebuild after any change is saved

NOTE that now that we are using a bundle we can simply import without having to specify the .js This: // import {Circle} from './circle.js' // Can become this: // import {Circle} from './circle.js' Inheritance

Imagine that Circle is a class (not an object)

Circle computeOptimalLocation()

And we have a Square that needs the computeOptimalLocation() method

Square computeOptimalLocation()

You dont want to be copying the method in more than one place. There must be a way to have a single implementation, and any fix to the implementation should be done in one place and all the users of that method should get the fix update.

That is where inheritance comes. We create a class that contains the implementation and other classes will inherit from it. For example lets create a class Shape and make Circle and Square inherit from it.

Shape (Base/Super/Parent class) | computeOptimalLocatoin() | ---Square (Derived/Sub/Child class) | ---Circle (Derived/Sub/Child class)

So we say: Circle is a Shape. Or Square is a Shape This is the classical definition of inheritance. But in javascript there is no such thing as Classes, we only have Objects

Inheritance Classical vs. Prototypical inheritance

Prototypical Inheritance Circle and Shape were classes, but in javascript there are only objects,

How can we implement inheritance using only objects? We can create a shape object which implements the computeOptimalLocation() method, and somehow link the shape to the circle. By doing so, the shape becomes the prototype of the circle.

Every object (except one) in javascript has a prototype or parent Whenever you hear prototype, just think of parent

let x = {a:1};
console.log(x);

See that when you inspect x, there is a property called proto it is a bit faded because you should not access it (it is deprecated). It is just here for debugging purposes. This proto has a few properties, for example: Every object has a constructor. proto has a few properties

console.log(x.__proto__.toString());
console.log(x.__proto__.valueOf());

but we shouldn't access proto so lets see if we can somehow access the proto methods from x directly like:

console.log(x.toString()); //we can but see the output
console.log(x.valueOf());

What is interesting here is that the output of valueOf() is different if we call it from the proto than if we call it from the x object directly This is a sign that inheritance is happening, because valueOf() is defined to return all the ownProperties of the object, when we call it from the proto it will return the proto's properties, and when called from x it will return the x object properties.

The allowed way to access the prototype of an object is not with proto. Instead of _proto we have to use Object.getPrototypeOf(x);

console.log(Object.getPrototypeOf(x));

But since all (except one) objects have a prototype, does proto have a prototype?

console.log('__proto__' in x); // true
console.log('__proto__' in x.__proto__); // true
console.log(x.__proto__.hasOwnProperty('__proto__')); // true

So it seems that proto also has a proto, this means that there is an infinite chain of __proto__s, and if that were true, since it is an object, we would need infinite space to store all those objects. There is one possible explanation, that would avoid needing infinite space:

  • Is it that the proto of proto are both references to the same object (itself)?
let y = {}, z = {};
console.log('are y and z the same object?: ', y === z); // false
let yProto = Object.getPrototypeOf(y);
let zProto = Object.getPrototypeOf(z);
console.log('do y and z have the same proto?: ', yProto === zProto); // true
let yProtosProto = Object.getPrototypeOf(yProto);
let yProtos__proto__ = yProto.__proto__;
console.log('is proto of proto, proto itself?', yProto === yProtosProto); // false !! weird!
console.log('is __proto__ of proto, proto itself?', yProto === yProtos__proto__); // false !! weird!

How come the proto of proto is not proto itself. When do we arrive at the base object???

console.log('typeof yProto: ', typeof yProto);
console.log(yProto);
console.log('typeof yProtosProto: ', typeof yProtosProto);
console.log(yProtosProto); // null! Null is of type object..
console.log(yProtos__proto__); // still null! Object.getPrototypeOf and x.__proto__ are equivalent
let objectBase = Object.getPrototypeOf({}); // the parent/prototype of all objects (except itself)

Object.getPrototypeOf(yProtosProto); // TypeError: Cannot convert undefined or null to object So yProto and zProto do not have a proto, meaning that yProto and zProto must be the legendary baseObject: the parent of all objects, which has no prototype. IMPORTANT : In the developer tools console object inspector, it seems as though the prototype chain is infinite. This must be a feature of the inspector which creates a fake self prototype for the base object (to be verified, speculation) Maybe using the Object.defineProperty(this, 'proto', func...)

Let's inspect an array's prototype

let arrayBase = Object.getPrototypeOf([]);
let arrayBaseProto = Object.getPrototypeOf(arrayBase);
console.log('an array prototype: ', arrayBase);
console.log(`is array prototype' prototype, the base object?`, arrayBase === objectBase);

So our inheritance for an array looks like this: myArray -> arrayBase -> objectBase This is called multilevel inheritance It happens for every object in javascript: Arrays, Strings, custom etc.

Every object created from the Array() (or []) constructor, will inherit from arrayBase Similarly, every object created from the String() (or "") constructor will inherit from baseString

let stringBase = Object.getPrototypeOf("hello");
console.log(`stringBase the string's prototype: `, stringBase); // String {"", constructor: f, ...}
console.log(`stringBase's prototype is objectBase?`, Object.getPrototypeOf(stringBase) === objectBase); //true

What happens for custom objects? Objects whose constructors we have coded.

function Circle(radius) {
  this.raidus = radius;

  this.draw = function() {
    console.log('draw');
  }
}

let c = new Circle(2);
let cBase = Object.getPrototypeOf(c);
console.log(`c's prototype`, cBase);
console.log(`c's prototype own property names`, Object.getOwnPropertyNames(cBase)); // ["constructor"]

Object.keys() is like Object.getOwnPropertyNames() but only returns enumerable ones

console.log(`c's prototype own ENUMERABLE property names`, Object.keys(cBase)); // []

The above empty array means that the only own property: "constructor" has been defined as non enumerable, we can quickly change that via:

Object.defineProperties(cBase, {constructor: {enumerable: true,},});

Now Object.keys() returns exactly the same as Object.getOwnPropertyNames()

console.log(`c's prototype own ENUMERABLE property names`, Object.keys(cBase)); // ["constructor"]
console.log(`is the Circle function the prototype?`, cBase === Circle); // false!
console.log(`Circle function`, Circle); // a function
console.log(`c's prototype has a property called "constructor". Is it the Circle function?`);
console.log(cBase.constructor === Circle); // true!

So c's prototype has a reference to c's constructor. If you want to enumerate all the properties of cBase, they need to be ENUMERABLE and "constructor" is not enumerable, neither is "proto" But cBase has only two properties (both non-enumerable):

  • constructor
  • proto And we can again check that the proto of cBase is the main objectBase
console.log(`All objects lead at some point in the inheritance chain to objectBase`, Object.getPrototypeOf(cBase) === objectBase);// true
let c2 = new Circle(13);
let c2Base = Object.getPrototypeOf(c2);
console.log(`all objects constructed from the same function have the same prototype?`, cBase === c2Base); // true

When we call

let a1 = ['a']; // we are actually doing: let a1 = new Array('a')

which you can see is constructing an array from the Array constructor function. So all arrays have the same constructor, hence the same prototype

Property Descriptors

let person = {name: "John"};
console.log(`objectBase.toString() method, can be called from person`, person.toString());
let toStringDescriptor = Object.getOwnPropertyDescriptor(objectBase, "toString");

console.log('objectBase.toString() descriptor: ', toStringDescriptor); // see line below

prints the descriptor object these descriptor attributes determine a few things you can do to a property of an object each property has these descriptors { value: false, // (undefined) the value associated with the prop, any type writable: true, // (false) can overwrite: ob.toString = 'hello' enumerable: false, // (false) is returned in Object.getOwnPropertyNames(ob), // but is not returned in a "for in" or Object.keys(ob) configurable: true, // (false) can do: delete ob.toString } (by default all property descriptor attributes are set to false and undefined when using the Object.defineProperty(ob, "propHello", {value: 'hi'}) method.

BUT when doing a normal property addition through: ob.hello = "my value", all the descriptor attributes are set to true and the value to the value.

console.log(`being able to call a property does not mean it is enumerable`, toStringDescriptor.enumerable === false); // enumerable means it is listed by Object.keys() and "for in"

IMPORTANT: terminology. A "key" is an "enumerable own property". An "own property" is any member of an object without considering the prototype chain. The enumerability depends on the object "property descriptor": "enumerable.

we can get all "keys" (aka ENUMERABLE own properties) with Object.keys

let personEnumerableProperties = Object.keys(person);

"for in" is another way to get ENUMERABLE properties (own + prototypes) of an object is :

let personEnumerableProperties2 = [];
for (let k in person) {
  personEnumerableProperties2.push(k);
}

both return the same

console.log(personEnumerableProperties); // will only print 'name'
console.log(personEnumerableProperties2); // will only print 'name'

IMPORTANT the "in" operator has different meaning when used alone like below:

console.log(`toString is not enumerable but computes to true with the "in" operator`, "toString" in Object.keys(person)); // true

Why?? Because the "in" operator checks for the property in the whole prototype chain, and does not care for enumerability.


SUMMARY: | own | prototype chain

enumerable | in, gOPN(), for-in, keys() | in, for-in, keys()

non-enumer | in, gOPN() | in

// "in" : checks properties in the whole prototype chain (enumerable or not) "Object.getOwnPropertyNames(my)" : check for "own properties" (enumerable or not) properites in the current object without looking up the prototype chain "Object.keys(my)" and "for in" : checks for "enumerable properties (own + prototype)"

"Object.getOwnPropertyDescriptor(my, 'myprop')" gets the descriptor for a given property (here "myprop") of object "my" "Object.defineProperty(my, 'myprop', { value: 'hello', writable: true, enumerable: true, configurable: true })" is equivalent to my.myprop = 'hello'

Constructor Prototypes Every object except the baseObject has a prototype object.

Object.getPrototypeOf(person); // <=> person.__proto__ (deprecated)

You know that each object has properties inherited from its respective proto. Functions are objects whose proto is f () {[native code]} this native code ends up generating some javascript object with a few nice properties

let helloFunc = function () {
  console.log('hello');
};
console.log(`a simple function object`, helloFunc);
console.log(`list of properties of a function object`, Object.getOwnPropertyNames(helloFunc));

["length", "name", "arguments", "caller", "prototype"]

Every Constructor is a function, and since all functions have a property named: "prototype", every constructor function has a property named "prototype" remember that {} <=> new Object(), so Object is the constructor for any object therefore, it is a function, and it must have a property named "prototype" which should be equal to {}.proto

console.log(Object.prototype);
console.log({}.__proto__);
console.log(Object.prototype === {}.__proto__);

console.log(Array.prototype);
console.log([].__proto__);
console.log(Array.prototype === [].__proto__);

console.log(
  `therefore the Circle Constructor function must have a prototype property (!= __proto__)`,
  "prototype" in Circle // true
);
console.log(
  `but the objects that Constructors instantiate don't`,
  "prototype" in c // false
);

----- KEY PART

console.log(
  `Circle.prototype is what will be used as the __proto__ object
for the instantiated object`, 
  Circle.prototype === Object.getPrototypeOf(c) // true
);

console.log(
  `we did not explicitely set Circle.prototype so for the moment it is:`,
  Circle.prototype // {constructor: f}
);
console.log(
  `which is simply an object with the "constructor" enumerable property`,
  Object.getOwnPropertyNames(Circle.prototype), // ['constructor']
  `and the __proto_ non enumerable one`, 
  "__proto__" in Circle.prototype // true
);

Prototype vs Instance members

function Circle12(radius) {
  this.radius = radius;
  this.draw = function () {
    console.log('draw');
  };
}
const c121 = new Circle12(99);
const c122 = new Circle12(71);

When creating a new object, all own properties will be added to a dedicated space in memory, which is unique to that object. with each new object, a new space is used for its own properties. It makes sense so we can have different radius for different circles. But that also means that in the example above, "this.draw()" will be reated for every object that is created with the Circle12 constructor. In OOP langauges, methods belong to classes, therefore all objects in a class use methods from a shared space in memory (reserved for the class). HOWEVER in Javascript if we declare the method using the "this" operator, it will be an own property: the method memory will not be shared among objects from the same constructor; for each object a new space in memory is created, so the method ends up being duplicated as many times as there are objects.

console.log(
  `Own properties are instance specific space in memory`,
  Object.getOwnPropertyNames(c121),
  Object.getOwnPropertyNames(c122),
  `draw() is returned as own property therefore it is duplicated in memory`
);

SOLUTION: use the prototype to store methods. This will effectively serve as a "class". We can access properties from the prototype chain, directly from a child. HOW:

console.log(`is Circle.prototype writable?`, Object.getOwnPropertyDescriptor(Circle, "prototype").writable);

GOOD WAY:

Circle12.prototype.draw2 = function() {
  console.log(this.radius); // works because this.radius is PUBLIC
};
const c123 = new Circle12(23);
console.log(c123.draw2()); // 23

works, and even the this.radius references properly the object c123's property

WRONG WAY ---- : Can we override it?

Circle12.prototype = new (function() {
  this.draw3 = function () {
    console.log(this.radius*3);
  }
});
const c124 = new Circle12(13);
console.log(c124.draw3()); // 39, exactly what is expected

yes we can override it, and it works BUT we LOOSE any property that was present in the prototype object it actually gets moved one level deeper in the chain. Instead of belonging to the c124.proto, the constructor is moved up to c124.proto.proto, which is not much of a deal, but since it The REAL PROBLEM is that once we do this, we are actually changing the prototype object itself, meaning that any object created previously will point to the old prototype object, and any new object created with the updatd Circle12.prototype will point to the new prototype This will actually make two separate families of objects with diff protos ---- END of WRONG WAY

Instance members vs Prototype members We can easily override the toString() property

Circle12.prototype.toString = function() {
  return `Circle with radius ${this.radius}`
};

there is no restriction on referencing prototype properties from the object itself, or even referencing child properties from the prototype

IMPORTANT if we do it the GOOD way, then it does not make any difference to change prototype's members after construction of objects has already taken palce. Since the prototype is an object and any already created child will still have a reference to the prototype object, no matter what changes were made to its props.

console.log(
  `c124 does not have own property "hello": `,
  c124.hasOwnProperty('hello') // false
);
Circle12.prototype.hello = 123;
console.log(`Lets add "hello" property to Circle12.prototype after c124 has already been created`);
console.log(
  `c124 has property "hello"`,
  'hello' in c124 // true
);

WARNING : Avoid altering the builtin objects even by adding additional methods to their prototypes BAAAADDD:

Array.prototype.shuffle = function () {
  console.log('Shuffleing');
}

BAAADDD continued

[].shuffle();

Seems cool but, some one else in another library may have also modified the builtin objects and therefore you will end up pulling your hairs trying to figure out what it is that is creating issues in your code...


KEY TAKEOUT : DONT MODIFY OBJECTS YOU DON'T OWN

function HtmlElement() {
  this.click = function() {
    console.log('clicked');
  };
}

HtmlElement.prototype.focus = function() {
  console.log('focus');
};

const e = new HtmlElement();

function HtmlSelectElement(...els) {
  this.items = els;
  this.addItem = function(item) {
    if (false !== this.items.indexOf(item)) {
      this.items.push(item);
    }
  };

  this.removeItem = function(item) {
    let index = this.items.indexOf(item);
    if (index !== false) {
      this.items.splice(index, 1);
    }
  };

  this.render = function() {
    return `
<select>${this.items.reduce((acc, it) => acc + `
  <option>${it}</option>`, '')}
</select>`;
  };
}

HtmlSelectElement.prototype = new HtmlElement();

we still need to reset the constructor

HtmlSelectElement.prototype.constructor = HtmlSelectElement;

const s = new HtmlSelectElement(1,2,3);

function HtmlImgElement(src) {
  this.src = src;
  this.render = function() {
    return `<img src="${this.src}" />`;
  };
}

HtmlImgElement.prototype = new HtmlElement();
HtmlImgElement.prototype.constructor = HtmlImgElement;

const i = new HtmlImgElement('http://hello.com');

for (let el of [s, i]) {
  console.log(el.render());
}

Up until now we have seen prototypical inheritance But ES6 which is Javascript 2015 has added classes They are not all the same as Python and Java classes rather they are added syntactic sugar over the usual prototypical inheritance concepts that we have covered so far

Look at this known case

function CircleUsualWay(radius) {
  this.radius = radius;
  this.describe = function() {
    console.log('this is an instance method'); // will be copied over and over
  };
}
CircleUsualWay.prototype.draw = function() {
  console.log('draw is a prototype method');
};

Here is how we can achieve this case we have used so far

class Circle {
  constructor(radius) {
    this.radius = radius;
    this.describe = function() {
      console.log('this is how you achieve instance methods with new ES6 syntax');
    };
  }

  draw() {
    console.log('draw is a prototype method because it is outside constructor');
  }
}

const c = new Circle(10);

What is the type of Circle?

console.log(typeof Circle); // function!

even though we used the keyword 'class' to create the Circle class, Circle still ended up being a simple constructor function

Classes are syntactic shugar for what we have already learned. The only difference here, if we look at babel complier, is that classes have an added goodie, that if they are used without the 'new' operator then the compiler will complain and throw


  1. Hoisiting

Function expressions (NOT Hoisted)

const sayHello = function() {};

Function declaration (Hoisted)

function sayGoodbye() {}

What is the difference between these two? Function declarations get hoisted, that means that there is no problem if we call the function before it is declared, because the declaration gets hoisted to the top // On the other hand function expressions don't get hoisted and if you call it before you define it, the compiler will throw

class Expression (Not Hoisted, no one uses it)

const Square = class {};

class Declarations (Not Hoisted, use this one)

class Square {}

3. Instance Methods vs Static Methods

class CircleStatDemo {
  constructor(radius) {
    this.radius = radius;
    this.describe = function() {
      console.log('this is how you achieve instance methods with new ES6 syntax');
    };
  }

  // instance method (available thorough inst.draw)
  draw() {
    console.log('draw is a prototype method because it is outside constructor');
  }

  // adding the static keyword makes the method only available through the ClassName.meth
  static parse() {
    console.log('this method will only be available on class level with CircleStaticDemo.parse()');
  }
}

CircleStatDemo.parse(); // no need to create an instance to use it

Use static methods for utility functions that are not tied to the state of the object itself.


4. The this keyword

usually only when we enabled the 'strict mode' did we have the 'this' keyword assigned to undefined when a method using 'this' keyword was called without 'new meth()' or without the hey.meth()

in ES6, when using classes, the 'strict mode' is enabled by default


5. Private Members using Symbols

There are 3 different approaches to defining private members

  1. this._myPrivMemberBecauseISayIt (BAD) the first one which is acutally very bad is a simple convention but it violates the Abstraction principle that requires object to not expose parts that should not be accessed from the outside

  2. using ES6 symbols : NOTICE WE CANNOT USE THE NEW keyword on SYMBOL

const _radius = Symbol();
console.log(Symbol() === Symbol()); // false, Every call to Symbol gives a diffrent ref

const _priv = new WeakMap();
class Stack {
  constructor() {
    _priv.set(this, {});
    _priv.get(this).stack = [];
  }

  get count() {
    return _priv.get(this).stack.length;
  }

  push(el) {
    _priv.get(this).stack.push(el);
  }

  pop() {
    if (this.count <= 0) throw new Error('Stack is empty');
    return _priv.get(this).stack.splice(this.count - 1, 1)[0];
  }

  peek() {
    if (this.count <= 0) throw new Error('Stack is empty');
    return _priv.get(this).stack[this.count-1];
  }
}

const s = new Stack();

console.log(s.count);
s.push(1);
s.push(2);
console.log(s.count);
s.push(3);
console.log(s.count);
console.log(s.peek());
s.push(4);
console.log(s.peek());
console.log(s.pop());
console.log(s.peek());
console.log(s.count);
console.log(s.pop());
console.log(s.count);
console.log(s.pop());
console.log(s.count);
console.log(s.pop());
console.log(s.count);
console.log(s.pop());
console.log(s.count);

WORKS!

Summary of construction:

function MyConstr() {
  this.constrMember = function() { 
    console.log('constrMember function'); 
  };
}

this will create a variable MyConstructor (in current scope): of type Function MyConstr inherits all the properties from the FunctionBase < ObjectBase

MyConstr:

__proto__ = FunctionBase
|. __proto__ = ObjectBase

... inherits...

Function::prototype
ObjectBase::constructor -> points to MyConstr
...

and

MyConstr.prototype is an object and inherits from ObjectBase. Specifically:

__proto__ = ObjectBase

... inherits...

ObjectBase::constructor -> points back to MyConstr
...

now if we add:

MyConstr.prototype.myProtoMethod = function() { 
  console.log('myProtoMethod'); 
};

then it becomes

MyConstr.prototype:
__proto__ = ObjectBase
myProtoMethod -> function ... defined just above

... inherits...

ObjectBase::constructor -> points back to MyConstr
...

MyConstr.prototype has a reference to MyConstr through :

MyConstr.(Function::prototype).(ObjectBase::constructor) === MyConstructor

now the moment we have all been waiting for:

let myInst = new MyConstr;

myInst

__proto__ === MyConstr.prototype
|. __proto__ = ObjectBase
constrMember = copied from MyConstr() {this.constrMember = function() {...}}

... inherits...

MyConstr.prototype::myProtoMethod -> a method defined in the MyConstr.prototype
ObjectBase::constructor -> points back to MyConstr
...

Creating your own Prototypical Inheritance

In our example where we had a Circle being a Shape and a Square being a Shape too.

The main goal is to be able to have the "shapeBaseMethod" method defined in one place such that when we define our Square class, we don't need to redefine it again. This is why we go through the trouble of creating a class Shape, and then try to inherit its useful functions from Circle and Square.

How do we go about implementing this inheritance?

function Shape() {
  // this method is defined for illustration purposes only
  this.shapeConstructorMethod = function() {
    console.log('shapeConstructorMethod');
  }
}

We define the "shapeBaseMethod" method in the Shape prototype

Shape.prototype.shapeBaseMethod = function() {
  console.log('shapeBaseMethod');
}

function Circle(radius) {
  this.radius = radius;
}

Circle.prototype.circleBaseMethod = function() {
  console.log('circleBaseMethod');
}

const s = new Shape();
let c = new Circle(1);

With this, we have two objects s and c that inherit from their own base (i.e. s inheirts from shapeBase which is Shape.prototype (the default prototype for new objects customized with our method "shapeBaseMethod")). s.__proto__ (we call it shapeBase) inherits from objectBase

Object.getPrototypeOf(s) or Shape.prototype

let shapeBase = s.__proto__;

Object.getPrototypeOf(c) or Circle.prototype

let circleBase = c.__proto__; 

Object.getPrototypeOf(shapeBase) or Shape.prototype.proto Object.getPrototypeOf(circleBase) or Circle.prototype.proto

let objectBase = shapeBase.__proto__; 

or we could have gotten it from circle let objectBase = circleBase.proto;

console.log(
  `circleBase.__proto__ is actually === objectBase`,
  circleBase.__proto__ === shapeBase.__proto__,
  objectBase === circleBase.__proto__,
  objectBase === {}.__proto__
);

Now how do we go about giving circleBase access to all shapeBase methods? one way is:

circleBase.__proto__ = shapeBase
console.log(circleBase);
let asdf = new Circle(2);
console.log('i can call asdf.shapeBaseMethod', asdf.shapeBaseMethod);
console.log('can i call asdf.shapeConstructorMethod', asdf.shapeConstructorMethod); //undefined

shapeConstructorMethod does not get passed along since this process is done when we use the "new" operator on the constructor. During this process, the "new" will create an object whose __proto__ is Constr.prototype, and whose members are defined in Constr() { this.asdf = asdf, this.oiu = funct.. etc. } And since we are using Circle constructor instead of Shape, the shape constructor this.memebers are not transfered to the new object.

Now how do we go about making circleBase.__proto__ === shapeBase?

IMPORTANT: notice we are saying cirlceBase.__proto__ and not Circle.prototype. circleBase.__proto__ is Circle.prototype.prototype

console.log('circleBase proto is initially objectBase:', circleBase.__proto__ === objectBase);
console.log('circleBase proto is:', Circle.prototype);

meaning that c < circleBase < shapeBase < objectBase or equivale. c < Circle.prototype < Shape.prototype < Object.prototype

let initialCirclePrototype = Circle.prototype; // keep track of the initial

Can we make this test suite succeed:

let properPrototypeChainTests = [
  (o) => o.__proto__ === Circle.prototype,
  (o) => 'circleBaseMethod' in o,
  (o) => o.__proto__.__proto__ === Shape.prototype,
  (o) => 'shapeBaseMethod' in o,
  (o) => o.__proto__.__proto__.__proto__ === {}.__proto__,
];

works only if object instantiated after we reassign the Circle.prototype REMEMBER: MyConstructor.prototype is the Object that will be cloned and used as the new MyConstructor() return value, which is ultimately our newly instantiated object.

function MyConstructor(param) {
  this.ownAttribute = 'ownAttr:' + param;
  this.ownMethod = function() {
    console.log('ownMethod');
  };
}
MyConstructor.prototype.protoMethod = function() {
  console.log('protoMethod');
};
MyConstructor.prototype.protoAttribute = 'protoAttribute';

let my = new MyConstructor('constrParam');
console.log(my);

o = new MyConstructor();

WAY #1 BAD

Circle.prototype = Shape.prototype;

What is wrong with this? -> we are overriding the Circle.prototype Where is the Circle.prototype.circleBaseMethod method now? VANISHED! BAD! We want to be able to keep the Circle.prototype

c = new Circle(12);
console.log(`first test run`);
for (let test of properPrototypeChainTests) {
  console.log(test, test(c)); // true, false, false, true, false
}
console.log(`we trashed "circleBaseMethod" method`);
console.log(`instead of desired shapeBase, we made 2nd parent objectBase:`, c.__proto__.__proto__);
console.log(`instead of desired objectBase, we made 3nd parent null:`, c.__proto__.__proto__.__proto__);

reset to original

Circle.prototype = initialCirclePrototype

WAY #2

the key difference is that we are creating an object which uses the Shape.prototype as its proto

let objectHavingShapeAsProto = new Shape();

and we assign it to the Circle.prototype, this means that Circle.prototype is an object having Shape.prototype as its proto and also will have Shape's methods and members

Circle.prototype = objectHavingShapeAsProto;

now c < Circle.prototype < Shape.prototype < {}.proto

c = new Circle(12);
console.log(`second test run`);
for (let test of properPrototypeChainTests) {
  console.log(test, test(c)); // true, false, true, true, true
}

failed test is the Circle.prototype.circleBaseMethod method

console.log(`we still trashed "circleBaseMethod" method`);

We can fix this with:

Circle.prototype.circleBaseMethod = initialCirclePrototype.circleBaseMethod;
console.log(
  `And notice we are having all Shape's methods`,
  c,
  `So each new Circle has a __proto__ composed of 
the Circle.prototype methods combined with Shape's methods.
Then the Shapes.prototype is further down the chain`,
  `c: Circle < Circle.prototype + Shape < Shape.prototype < {}.__proto__`
);

If this is not the desired behavior then we can do the next way

reset to original

Circle.prototype = initialCirclePrototype

WAY #3.1 almost GOOD the key difference is that we are creating an object which uses the Shape.prototype as its proto instead

objectHavingShapeAsProto = new Shape.prototype();

or equivalently:

objectHavingShapeAsProto = Object.create(Shape.prototype);

and we assign it to the Circle.prototype, this means that Circle.prototype is an object having Shape.prototype as its proto

Circle.prototype = objectHavingShapeAsProto;

now c < Circle.prototype < Shape.prototype < {}.proto

c = new Circle(12);
console.log(`third test run`);
for (let test of properPrototypeChainTests) {
  console.log(test, test(c)); // true, false, true, true, true
}

failed test is the Circle.prototype.circleBaseMethod method

console.log(`we still trashed "circleBaseMethod" method`);

reset to original

Circle.prototype = initialCirclePrototype

WAY #2.2 completed GOOD

Circle.prototype = Object.create(Shape.prototype);

We can fix the missing circleBaseMethod method by doing: in practice we define the circle prototype methods after reassigning it to the parent prototype

Circle.prototype.circleBaseMethod = initialCirclePrototype.circleBaseMethod;

c = new Circle(12);
console.log(`thrid test run`);
for (let test of properPrototypeChainTests) {
  console.log(test, test(c)); // true, true, true, true, true
}

The proper way to go is to:

  1. Define the Parent class
  2. Define the Parent prototype methods
  3. Define the Child class
  4. reset the Child.prototype = Object.create(Parent.prototype)