Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
..
Failed to load latest commit information.
README.md
js-objects-prototypes_notes.pdf

README.md

LESSON: js-objects-prototypes


2018-06-11

Tags: javascript, objects, constructors, prototypes

OBJECTIVES

  • Define methods on custom objects by attaching them to the prototype

VOCABULARY

Constructor: A type of Javascript function with a capital first letter which return a new copy of an object with properties and methods defined in the Constructor. Within a Constructor function, you use the this keyword to bind properties and methods to objects created by it. Using a Constructor function automatically generates a prototype object linke to that Constructor function.

Instantiate: To make a new copy of an object based that has the properties and methods that are defined for that object type in Constructor function and in the Construtor function's prototype object.

Instance: A new copy of an object (ie what is created when you instantiate an object).

Prototype: A special kind of Javascript object that gives particular instances of object their built-in methods and properties. When you use the new keyword to instantiate an object, the new object will have all of the methods and properties in the prototype.

Inheritance: Objects inherit methods/properites from their prototypes. One crazy thing about prototype objects is that they too can inherit from their own prototype objects!

Deduplicate/Dedupe: To not repeat the same or very similar code when you can just reuse the code instead. A great example of this is using a Constructor function for objects that share the same structure (same keys), instead of explicitly making two objects with two separate object assignments.

Dunder variable: "double underscore" variable of the form __someVar__ . In JavaScript, these are normally variables that are made by JS and used under the hood to achieve functionality, ie not meant to be accessed directly most of the time. __proto__ is a dunder variable that links an instance to the Constructor's prototype object.

Importing/Exporting Modules: Javascript modules are basically just scripts that whose variable can be accessed in other scripts. When we set up a Javascript script as a module, we assign a value to module.export within the script to indicate which variable(s) to export. In a script that wants to import from a module, we use require(<relative path to module>) to import variable(s) exported by said module.

Expression: In Javascript, any piece of code that runs and returns a single value is called an expression. Expressions can be used in places where we would normally use simple values such as in function arguments, as expressions are first evaluated to whatever their return value will be, then that return value is what is passed to the function.

Statement: A statement is a block of Javascript code. It runs and doesn't necessarily return a value. A statment can contain expressions within it.

Evaluate: To run a statement/expression.

NOTES

  • It is a much better pattern to put methods for an object type in the object Constructor prototype instead of using this.<method name> in the Constructor itself, as this.<method name> will make a new function every time you make an object, wheras putting the method in the prototype will make one and only one copy of the method that all instances can access. Deduplicate!
  • When a method/property is accessed within an object, Javascript will first look at that object to see if that method/property exists. If not, it will look at the prototype for that object to find it, and if not, it will search in the prototype of that object to find it etc... It will search "up the chain" of inheritance to try and find the method/property, and it will use the method/property that is closest to the object that was being used.
  • A Constructor function ’s prototype object can be found by using <my object>.prototype, however the actual methods and properties given to an instance from <my object>.prototype are located in the object itself under <my object>.__proto__
  • Here is a great, if somewhat intermediate/advanced discussion of one object creation methods we have talked about (Constructors), as well as two we have not (Classes and Factory functions). The takeaway: don’t use Classes! (not required reading!)
  • A little more depth on Javascript Prototypes (not required reading!)
  • node allows us to import variable from modules by
    1. exporting the values from one of the scripts using module.exports
    2. Importing those value in another script by using require(<relative path to export script>)

EXAMPLES

Constructors and Prototypes

  • A simple Constructor function:

    const Person = function(name) {
        this.name = name
        this.sayName = function() {
            console.log(`my name is ${this.name}`)
        }
    }
  • Instantiating an object from a Constructor function:

  • const me = new Person('Arjun')
    me.sayName()
    // LOGS:
    // "my name is Arjun"
  • Adding a method to all of our Person objects using Person.prototype

  • Person.prototype.singASong = function () {
        console.log('Happy Birthday to You!')
    }
    
    me.singASong()
    // LOGS:
    // "Happy Birthday to You!"
  • Viewing the Constructor prototype:

  • console.log(Person.prototype)
    // LOGS:
    // {singASong: ƒ, constructor: ƒ}
    
    /* 
    Note that an instance does not have a prototype property 
    */
    console.log(me.prototype)
    // LOGS:
    // undefined
  • Viewing an instance's inheritied prototype properties:

  • console.log(me.__proto__)
    // LOGS:
    // {singASong: ƒ, constructor: ƒ}
  • Objects inherit from their prototypes. If they do not contain a method or property, they can still search their prototypes for that method or property:

  • /* notice that there is no 'singASong' method directly in the me object */
    console.log(me)
    // LOGS:
    // Person {name: "Arjun", sayName: ƒ}
    
    /* However me.singASong() works because Javascript searches in me.__proto__ for the singASong method, and since it exists, it runs it */
    console.log(me.__proto__.singASong)
    // LOGS:
    // ƒ () {
    //    console.log('Happy Birthday to You!')
    // }
    
    me.singASong()
    // LOGS:
    // "Happy Birthday to You!"

