#Javascript Objects and Prototypes
##Chapter 1: Creating Javascript Objects
There are 4 ways of creating Javascript objects:
- Using Object Literals
var cat = {
name: 'Fluffy',
color: 'White'
}
display(cat.name);
- Using Constructor Functions
function Cat(name, color){
this.name = name;
this.color = color;
}
var cat = new Cat('Fluffy', 'White');
display(cat.name);
- Using Object.create
var cat = Object.create(Object.prototype,
{
name: {
value: 'Fluffy',
enumerable: true,
writable: true,
configurable: true
},
color: {
value: 'White',
enumerable: true,
writable: true,
configurable: true
}
})
display(cat.name);
- Using ES6 classes
class Cat{
constructor(name, color){
this.name = name;
this.color = color;
}
}
var cat = new Cat('Fluffy', 'White');
display(cat.name);
##Chapter 2: Javascript Object Properties
###Using bracket notation
var cat = {
name: 'Fluffy',
color: 'White'
}
display(cat.color);
//Bracket notation
display(cat['color']);
###Using Javascript Object Properties
Displaying properties of an object
var cat = {
name: 'Fluffy',
color: 'White'
}
display(Object.getOwnPropertyDescriptor(cat, 'name'));
>>Object{
value: Fluffy
writable: true
enumerable: true
configurable: true
}
- Writable Attribute
Defines whether property's value can be changed from initial value
Object.defineProperty(cat, 'name', {writable: false});
- Enumerable Attribute
By default, properties of an object are enumerable, i.e. We can loop over them. We can change that by setting enumerable to false.
Object.defineProperty(cat, 'name', {writable: false});
Setting enumerable attribute to false affects looping through the object and JSON serialization.
- Configurable Attribute
Configurable attribute locks down a property to prevent certain attributes from being changed.
Object.defineProperty(cat, 'name', {configurable: false});
You can change the writeble attribute even when the configurable attribute is set to false.
###Using Getters and Setters
'use strict'
var cat = {
name: 'Fluffy',
color: 'White'
}
Object.defineProperty(cat, 'fullName',
{
get: function(){
return this.name.first + ' ' + this.name.last;
},
set: function(value){
var nameParts = value.split(' ');
this.name.first = nameParts[0];
this.name.last = nameParts[1];
}
})
cat.fullName = 'Muffin Top';
display(cat.fullName);
display(cat.name.first);
display(cat.name.last);
##Chapter 3: Javascript Prototypes and Inheritance
###Using DefineProperty to define properties of a javascript object in runtime
'use strict'
Object.defineProperty(Array.prototype, 'last', (get: function(){
return this[this.length-1]
}});
var arr = ['red', 'blue', 'green']
var arr2 = ['one', 'two', 'three'];
display(arr.last);
display(arr2.last);
Output:
green
three
###What is prototype?
Definition: A function's prototype: A function's prototype is the object instance that will become the prototype for all objects created using this function as a constructor. An object's prototype: An object's prototype is the object instance from which the object is inherited.
A prototype is an object that exists on every function in javascript. It is just an empty object. Objects don't have a prototype property. They have a _proto_ property which points to the exact same object of it's parent function.
'use strict'
function Cat(name, color){
this.name = name;
this.color = color;
}
var fluffy = new Cat('Fluffy', 'White');
display(Cat.prototype);
display(fluffy.__proto__);
display(Cat.prototype === fluffy.__proto__);
Output:
Cat{
}
Cat{
}
true
###Instance vs Prototype
Let's say you have a function called Cat
function Cat(name, color){
this.name = name;
this.color = color;
}
Cat.prototype.age = 3;
You can check it's prototype age by typing
console.log(Cat.prototype.age);
which will return
3
Now, if you create an instances of that function
var fluffy = new Cat('Fluffy', 'White');
var muffin = new Cat('Muffin', 'Brown');
you can check age of each instance's prototype by typing
console.log(fluffy.__proto__.age);
console.log(muffin.__proto__.age);
which will return
3
3
This means that both prototype and __proto__ point to the same object.
However, the value in prototype object is not accessible if we loop through the object. You can verify this by typing
console.log(Object.keys(fluffy));
which will return
Array{
0: name
1: color
}
One interesting thing to note here is that, if you type console.log(fluffy.age)
, it also returns 3
. What happens here is that, when javascript doesn't find age
as a property of fluffy object, it checks it's prototype for the same, and upon finding, displays the value.
You can override this by creating a property of that object, e.g.
fluffy.age = 4;
console.log(fluffy.age);
which will return the output as
4
###Changing a function's prototype
Let's say we have the following code
'use strict'
function Cat(name, color){
this.name = name;
this.color = color;
}
Cat.prototype.age = 4;
var fluffy = new Cat('Fluffy', 'White');
If we type console.log(fluffy.age);
we get the result as 4
. Now, we can change the prototype of Cat function by adding the following code
Cat.prototype = { age: 5 }
You can see that adding this doesn't change the value of console.log(fluffy.age)
. This is because fluffy object was created before Cat function started pointing to another prototype.
If we create a new object now, we will see that it's prototype points to the new Cat prototype.
var muffin = new Cat('Muffin','Brown');
console.log(muffin.age);
which will give the result as 5
.
###Object Oriented structure using prototypal inheritance
'use strict'
function Animal(voice) {
this.voice = voice || 'grunt'
}
Animal.prototype.speak = function(){
display(this.voice)
}
function Cat(name, color){
Animal.call(this, 'Meow')
this.name = name;
this.color = color;
}
Cat.prototype = Object.create(Animal.-prptotype);
Cat.prototype.constructor = Cat;
var fluffy = new Cat('Fluffy', 'White');
display(fluffy.__prpto__.__proto__);
###Object Oriented structure using ES6 Classes
'use strict'
class Animal{
constructor(voice){
this.voice = voice || 'grunt';
}
speak(){
console.log(this.voice);
}
}
class Cat extends Animal{
constructor(name, color){
super('Meow');
this.name = name;
this.color = color;
}
}
var fluffy = new Cat('Fluffy', 'White');
console.log(fluffy.speak());
##Display Source Code
//Display.js Source Code
function display() {
for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] === 'object')
displayObject(arguments[i])
else
displayValue(arguments[i], true)
}
}
function displayObject(object) {
if (object == null)
displayValue('null')
displayValue(getTypeName(object) + ' {')
for(var propertyName in object) {
if (propertyName != 'constructor') {
displayValue(propertyName + ': ' + object[propertyName], false, true);
}
}
displayValue('}', true)
}
function displayValue(value, addMargin, addPadding) {
var div = document.createElement('div');
div.style.fontSize='32px'
if (addMargin)
div.style.marginBottom='30px'
if (addPadding)
div.style.paddingLeft='30px'
div.textContent = value;
document.body.appendChild(div)
}
function getTypeName(object) {
var funcNameRegex = /function (.{1,})\(/;
var results = (funcNameRegex).exec(object.constructor.toString());
return (results && results.length > 1) ? results[1] : "";
}