Prototypal Inheritance

VIDEO

Adding Methods to the Prototype

Recall that objects contain data (i.e., properties), as well as the means the manipulate that data (i.e., methods). Earlier in this Lesson, we simply added methods_directly_into the constructor function itself:

function Cat() {
 this.lives = 9;

 this.sayName = function () {
   console.log(`Meow! My name is ${this.name}`);
 };
}

This way, asayName()method gets added to allCatobjects by saving a function to thesayNameattribute of newly-createdCatobjects.

This works just fine, but what if we want to instantiate more and moreCatobjects with this constructor? You'll create a new function every single time for thatCatobject'ssayName! What's more: if you ever want to make changes to the method, you'll have to update all objectsindividually. In this situation, it makes sense to have all objects created by the sameCatconstructor function just_share_a singlesayNamemethod.

To save memory and keep things DRY, we can add methods to the constructor function'sprototypeproperty. The prototype is just an object, and all objects created by a constructor function keep a reference to the prototype. Those objects can even use theprototype's propertiesas their own!

JavaScript leverages this secret link -- between an object and its prototype -- to implement inheritance. Consider the following prototype chain:

TheCat()constructor function is invoked using thenewoperator, which creates thebaileyinstance (object). Note that themeow()method is defined in the prototype of thebaileyobject's constructor function. The prototype is just an object, and all objects created by that constructor are secretly linked to the prototype. As such, we can executebailey.meow()as if it werebailey's own method!

Recall that each function has aprototypeproperty, which is really just an object. When this function is invoked as a constructor using thenewoperator, it creates and returns a new object. This object is secretly linked to its constructor'sprototype, and this secret link allows the object to access theprototype's properties and methods as if it were its own!

Since we know that theprototypeproperty just points to a regular object,_that_object itself also has a secret link to_its_prototype. And_that_prototype object also has reference to_its own_prototype -- and so on. This is how the prototype chain is formed.

Finding Properties and Methods on the Prototype Chain

Whether you're accessing a property (e.g.,bailey.lives;) or invoking a method (i.e.,bailey.meow();), the JavaScript interpreter looks for them along the prototype chain in a very particular order:

  1. First, the JavaScript engine will look at the object's own properties. This means that any properties and methods defined directly in the object itself will take precedence over any properties and methods elsewhere if their names are the same (similar to variable shadowing in the scope chain).
  2. If it doesn't find the property in question, it will then search the object's constructor's prototype for a match.
  3. If the property doesn't exist in the prototype, the JavaScript engine will continue looking up the chain.
  4. At the very end of the chain is the Object() object, or the top-level parent. If the property _still _cannot be found, the property is undefined.

Previously, we simply defined methods directly in a constructor function itself. Let's see how things look if we defined methods in the constructor'sprototypeinstead!

VIDEO

Here's the code from the preceding video.

For the next quiz, consider the following two code snippets below (i.e., A and B):

// (A)

function Dalmatian (name) {
  this.name = name;

  this.bark = function() {
    console.log(`${this.name} barks!`);
  };
}
// (B)

function Dalmatian (name) {
  this.name = name;
}

Dalmatian.prototype.bark = function() {
  console.log(`${this.name} barks!`);
};

QUESTION 1 OF 5

Let's say that we want to define a method that can be invoked on instances (objects) of theDalmatianconstructor function (we'll be instantiating at least 101 of them!).

Which of the preceding two approaches is optimal?

  • (A)is optimal, becausebark()can be called directly on an instance ofDalmatian. In (B),bark()needs to be called on theDalmatianinstance's prototype for the method to be properly invoked (e.g., spotty.prototype.bark()).

  • (B)is optimal, because the function thatbarkpoints to does not need to be recreated each time an instance ofDalmatianis created.

  • (A)is optimal, because all methods should always be enclosed within theDalmatian()constructor function itself.

  • Both approaches (both(A)and(B)) are equal; there are no differences.

  • None. Methods should always be manually added to individual instances ofDalmatian.

SUBMIT: While both approaches work just fine (i.e., any instances created by the constructor function will be able to invoke a bark() method), the second approach is more ideal. By adding methods to the prototype, memory is saved as more Dalmatian objects are instantiated. Along with being more efficient, we also don't have to update all objects individually should be decide to change a method.

💡 Replacing theprototypeObject 💡

What happens if you completely replace a function'sprototypeobject? How does this affect objects created by that function? Let's look at a simpleHamsterconstructor function and instantiate a few objects:

function Hamster() {
  this.hasFur = true;
}

let waffle = new Hamster();
let pancake = new Hamster();

First, note that even after we make the new objects,waffleandpancake, we can still add properties toHamster's prototype and it will still be able to access those new properties.

