- A more efficient way is to set the
prototypeto a new objectAdd the propertynumLegsand the two methodseat()anddescribe()to theprototypeofDogby setting theprototypeto a new object.There is one crucial side effect of manually setting theprototypeto a new object. It erased theconstructorproperty! To fix this, whenever a prototype is manually set to a new object, remember to define theconstructorproperty: Bird.prototype = { constructor: Bird, // define the constructor property numLegs: 2, eat: function() { console.log("nom nom nom"); }, describe: function() { console.log("My name is " + this.name); } };Bird.prototype = { numLegs: 2, eat: function() { console.log("nom nom nom"); }, describe: function() { console.log("My name is " + this.name); } };- Understand the Prototype Chain
Object.prototype.isPrototypeOf(Bird.prototype); // returns true
Thelet duck = new Bird("Donald"); duck.hasOwnProperty("name"); // => truehasOwnPropertymethod is defined inObject.prototype, which can be accessed byBird.prototype, which can then be accessed byduck. This is an example of theprototypechain. - All objects in JavaScript (with a few exceptions) have a
prototype. Also, an object’sprototypeitself is an object. Because aprototypeis an object, aprototypecan have its ownprototype! In this case, theprototypeofBird.prototypeisObject.prototype: - Use Inheritance So You Don't Repeat YourselfThe
describemethod is repeated in two places. The code can be edited to follow theDRYprinciple by creating asupertype(or parent) calledAnimal:SinceAnimalincludes thedescribemethod, you can remove it fromBirdandDog:How to reuseAnimal'smethods insideBirdandDogwithout defining them again. It uses a technique calledinheritance. This challenge covers the first step: make an instance of thesupertype(or parent).There are some disadvantages when using this syntax forinheritance, which are too complex for the scope of this challenge. Instead, here's an alternative approach without those disadvantages:Object.create(obj)creates a new object, and setsobjas the new object'sprototype. Recall that theprototypeis like the "recipe" for creating an object. By setting theprototypeofanimalto beAnimal's prototype, you are effectively giving theanimalinstance the same "recipe" as any other instance ofAnimal.let duck = new Bird("Donald"); // 여기선 new operator을 썼다 duck.eat(); // prints "nom nom nom"duckinherits all ofAnimal's properties, including theeatmethod. Bird.prototype = Object.create(Animal.prototype);let animal = Object.create(Animal.prototype);let animal = new Animal();Bird.prototype = { constructor: Bird }; Dog.prototype = { constructor: Dog };function Animal() { }; Animal.prototype = { constructor: Animal, describe: function() { console.log("My name is " + this.name); } };Bird.prototype = { constructor: Bird, describe: function() { console.log("My name is " + this.name); } }; Dog.prototype = { constructor: Dog, describe: function() { console.log("My name is " + this.name); } };
- Reset an Inherited Constructor Property
Butfunction Bird() { } Bird.prototype = Object.create(Animal.prototype); let duck = new Bird(); duck.constructor // function Animal(){...}duckand all instances ofBirdshould show that they were constructed byBirdand notAnimal. To do so, you can manually setBird'sconstructor property to theBirdobject: Bird.prototype.constructor = Bird; duck.constructor // function Bird(){...}- When an object inherits its
prototypefrom another object, it also inherits thesupertype's constructor property.
- Add Methods After Inheritance
function Animal() { } Animal.prototype.eat = function() { console.log("nom nom nom"); }; function Bird() { } Bird.prototype = Object.create(Animal.prototype); Bird.prototype.constructor = Bird;
Now instances ofBird.prototype.fly = function() { console.log("I'm flying!"); };Birdwill have botheat()andfly()methods: es of Bird will have both eat() and fly() methods: let duck = new Bird(); duck.eat(); // prints "nom nom nom" duck.fly(); // prints "I'm flying!"- A constructor function that inherits its
prototypeobject from asupertypeconstructor function can still have its own methods in addition to inherited methods.
- Override Inherited Methods
You call// Bird.eat() overrides Animal.eat() Bird.prototype.eat = function() { return "peck peck peck"; }; let duck = new Bird();duck.eat(), this is how JavaScript looks for the method onduck’s``prototypechain:- duck => Is eat() defined here? No.
- Bird => Is eat() defined here? => Yes. Execute it and stop searching.
- Animal => eat() is also defined, but JavaScript stopped searching before reaching this level.
- Object => JavaScript stopped searching before reaching this level.
- Here's an example of
Birdoverriding theeat()method inherited fromAnimal:
- Use a Mixin to Add Common Behavior Between Unrelated Objects
Thelet flyMixin = function(obj) { obj.fly = function() { console.log("Flying, wooosh!"); } };flyMixintakes any object and gives it theflymethod. let bird = { name: "Donald", numLegs: 2 }; let plane = { model: "777", numPassengers: 524 }; flyMixin(bird); flyMixin(plane); bird.fly(); // prints "Flying, wooosh!" plane.fly(); // prints "Flying, wooosh!"- Here are cases when inheritance is not the best solution. Inheritance does not work well for unrelated objects like
BirdandAirplane. They can both fly, but aBirdis not a type ofAirplaneand vice versa. For unrelated objects, it's better to usemixins. Amixinallows other objects to use a collection of functions.
- Use Closure to Protect Properties within an Object from Modified ExternallyThe simplest way to make properties private is by creating a variable within the constructor function. This changes the scope of that variable to be within the constructor function versus available globally. This way, the property can only be accessed and changed by methods also within the constructor function.Here
getHachedEggCountis a privileged method, because it has access to the private variablehatchedEgg. This is possible becausehatchedEggis declared in the same context asgetHachedEggCount. In JavaScript, a function always has access to the context in which it was created. This is calledclosure. function Bird() { let hatchedEgg = 10; // private property this.getHatchedEggCount = function() { return hatchedEgg; }; } let ducky = new Bird(); ducky.getHatchedEggCount(); // returns 10- In the previous challenge,
birdhad a public propertyname. It is considered public because it can be accessed and changed outside ofbird's definition.
- IIFE(Immediately Invoked Function Expression)Note that the function has no name and is not stored in a variable.An
immediately invoked function expression(IIFE) is often used to group related functionality into a single object ormoduleThe advantage of themodulepattern is that all of the motion behaviors can be packaged into a single object that can then be used by other parts of your code. Here is an example using it: motionModule.glideMixin(duck); duck.glide();let motionModule = (function(){ return { glideMixin: function(obj) { obj.glide = function() { console.log("Gliding on the water"); }; }, flyMixin: function(obj) { obj.fly = function() { console.log("Flying, wooosh!"); } } } })(); // 2개의 mixin을 동시에(function () { console.log("Chirp, chirp!"); })(); // this is an anonymous function expression that executes right away // Outputs "Chirp, chirp!" immediately
