- A more efficient way is to set the
prototype
to a new objectAdd the propertynumLegs
and the two methodseat()
anddescribe()
to theprototype
ofDog
by setting theprototype
to a new object.There is one crucial side effect of manually setting theprototype
to a new object. It erased theconstructor
property! To fix this, whenever a prototype is manually set to a new object, remember to define theconstructor
property: 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"); // => true
hasOwnProperty
method is defined inObject.prototype
, which can be accessed byBird.prototype
, which can then be accessed byduck
. This is an example of theprototype
chain. - All objects in JavaScript (with a few exceptions) have a
prototype
. Also, an object’sprototype
itself is an object. Because aprototype
is an object, aprototype
can have its ownprototype
! In this case, theprototype
ofBird.prototype
isObject.prototype
: - Use Inheritance So You Don't Repeat YourselfThe
describe
method is repeated in two places. The code can be edited to follow theDRY
principle by creating asupertype
(or parent) calledAnimal
:SinceAnimal
includes thedescribe
method, you can remove it fromBird
andDog
:How to reuseAnimal's
methods insideBird
andDog
without 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 setsobj
as the new object'sprototype
. Recall that theprototype
is like the "recipe" for creating an object. By setting theprototype
ofanimal
to beAnimal's prototype
, you are effectively giving theanimal
instance the same "recipe" as any other instance ofAnimal
.let duck = new Bird("Donald"); // 여기선 new operator을 썼다 duck.eat(); // prints "nom nom nom"
duck
inherits all ofAnimal
's properties, including theeat
method. 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(){...}
duck
and all instances ofBird
should show that they were constructed byBird
and notAnimal
. To do so, you can manually setBird's
constructor property to theBird
object: Bird.prototype.constructor = Bird; duck.constructor // function Bird(){...}
- When an object inherits its
prototype
from 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!"); };
Bird
will 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
prototype
object from asupertype
constructor 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``prototype
chain:- 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
Bird
overriding 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!"); } };
flyMixin
takes any object and gives it thefly
method. 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
Bird
andAirplane
. They can both fly, but aBird
is not a type ofAirplane
and vice versa. For unrelated objects, it's better to usemixins
. Amixin
allows 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
getHachedEggCount
is a privileged method, because it has access to the private variablehatchedEgg
. This is possible becausehatchedEgg
is 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,
bird
had 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 ormodule
The advantage of themodule
pattern 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