Hamster.prototype.eat = function () {
  console.log('Chomp chomp chomp!');
};

waffle.eat();
// 'Chomp chomp chomp!'

pancake.eat();
// 'Chomp chomp chomp!'

Now, let's replaceHamster'sprototypeobject with something else entirely:

Hamster.prototype = {
  isHungry: false,
  color: 'brown'
};

The previous objects don't have access to the updated prototype's properties; they just retain their secret link to the old prototype:

console.log(waffle.color);
// undefined

waffle.eat();
// 'Chomp chomp chomp!'

console.log(pancake.isHungry);
// undefined

As it turns out, any newHamsterobjects created moving forward will use the updated prototype:

const muffin = new Hamster();

muffin.eat();
// TypeError: muffin.eat is not a function

console.log(muffin.isHungry);
// false

console.log(muffin.color);
// 'brown'

VIDEO

Here's the code from the preceding video.

Checking an Object's Properties

As we've just seen, if an object doesn't have a particular property of its own, it can access one somewhere along the prototype chain (assuming itexists, of course). With so many options, it can sometimes get tricky to tell just_where_a particular property is coming from! Here are a few useful methods to help you along the way.

hasOwnProperty()

hasOwnProperty()allows you to find the origin of a particular property. Upon passing in a string of the property name you're looking for, the method will return a boolean indicating whether or not the property belongs to the object itself (i.e., that property was_not_inherited). Consider thePhoneconstructor with a single property defined directly in the function, and another property on itsprototypeobject:

hasOwnProperty()를 사용하면 특정 속성의 출처를 찾을 수 있습니다. 찾고있는 속성 이름의 string을 전달할 때 method는 속성이 object 자체에 속하는지 (즉, 해당 속성 was_not_inherited) 여부를 나타내는 boolean을 반환합니다. function에 직접 정의된 하나의 속성과 prototype object의 또 다른 속성을 가진 Phone constructor를 생각해보십시오.

function Phone() {
  this.operatingSystem = 'Android';
}

Phone.prototype.screenSize = 6;

Let's now create a new object,myPhone, and check whetheroperatingSystemis its own property, meaning that it was not inherited from its prototype (or somewhere else along the prototype chain):

const myPhone = new Phone();

const own = myPhone.hasOwnProperty('operatingSystem');

console.log(own);
// true

Indeed it returns true! What about thescreenSizeproperty, which exists onPhoneobjects'prototype?

const inherited = myPhone.hasOwnProperty('screenSize');

console.log(inherited);
// false

UsinghasOwnProperty(), we gain insight a certain property's origins.

isPrototypeOf()

Objects also have access to theisPrototypeOf()method, which checks whether or not an object exists in another object's prototype chain. Using this method, you can confirm if a particular object serves as the prototype of another object. Check out the followingrodentobject:

또한 object는 다른 object의 prototype chain에 object가 있는지 여부를 확인하는 isPrototypeOf() method에 액세스 할 수 있습니다. 이 방법을 사용하면 특정 object가 다른 object의 prototype 역할을하는지 확인할 수 있습니다. 다음 rodent object를 확인하십시오.

const rodent = {
  favoriteFood: 'cheese',
  hasTail: true
};

Let's now build aMouse()constructor function, and assign itsprototypetorodent:

function Mouse() {
  this.favoriteFood = 'cheese';
}

Mouse.prototype = rodent;

If we create a newMouseobject, its prototype should be therodentobject. Let's confirm:

const ralph = new Mouse();

const result = rodent.isPrototypeOf(ralph);

console.log(result);
// true

Great!isPrototypeOf()is a great way to confirm if an object exists in another object's prototype chain.

Object.getPrototypeOf()

isPrototypeOf()works well, but keep in mind that in order use it, you must have_that prototype object at hand in the first place! What if you're not sure what a certain object's prototype_is?Object.getPrototypeOf()can help with just that!

Using the previous example, let's store the return value ofObject.getPrototypeOf()in a variable,myPrototype, then check what it is:

isPrototypeOf()는 잘 작동하지만 순서대로 사용하려면 먼저 prototype object를 준비해야합니다! 특정 객체의 prototype이 무엇인지 확실하지 않은 경우 어떻게 해야합니까? Object.getPrototypeOf()를 사용하면 도움이됩니다!

이전 예제를 사용하여 myPrototype 변수에 Object.getPrototypeOf()의 반환값을 저장한 다음 그 값을 확인합니다.

const myPrototype = Object.getPrototypeOf(ralph);

console.log(myPrototype);
// { favoriteFood: 'cheese', hasTail: true }

Great! The prototype ofralphhas the same properties as the result because they_are_the same object. Object.getPrototypeOf()is great for retrieving_the_prototype of a given object.

