Created
March 30, 2015 18:30
-
-
Save billmei/a83f5002262e2e898a93 to your computer and use it in GitHub Desktop.
Lesson plan for teaching JavaScript prototypes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Floobits: | |
// https://floobits.com/Kortaggio/prototypes | |
// Inheritance Diagram: | |
// https://docs.google.com/drawings/d/1PTgRq2UPD0LilvZOFIpTWUsgzGo1iN7XdL7X6BXvA-U/edit?usp=sharing | |
// Hi guys! Today I'm going to tell you about prototypes in JavaScript. | |
// Before I get started, I want to ask you a question: | |
// How many of you have: | |
// - started a business? | |
// - you're taking this course because you want to start a business? | |
// Let's open a new business: a lemonade stand! | |
function Lemonade () { | |
} | |
/////////////////////// | |
// REVIEW OF OBJECTS // | |
/////////////////////// | |
// This is a constructor. | |
function Lemonade (brand) { | |
this.price = 2; | |
this.ingredients = { | |
"lemons" : 5, | |
"tbsp of sugar" : 16, // 1 cup | |
"cups of water" : 8 | |
}; | |
// Sidenote: If this was done in person in front of a real class, | |
// I would actually bring in a jug of store-bought lemonade for the class | |
this.servings = 3; | |
this.isCold = true; | |
this.brand = brand; | |
} | |
// 2 minutes | |
// Now, if you want to serve someone a glass of lemonade: | |
var delsCoolLemonade = new Lemonade("Del's Cool Lemonade"); | |
console.log(delsCoolLemonade); | |
/* { | |
ingredients: { lemons: 5, 'tbsp of sugar': 16, 'cups of water': 8 }, | |
servings: 3, | |
price: 2, | |
brand: 'Del\'s Cool Lemonade', | |
isCold: true | |
} */ | |
////////////////////////////////////////// | |
// EXPLAIN WHY PROTOTYPES ARE IMPORTANT // | |
////////////////////////////////////////// | |
// For the people in this room that | |
// are thinking about starting their own business, I'll show you how prototypes | |
// can save you a lot of work when setting up your lemonade stand | |
// Your business takes off, and your customers want more drinks! | |
// Lets start serving orange juice and apple juice too: | |
function OrangeJuice () { | |
this.price = 1; | |
this.ingredients = { | |
"large oranges" : 6 | |
}; | |
this.servings = 3; | |
this.isCold = true; | |
} | |
var delsOJ = new OrangeJuice(); | |
function AppleJuice () { | |
this.price = 1; | |
this.ingredients = { | |
"large apples" : 4, | |
"tbsp of sugar" : 6, | |
"cups of water" : 6 | |
}; | |
this.servings = 3; | |
this.isCold = true; | |
} | |
var delsAppleJuice = new AppleJuice(); | |
// This is not efficient, we're repeating ourselves | |
// There are many shared properties between the three drinks. | |
// They're all juices! Lemonade is really just like "lemon juice" | |
// Let's create a Juice class | |
function Juice () { | |
this.servings = 3; | |
this.isCold = true; | |
} | |
///////////////////////// | |
// EXPLAIN INHERITANCE // | |
///////////////////////// | |
// We need to make OJ, AJ, and Lemonade have the same properties from our | |
// Juice object. | |
// PROTOTYPES | |
// "What type of thing is this?" | |
// The answer is, OJ, AJ, Lemonade, this is a "type of juice" | |
// In JavaScript world, this is known as "prototype" | |
Lemonade.prototype = new Juice(); | |
OrangeJuice.prototype = new Juice(); | |
AppleJuice.prototype = new Juice(); | |
// delsAppleJuice still returns the same object | |
console.log(delsAppleJuice.isCold); | |
//////////////////////////// | |
// PAUSE BEFORE CONTINUING | |
// Does all of this make sense so far? Any questions or anything I should clarify? | |
// 5 minutes | |
// Lets make Spiked Lemonade. | |
// This is exactly the same as lemonade except | |
// that it's alcoholic, and instead of $2, we're going to charge $4. | |
function SpikedLemonade () { | |
this.price = 4; | |
this.ingredients = { | |
"lemons" : 5, | |
"tbsp of sugar" : 16, | |
"cups of water" : 8, | |
"oz of vodka" : 2 | |
}; | |
// Alternate way of writing the ingredients | |
this.ingredients["oz of vodka"] = 2; | |
} | |
///////////////////////////// | |
// CHECK FOR UNDERSTANDING // | |
///////////////////////////// | |
// What should I do after writing the constructor | |
// to make sure that the SpikedLemonade inherits all its properties from Lemonade? | |
SpikedLemonade.prototype = new Lemonade("Del's Hard Lemonade"); | |
var delsHardLemonade = new SpikedLemonade(); | |
///////////////////// | |
// PROTOTYPE CHAIN // | |
///////////////////// | |
/* | |
Diagram: | |
https://docs.google.com/drawings/d/1PTgRq2UPD0LilvZOFIpTWUsgzGo1iN7XdL7X6BXvA-U/edit?usp=sharing | |
*/ | |
// The default is "Object" | |
// if you don't specify a prototype. | |
// If you try to refer to a property that doesn't exist in the current object, | |
// JavaScript will go up the prototype chain to try to find that property | |
// Example: | |
console.log(delsHardLemonade.isCold); | |
// 8 minutes | |
// ATTACHING FUNCTIONS TO THE PROTOTYPE | |
// You're making enough lemonade now that you have to buy a lot of lemons. | |
// You have to make a shopping list to keep track | |
delsCoolLemonade.makeShoppingList = function (numPeople) { | |
var shoppingList = []; | |
for (var foodItem in this.ingredients) { | |
shoppingList.push(numPeople * (this.ingredients[foodItem] / this.servings) + | |
" " + foodItem); | |
} | |
return shoppingList; | |
}; | |
// 9 minutes | |
console.log(delsCoolLemonade.makeShoppingList(12)); | |
// If you run this, you get the output: | |
// [ '20 lemons', '64 tbsp of sugar', '32 cups of water' ] | |
// You want to do market research | |
// "Awesome Lemonade" is a better name than "Cool Lemonade". | |
// You run an A/B test of the two names | |
var delsCoolLemonade = new Lemonade("Del's Cool Lemonade"); | |
var delsAwesomeLemonade = new Lemonade("Del's Awesome Lemonade"); | |
// Now, you also have to build a shopping list for Awesome Lemonade | |
// But, you want makeShoppingList to exist for ALL lemonades, not just Cool Lemonade. | |
Lemonade.prototype.makeShoppingList = function () { /* ... */ }; | |
// PAUSE TO EXPLAIN | |
// "constructor" is like the "blueprint" for the Lemonade object | |
// "prototype" is like the "blueprint" for the object | |
// When you say Lemonade.prototype = new Juice();, you're saying: | |
// "Make the initial blueprint for a Lemonade looks like exactly like Juice" | |
// You can change this blueprint later by putting stuff in the constructor, | |
// or by putting sub-properties into the prototype directly. | |
Lemonade.prototype.isSweet = true; | |
// 11 minutes | |
//////////////////////// | |
// SUMMARY AND REVIEW // | |
//////////////////////// | |
// The "prototype" is a special property of an object that refers to | |
// "What type of thing is this?" | |
// Every object has a prototype, and the default prototype is Object | |
// If you refer to it directly, you are referring to | |
// the blueprint of your object. | |
// Anything you attach to the prototype of an object will be attached to all instances | |
// of that object that you create. (Cool Lemonade vs Awesome Lemonade) | |
// The next time you want to start a new business, keep prototypes | |
// in the back of your mind. | |
// Example usage: to keep track of things like your employees, or customer data, | |
// or the addresses of your suppliers | |
///////////////////////////// | |
// CHECK FOR UNDERSTANDING // | |
///////////////////////////// | |
// Remember our A/B test between Cool Lemonade and Awesome Lemonade? | |
// I also want to calculate revenue for Awesome Lemonade | |
delsCoolLemonade.calculateCoolRevenue = function (numSold) { | |
return delsCoolLemonade.price * numSold; | |
}; | |
delsAwesomeLemonade.calculateAwesomeRevenue = function (numSold) { | |
return delsAwesomeLemonade.price * numSold; | |
}; | |
// How can I refactor this? | |
// Good answer: attach it to the Lemonade prototype | |
// Better answer: attach it to the Juice prototype (remember the prototype chain!) | |
Lemonade.prototype.calculateRevenue = function (numSold) { | |
return this.price * numSold; | |
}; | |
///////// | |
// END // | |
///////// | |
// Any questions? | |
// Sidenote: | |
// Another advantage of prototypes is that it's faster, | |
// because you're not instantiating every method all the time. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment