Repository navigation
Advanced-JavaScript Completed #39
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| /** | ||
| Let's say we have a class Monster. This class will define all of our Monsters at a generic level. | ||
| It will hold attributes that each Monster, be it a Zombie, a Skeleton, a Banshee, etc should have. | ||
| **/ | ||
| class Monster { | ||
| constructor(name, hitpoints, level, inventory, nickname) { | ||
| this.name = name; | ||
| this.hitpoints = hitpoints; | ||
| this.level = level; | ||
| this.inventory = inventory || []; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could potentially just default this in the constructor parens. |
||
| this.nickname = nickname; | ||
| } | ||
| getDisplayName() { | ||
| return this.nickname || this.name; //if no nickname was provided, nickname will be undefined so name will be returned. | ||
| } | ||
| onAttack() { | ||
| console.log(`${this.getDisplayName()} attacked for 1 damage.`); | ||
| } | ||
| onDamage() { | ||
| console.log(`${this.getDisplayName()} took ${this.hitpoints - --this.hitpoints} damage!`); | ||
| if (this.hitpoints <= 0) { | ||
| this.onDeath(); | ||
| } | ||
| } | ||
| onDeath() { | ||
| console.log(`${this.getDisplayName()} died!`); | ||
| if(this.inventory.length > 0) { | ||
| console.log(`${this.getDisplayName()} dropped: `); | ||
| this.inventory.forEach((item) => console.log(`\t${item}`)); | ||
| } else { | ||
| console.log(`${this.getDisplayName()} didn't drop anything :(`); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| We now have the basic features a monster should have as well as default onAttack and onDamage methods that will apply to any | ||
| Monsters that don't come with their own. Lets create our different Monsters and extend the Monster class so they all get these features. | ||
| **/ | ||
| class Zombie extends Monster { | ||
| constructor(stats) { | ||
| // You call `super` which calls Monster's constructor and lets Zombie inherit everything from Monster | ||
| super("Zombie", stats.hitpoints, stats.level, stats.inventory || ['sword'], stats.nickname); | ||
| this.rotten = 'hella'; | ||
| } | ||
| } | ||
|
|
||
| class Skeleton extends Monster { | ||
| constructor(hitpoints, level, inventory = ["bow"], nickname) { | ||
| // You call `super` which calls Monster's constructor and lets Skeleton inherit everything from Monster | ||
| super("Skeleton", hitpoints, level, inventory, nickname); | ||
| } | ||
| } | ||
|
|
||
| class Banshee extends Monster { | ||
| constructor(hitpoints, level, inventory = undefined, nickname) { | ||
| // You call `super` which calls Monster's constructor and lets Banshee inherit everything from Monster | ||
| super("Banshee", hitpoints, level, inventory, nickname); | ||
| } | ||
| onAttack() { | ||
| this.scream(); | ||
| // even though we are making our own onAttack method, we can still call the one from super | ||
| super.onAttack(); | ||
| } | ||
| scream() { | ||
| console.log("AHHHHHH!!!!") | ||
| } | ||
| } | ||
|
|
||
| // Set up our dope battle simulator right here | ||
| function battle(mon1, mon2) { | ||
| if(Math.random() >= 0.5) { | ||
| mon1.onAttack(); | ||
| mon2.onDamage(); | ||
| } else { | ||
| mon2.onAttack(); | ||
| mon1.onDamage(); | ||
| } | ||
| } | ||
|
|
||
| // Now we make some Monsters | ||
| const zombie = new Zombie({hitpoints: 5, level: 3}); | ||
| const skeleton = new Skeleton(5, 3); | ||
| const banshee = new Banshee(10, 3); | ||
| const fred = new Zombie({hitpoints: 1, level: 1, nickname: "Fred"}); | ||
| const jill = new Banshee(2000, 1000, inventory = ['The God Staff of Omar Illingisisisisisisi', 'The Magic School Bus Season 1 Collectors Edition'], "Hillary, Joe's Mom"); | ||
|
|
||
| while (zombie.hitpoints > 0 && skeleton.hitpoints > 0) { | ||
| battle(skeleton, zombie); | ||
| console.log(" "); | ||
| } | ||
|
|
||
| while (fred.hitpoints > 0 && banshee.hitpoints > 0) { | ||
| battle(fred, banshee); | ||
| console.log(" "); | ||
| } | ||
|
|
||
| while (banshee.hitpoints > 0 && jill.hitpoints > 0) { | ||
| battle(banshee, jill); | ||
| console.log(" "); | ||
| } | ||
|
|
||
| console.log(" "); | ||
| console.log("He has risin! Oh glorious Fred, may he bring glory to the heavens and rain fire on all those who appose him!!!"); | ||
| console.log(" "); | ||
| console.log(" "); | ||
|
|
||
| fred.hitpoints = 200000; | ||
| fred.level = 9000+1; | ||
| fred.inventory.push('The God Staff of Omar Illingisisisisisisi +2'); | ||
|
|
||
| fred.scream = function() { | ||
| console.log("Ooooga Boooggggaaa!!"); | ||
| } | ||
|
|
||
| fred.onAttack = function() { | ||
| this.scream(); | ||
| console.log(`${this.getDisplayName()} attacked for !@#!$%@#~! damage.`); | ||
| jill.hitpoints = -100000; | ||
| } | ||
|
|
||
| while (fred.hitpoints > 0 && jill.hitpoints > 0) { | ||
| battle(fred, jill); | ||
| console.log(" "); | ||
| } | ||
|
|
||
| // Some further tests to show how these interact | ||
| console.log(`typeof zombie: ${typeof zombie}`); | ||
| console.log(`zombie instanceof Zombie: ${zombie instanceof Zombie}`); | ||
| console.log(`zombie instanceof Monster: ${zombie instanceof Monster}`); | ||
| console.log(`zombie instanceof Skeleton: ${zombie instanceof Skeleton}`); | ||
| console.log(`zombie instanceof Banshee: ${zombie instanceof Banshee}`); | ||
| console.log(`typeof zombie === typeof skeleton: ${typeof zombie === typeof skeleton}`); | ||
| console.log(`typeof zombie === typeof fred: ${typeof zombie === typeof fred}`); | ||
| // console.log(`zombie instanceof fred: ${zombie instanceof fred}`); //error because fred isn't a class/constructor. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| class Fruit{ | ||
| constructor(type, color){ | ||
| this.getType = function(){return type}; | ||
| this.getColor = () => color; | ||
| } | ||
| } | ||
|
|
||
| const apple = new Fruit("Apple", "Red"); | ||
|
|
||
| // Fruit.prototype.message = () => `I am a ${this.getColor()} ${this.getType()}`; | ||
| // console.log(apple.message()); | ||
| /** | ||
| Uncaught TypeError: this.getColor is not a function | ||
| at Fruit.message (<anonymous>:10:48) | ||
| at <anonymous>:12:19 | ||
| **/ | ||
|
|
||
| Fruit.prototype.message = function() { | ||
| return `I am a ${this.getColor()} ${this.getType()}`; | ||
| }; | ||
|
|
||
| console.log(apple.message()); | ||
| /** | ||
| I am a Red Apple | ||
| **/ | ||
|
|
||
| const orange = new Fruit("Orange", "Orange"); | ||
|
|
||
| let fruitMessage = apple.message; | ||
|
|
||
| console.log(fruitMessage); | ||
| // [Function] | ||
|
|
||
| console.log(fruitMessage.call(orange)); | ||
| // I am a Orange Orange | ||
| console.log(fruitMessage.apply(orange)); | ||
| // I am a Orange Orange | ||
|
|
||
| // console.log(fruitMessage()); | ||
| /** TypeError: this.getColor is not a function | ||
| `this` is evaluated when the code is run rather than when | ||
| the object is created. By calling fruitMessage() in this way, | ||
| you have taken apple.message out of apple's scope and moved it | ||
| into the global scope. `this.getColor()` is now looking for `getColor()` | ||
| in the global scope which is undefined. | ||
| **/ | ||
|
|
||
| fruitMessage = apple.message.bind(orange); | ||
| /** bind doesn't execute the function | ||
| it hold onto it and will apply the `this` scope when you call it later | ||
| **/ | ||
|
|
||
| console.log(fruitMessage); | ||
| // [Function] | ||
|
|
||
| console.log(fruitMessage()); | ||
| // I am a Orange Orange! |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,33 +8,67 @@ const each = (elements, cb) => { | |
| // Iterates over a list of elements, yielding each in turn to the `cb` function. | ||
| // This only needs to work with arrays. | ||
| // based off http://underscorejs.org/#each | ||
| for (let i = 0; i < elements.length; i++) { | ||
| cb(elements[i], i); | ||
| } | ||
| }; | ||
|
|
||
| const map = (elements, cb) => { | ||
| // Produces a new array of values by mapping each value in list through a transformation function (iteratee). | ||
| // Return the new array. | ||
| const arr = []; | ||
| each(elements, (item, index) => { arr.push(cb(item, index)); }); | ||
| return arr; | ||
| }; | ||
|
|
||
| const reduce = (elements, cb, memo = elements.shift()) => { | ||
| // Combine all elements into a single value going from left to right. | ||
| // Elements will be passed one by one into `cb`. | ||
| // `memo` is the starting value. If `memo` is undefined then make `elements[0]` the initial value. | ||
| if (typeof memo === 'undefined') { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would be unnecessary as you have defaulted already in the function parens |
||
| memo = elements[0]; | ||
| } | ||
| each(elements, (item, index) => { memo = cb(memo, item, index); }); | ||
| return memo; | ||
| }; | ||
|
|
||
| const find = (elements, cb) => { | ||
| // Look through each value in `elements` and pass each element to `cb`. | ||
| // If `cb` returns `true` then return that element. | ||
| // Return `undefined` if no elements pass the truth test. | ||
| for (let i = 0; i < elements.length; i++) { | ||
| if (cb(elements[i])) { | ||
| return elements[i]; | ||
| } | ||
| } | ||
| return undefined; | ||
| }; | ||
|
|
||
| const filter = (elements, cb) => { | ||
| // Similar to `find` but you will return an array of all elements that passed the truth test | ||
| // Return an empty array if no elements pass the truth test | ||
| const array = []; | ||
| for (let i = 0; i < elements.length; i++) { | ||
| if (cb(elements[i])) { | ||
| array.push(elements[i]); | ||
| } | ||
| } | ||
| return array; | ||
| }; | ||
|
|
||
| const flatten = (elements) => { | ||
| // Flattens a nested array (the nesting can be to any depth). | ||
| // Example: flatten([1, [2], [3, [[4]]]]); => [1, 2, 3, 4]; | ||
| const array = []; | ||
| const cb = (item) => { | ||
| if (item instanceof Array) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice, instance of is great, you should look into |
||
| each(item, cb); | ||
| } else { | ||
| array.push(item); | ||
| } | ||
| }; | ||
| each(elements, cb); | ||
| return array; | ||
| }; | ||
|
|
||
| /* eslint-enable no-unused-vars, max-len */ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,21 +1,32 @@ | ||
| // Complete the following functions. | ||
| const arrayMethods = require('../src/arrays'); | ||
|
|
||
| const counter = () => { | ||
| // Return a function that when invoked increments and returns a counter variable. | ||
| // Example: const newCounter = counter(); | ||
| // newCounter(); // 1 | ||
| // newCounter(); // 2 | ||
| let c = 0; | ||
| return () => ++c; | ||
| }; | ||
|
|
||
| const counterFactory = () => { | ||
| // Return an object that has two methods called `increment` and `decrement`. | ||
| // `increment` should increment a counter variable in closure scope and return it. | ||
| // `decrement` should decrement the counter variable and return it. | ||
| let c = 0; | ||
| return { increment: () => ++c, decrement: () => --c }; | ||
| }; | ||
|
|
||
| const limitFunctionCallCount = (cb, n) => { | ||
| // Should return a function that invokes `cb`. | ||
| // The returned function should only allow `cb` to be invoked `n` times. | ||
| return (...rest) => { | ||
| if (n-- > 0) { | ||
| return cb(...rest); | ||
| } | ||
| return null; | ||
| }; | ||
| }; | ||
|
|
||
| const cacheFunction = (cb) => { | ||
|
|
@@ -25,6 +36,13 @@ const cacheFunction = (cb) => { | |
| // If the returned function is invoked with arguments that it has already seen | ||
| // then it should return the cached result and not invoke `cb` again. | ||
| // `cb` should only ever be invoked once for a given set of arguments. | ||
| const cache = {}; | ||
| return (arg) => { | ||
| if (!(arg in cache)) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. well done! |
||
| cache[arg] = cb(arg); | ||
| } | ||
| return cache[arg]; | ||
| }; | ||
| }; | ||
|
|
||
| /* eslint-enable no-unused-vars */ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really really fun! I'd like to rephrase something though. Objects in Javascript don't necessarily "Inherit" they way that you may be used to from other OO Languages. It's more that a "Parent" class would be "Delegating" do it's "Sub-classes". I'm sure you're aware of these intricacies, but I wanted to make it very clear Here is a link to a great article that talks about Prototypal 'inheritance' which really should say prototypal 'delegation'