TheconstructorProperty

Each time an object is created, a special property is assigned to it under the hood:constructor. Accessing an object'sconstructorproperty returns a reference to the constructor function that created that object in the first place! Here's a simpleLongboardconstructor function. We'll also go ahead and make a new object, then save it to aboardvariable:

object를 생성할 때마다 hood:constructor 아래에 특수 속성이 할당됩니다. object의 constructor 속성에 액세스하면 먼저 해당 object를 생성한 constructor 기능에 대한 참조가 반환됩니다!

function Longboard() {
  this.material = 'bamboo';
}

const board = new Longboard();

If we accessboard'sconstructorproperty, we should see the original constructor function itself:

console.log(board.constructor);

// function Longboard() {
//   this.material = 'bamboo';
// }

Excellent! Keep in mind that if an object was created using literal notation, its constructor is the built-inObject()constructor function!

const rodent = {
  favoriteFood: 'cheese',
  hasTail: true
};

console.log(rodent.constructor);
// function Object() { [native code] }

QUESTION 2 OF 5

What is true abouthasOwnProperty()? Select all that apply:

  • An object is passed intohasOwnPropertyas an argument

  • It returns a boolean indicating whether the object has the specified property as its own property (i.e., the property isn't inherited)

  • A string cannot be passed as an argument intohasOwnProperty()

  • hasOwnProperty()is invoked as a method onto an object

SUBMIT: hasOwnProperty() takes in a single argument (i.e. the property to be checked), and the method is invoked directly on an object.

QUESTION 3 OF 5

What is true aboutisPrototypeOf()orgetPrototypeOf()? Select all that apply:

  • isPrototypeOf()checks whether or not an object exists in another object's prototype chain

  • isPrototypeOf()takes a single argument: an object whose prototype chain is to be searched

  • getPrototypeOf()is invoked on an instance of a constructor function (i.e., a single object itself)

  • getPrototypeOf()returns the prototype of the object passed into it

SUBMIT

QUESTION 4 OF 5

What is true aboutconstructorproperty? Select all that apply:

  • Accessing an object'sconstructorproperty returns a reference to the constructor function that created that object (instance)

  • The value of theconstructorproperty is just a string of the constructor function's name, rather than the function itself

  • Every object has aconstructorproperty

  • Objects created with literal notation are constructed with theObject()constructor function

SUBMIT

QUESTION 5 OF 5

Let's say that we create the following object,capitals, using regular object literal notation:

const capitals = {
  California: 'Sacramento',
  Washington: 'Olympia',
  Oregon: 'Salem',
  Texas: 'Austin'
};

What is returned whenObject.getPrototypeOf(capitals);is executed?

  • A reference toObject()'s prototype

  • undefined

  • Object()

  • {}

SUBMIT: This one may have been tricky! Keep in mind that since capitals was created with object literal notation, its constructor is the built-in Object() constructor function itself! As such, it maintains a reference to its constructor's prototype. That is,

Object.getPrototypeOf(capitals) === Object.prototype

// true

Summary

Inheritance in JavaScript is when an object is based on _another _object. Inheritance allows us to reuse existing code, having objects take on properties of other objects.

When a function is called as a constructor using thenewoperator, the function creates and returns a new object. This object is secretly linked to its constructor'sprototype, which is just another object. Using this secret link allows an object to access theprototype's properties and methods as if it were its own. If JavaScript does not find a particular property within an object, it will keep looking up the prototype chain, eventually reachingObject()(top-level parent) if necessary.

JavaScript의 상속은 object가 다른 object를 기반으로하는 경우입니다. 상속을 사용하면 object가 다른 object의 속성을 사용하는 기존 코드를 재사용 할 수 있습니다.

new 연산자를 사용하여 함수가 constructor로 호출되면 이 함수는 새 object를 만들고 반환합니다. 이 object는 constructor의 prototype에 비밀리에 연결됩니다.이 prototype은 또 다른 object입니다. 이 비밀 링크를 사용하면 object가 prototype의 속성 및 메서드에 액세스 할 수 있습니다. JavaScript가 object 내에서 특정 속성을 찾지 못하면 prototype chain을 계속 찾고 결국 필요에 따라 Object () (최상위 수준의 부모)에 도달합니다.

We also looked at a few methods and properties that allow use to check the origins and references of objects and their prototypes, namely:

  • hasOwnProperty()
  • isPrototypeOf()
  • Object.getPrototypeOf()
  • .constructor

In the next section, we'll check out another part of prototypal inheritance in the form of subclassing. What if you want to inherit just a few properties from an object -- but want an object to also have other, specialized properties of their own? We'll take an even deeper dive into prototypal inheritance in the next section!

Further Research

results matching ""

    No results matching ""