Modules Import/Export

  • in node you can import variables from script files using the following syntax:

    randomMethods.js

  • /* 
    file that will export some methods 
    */
    
    const helloWorld = function () {
        console.log('hello world!')
    }
    
    const foo = function () {
        console.log('bar')
    }
    
    /* 
    variables exported using module.exports can be
    imported in other scripts using require
    
    Note that the code below is a shorthand for:
        module.exports = {
            helloWorld: helloWorld,
            foo: foo
        }
    where the name of the keys in the export object
    are the names of the variables being exported in
    this script, and the values are simply the two
    functions we are exporting
    */
    
    module.exports = {
        helloWorld,
        foo
    }

    importScript.js

  • /* 
    This script will import the methods from randomMethods.js that are exported using module.exports, allowing us to run them
    */
    
    const randomMethods = require('randomMethods.js')
    
    console.log(randomMethods)
    // LOGS:
    // {helloWorld: ƒ, foo: ƒ}}
    
    randomMethods.helloWorld()
    // LOGS:
    // "hello world!"

Statements/Expressions

  • Here is an example of an Expression:

  • "hello " + "world"
    // RETURNS:
    // "hello world"
    
  • We can assign a variable using an statement containing an expression. Notice how the expression is evaluated first, and the value returned from the expression is what is assigned to our variable in the statement:

  • /*             expression
                  <---------->
            statement
    <------------------------>     */
    const myNum = 24 * 36 / 25
    
    
    console.log(myNum)
    // LOGS:
    // 34.56
  • Anothe way an expression can be used is by calling a function within a statement. Notice how the expression is evaluated first, and then it's returned values is what is logged in the statement:

  • function shout(text) {
        return text.toUpperCase()
    }
    
    
    /*                 expression
                 <-------------------->
                 statement
    <--------------------------------->     */
    console.log(shout('stop shouting'))
    // LOGS:
    // "STOP SHOUTING"

Lab Code

Here is the code we used today while going over the Lab.

  • lib/runs.js

  • 'use strict'
    
    const User = function (name, email) {
      this.name = name
      this.email = email
      this.runs = []
    }
    
    const Run = function (date, distance, timeTaken) {
      this.date = date
      this.distance = distance
      this.timeTaken = timeTaken
    }
    
    User.prototype.totalDistance = function () {
      let result = 0
      for (let i = 0; i < this.runs.length; i++) {
        result += this.runs[i].distance
      }
      return result
    }
    
    User.prototype.longestRunDistance = function () {
      let result = this.runs[0].distance
      for (let i = 1; i < this.runs.length; i++) {
        if (this.runs[i].distance > result) {
          result = this.runs[i].distance
        }
      }
      return result
    }
    
    User.prototype.averageSpeed = function () {
      let totalTime = 0
      for (let i = 0; i < this.runs.length; i++) {
        totalTime += this.runs[i].timeTaken
      }
      return this.totalDistance() / totalTime
    }
    
    module.exports = {
      Run,
      User
    }
  • and the code we ran in node to import and use the runs.js module:

  • /* 
    NOTE: each line was run separetly in node 
    */
    
    const runs = require('./lib/runs.js')
    
    const mazerunner = new runs.User('Mazerunner', 'mazerunner@gmail.com')
    
    mazerunner.runs.push(new runs.Run('2018-06-09', 10, 1))
    
    mazerunner.runs.push(new runs.Run('2018-06-10', 20, 2))
    
    mazerunner.totalDistance()
    // LOGS:
    // 30