<h1>View the console for the output!</h1>
<h1>If you're on Mobile, open this on CodePen!</h1>
html {
  font-family: Quicksand;
}
//a basic object
let Console = {
  name: "Machine",
  manufacturer: "Company",
  year: 1990,
  controllerPorts: 2
};

console.log(Console);
console.log(Console.name);

//constructor to make a new object of Car
//this constructor receives arguments
//Car is a supertype of 'tesla'
function Car(brand, model, automaticBool, fuelType, numWheels) {
  //'this' makes code reusable
  //these properties are called 'own' properties - they're defined on the instance object
  this.brand = brand;
  this.model = model;
  this.automaticBool = automaticBool;
  this.fuelType = fuelType;
  this.numWheels = numWheels;
}

//prototype - these properties are shared across ALL instance of Car
//can even run this code inside the function for creating a new object instace
//Car.prototype.numDoors = 5;

//creating prototypes individually can become tedious after a few properties
//set the prototype to a new object to make things easier
Car.prototype = {
  //REMEMBER TO SET THE CONSTRUCTOR PROPERTY WHEN CHANGING THE PROTOTYPE
  //If you don't - it erases the constructor property!
  constructor: Car,
  setPrice: function (price) {
    this.price = price;
  },
  setSeats: function (seats) {
    this.seats = seats;
  },
  setDoors: function (doors) {
    this.numDoors = doors;
  },
  printProperties: function () {
    console.log("Brand: " + this.brand);
    console.log("Model: " + this.model);
    console.log("Gear transmission: " + this.automaticBool);
    console.log("Fuel type: " + this.fuelType);
    console.log("Number of wheels: " + this.numWheels);
    console.log("Number of doors: " + this.numDoors);
    console.log("Number of seats: " + this.seats);
    console.log("Price: " + this.price);
  }
};

//creating a new object using the new operator
//this new object is an instance of it's constructor
let tesla = new Car("Tesla", "Model 3", true, "Electric", 4);

//calling functions to add and set values
tesla.setPrice(40000);

tesla.setSeats(5);

tesla.setDoors(5);

//displaying the object
console.log(tesla);

//access properties in a object with dot notation
console.log(tesla.brand);

//calling a function from the prototype
tesla.printProperties();

//verify if object is instance of constructor - will return true or false
console.log(tesla instanceof Car);

let ownProps = [];
let prototypeProps = [];

//for every property on the instance of an object, check if property is own or prototype, then push to appropriate array
for (let property in tesla) {
  if (tesla.hasOwnProperty(property)) {
    ownProps.push(property);
  } else {
    prototypeProps.push(property);
  }
}

console.log(ownProps);
console.log(prototypeProps);

//the constructor is a special property that is located on object instaces
console.log(tesla.constructor === Car);

//a function to check
function constructorCheck(objectName) {
  //if the constructor property is erased when changing to prototype - this returns false
  if (objectName.constructor === Car) {
    return true;
  } else {
    return false;
  }
}

//return false
console.log(constructorCheck(tesla));
//return false
console.log(constructorCheck(Console));

//an instance of an object will inherit the prototype from the constructor function that created it
console.log(Car.prototype.isPrototypeOf(tesla));

//all objects in JS have a prototype (with a few exceptions). In fact, an objects prototype is an object in itself
//this prints object
console.log(typeof Car.prototype);

//So this means that a prototype can have it's own prototype!
//this prints true
console.log(Object.prototype.isPrototypeOf(Car.prototype));

//inheritance
//parent object
function Animal() {}
Animal.prototype.eat = function () {
  console.log("nom nom nom");
};
//Bird is a constructor that inherits from Animal
function Bird() {}
Bird.prototype = Object.create(Animal.prototype);
Bird.prototype.constructor = Bird;
//this adds behaviour that is unique to bird
//so this function can't be used in the animal object!
Bird.prototype.fly = function () {
  console.log("I'm flying!");
};

let parrot = new Bird();

parrot.eat();
parrot.fly();

//we can override inherited methods
Bird.prototype.eat = function () {
  console.log("peck peck peck");
};

parrot.eat();

//mixin function - any object can use this!
let mixinFunction = function (obj) {
  obj.printMessage = function () {
    console.log("Hi, I'm a test!");
  };
};

mixinFunction(parrot);
parrot.printMessage();

//closure - protecting properties
function Computer() {
  //private variable
  let powerOnStatus = false;

  //public method that instances of Computer can use
  //method to get the value held
  this.getPowerOnStatus = function () {
    return powerOnStatus;
  };
  //method to change the value held
  this.setPowerOnStatus = function (state) {
    powerOnStatus = state;
  };
}

let mac = new Computer();
// returns undefined
console.log(mac.powerOnStatus);
//returns false
console.log(mac.getPowerOnStatus());
//change to true
mac.setPowerOnStatus(true);
//return true
console.log(mac.getPowerOnStatus());

//Immediately Invoked Function Expression (IIFE)
//this function will execuate as soon as it is declared!
(function () {
  console.log("Boom!");
})();

//modules
let funModule = (function () {
  return {
    isCuteMixin: function (obj) {
      obj.isCute = function () {
        return true;
      };
    },
    singMixin: function (obj) {
      obj.sing = function () {
        console.log("Singing to an awesome tune");
      };
    }
  };
})(); //the two parentheses cause the function to be invoked immediately

funModule.singMixin(parrot);
parrot.sing();

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.