Prototypes

发布时间:2025-12-09 11:52:22 浏览次数:2

Where did we Leave Off?

At a cliffhanger!

  • the default value of a constructor's prototype is a plain, empty object that derives from Object.prototype
  • every instance created with the constructor will have that object as its actual prototype
  • note that there's a difference between the constructor's prototype property that's used to set an instance's prototype versus the constructor's actual prototype… can you guess what that is? →Function.prototype
  • for example, we could use a constructor's prototype to add a howl method on every instance of Werewolf
Werewolf.prototype.howl = function(thing) {console.log('The werewolf howls at the ' + thing + '.');}sadWerewolf.howl('moon');partyWerewolf.howl('bowl of chips');
 

Something Happened! Just one Prototype

When we added a property to the constructor's prototype, something unusual happened!  How were the instances of that constructor affected? →

The instances immediately had access to the new property, even though they were instantiated before the prototype was set.

  1. all instances share that prototype object
  2. so… when a property is looked up on any of those instances and isn't found
  3. it looks at that shared prototype object
  4. it's typical for a prototype object to only contain methods

Searching for a Property

When a property is requested from an object, where are the places that the property is searched for?

  • the object itself
  • the object's prototype
  • the object's prototype's prototype
  • and so on up the prototype chain up until Object.prototype

Overriding Properties

If you add a property directly to an object, it is added to the object itself, not the object's prototype. What's the output of the following code? →

Werewolf.prototype.clothing = 'tattered shirt';console.log(partyWerewolf.clothing);partyWerewolf.clothing = 'backwards cap';console.log(partyWerewolf.clothing);console.log(sadWerewolf.clothing);
tattered shirtbackwards captattered shirt
 

Overriding Properties Continued

Again, when you add a property to an object, that property is added to the object itself…

  • (not the prototype)
  • this happens regardless of whether or not there's already a property with the same name in the prototype
  • if there is a property with the same name in the prototype, it is masked or overridden by the new property
  • note that the prototype itself is not changed

Where Did Those Properties Come From?

Let's break down all of the properties of our partyWerewolf object and determine where they came from →

partyWerewolf properties=====
from partyWerewolf object-----clothing: backwards cap  mood: partying 
from Werewolf.prototype-----clothing: tattered shirt (masked)howl: (function)
from Object-----toString: (function)etc.
 

Common Pattern for Inheritance

A common pattern for implementing inheritance is to: →

  • use a fresh object that has the prototype set to the parent constructor's prototype property (WAT?)
  • as the child constructor's prototype property
  • … which can be done with Object.create

Using our parent constructor, Werewolf

function Werewolf(mood) {    this.mood = mood;}Werewolf.prototype.howl = function(thing) {console.log('The werewolf howls at the ' + thing + '.');}

Create a constructor for a space werewolf (!!!) by setting its prototype to a new object who's prototype is Werewolf.prototype

function SpaceWerewolf() {}SpaceWerewolf.prototype = Object.create(Werewolf.prototype);

This isn't quite complete, though. →

Common Pattern for Inheritance

A common pattern for implementing inheritance is to: →

  • use a fresh object that has the prototype set to the parent constructor's prototype property (WAT?)
  • as the child constructor's prototype property
  • … which can be done with Object.create

Using our parent constructor, Werewolf

function Werewolf(mood) {    this.mood = mood;}Werewolf.prototype.howl = function(thing) {console.log('The werewolf howls at the ' + thing + '.');}

Create a constructor for a space werewolf (!!!) by setting its prototype to a new object who's prototype is Werewolf.prototype

function SpaceWerewolf() {}SpaceWerewolf.prototype = Object.create(Werewolf.prototype);

This isn't quite complete, though. →

 

Inheritance Continued

In the previous implementation, there's actually some stuff missing when we create a SpaceWerewolf. What's missing from the previous implementation that results in incomplete inheritance? →

  • the prototype only contains methods
  • what about properties set from the constructor (like mood)?
const w = new SpaceWerewolf();console.log(mood)
 

Calling Super

Hm. The constructor, Werewolf, sets the property, mood

  • if only we can execute the parent constructor (you know… like call super in Java).
  • but we can, how? →
  • use call
function SpaceWerewolf(mood) {    Werewolf.call(this, mood);}
 

One Last Detail, Constructor Property

All object's have a property named constructorconstructor is the function that was used to create the instance's prototype.

const a = [];console.log(a.constructor); // [Function: Array] 

So we should probably set that on our child constructor's prototype property explicitly so that all objects created from SpaceWerewolf have that as its constructor.

SpaceWerewolf.prototype.constructor = SpaceWerewolf;

All Together

function Werewolf(mood) {    this.mood = mood;}Werewolf.prototype.howl = function(thing) {console.log('The werewolf howls at the ' + thing + '.');}
function SpaceWerewolf(mood) {    Werewolf.call(this, mood);}SpaceWerewolf.prototype = Object.create(Werewolf.prototype);SpaceWerewolf.prototype.constructor = SpaceWerewolf;const w = new SpaceWerewolf('in space');console.log(w.mood);console.log(w.constructor);
 

Prototype: An Example

Check out the following example… →

function Monster() {this.scary = true;}Monster.prototype.boo = function() { console.log('Boo!');}
function Werewolf(mood) {    Monster.call(this);this.mood = mood;}Werewolf.prototype = Object.create(Monster.prototype);Werewolf.prototype.constructor = Werewolf;
Werewolf.prototype.howl = function(thing) {console.log('The werewolf howls at the ' + thing + '.');}
 

Example Continued

What would the output be if the following code were run… →

const sadWerewolf = new Werewolf('sad');const partyWerewolf = new Werewolf('partying');partyWerewolf.scary = false;console.log(sadWerewolf.scary);console.log(partyWerewolf.scary);partyWerewolf.boo();
truefalseBoo!

Some notes on the example:

  • to inherit properties from Monster…
    • we set our Werewolf constructor's prototype to a fresh object with Monster.prototype as the prototype
    • we called the "super" constructor
  • const sadWerewolf = new Werewolf('sad'); 
  • …which is why scary was found in the prototype chain for sadWerewolf

Own Property

What if we only want the properties that were explicitly set on our object, rather than including inherited ones. →

We could use the hasOwnProperty method that every object inherits from Object.prototype!

console.log('party\n-----');for (const p in partyWerewolf) {if (partyWerewolf.hasOwnProperty(p)) {console.log(p + ': ' + partyWerewolf[p]);}}console.log('\n');console.log('sad\n-----');for (const p in sadWerewolf) {if (sadWerewolf.hasOwnProperty(p)) {console.log(p + ': ' + sadWerewolf[p]);}}
 

What Instance do I Have?

If you have an object, and you'd like to know what constructor it came from, you can use the instanceof operator.

  • instance on left
  • constructor on right

What do you think the following code will print out? →

console.log(myCar instanceof Car);console.log(myCar instanceof Bike);
truefalse

(in actuality instance of checks if an object has in its prototype chain the prototype property of a constructor)

Example ES6 Class

These two bits of code both produce a function called HttpRequest! →

ES6 class:

class HttpRequest {}

ES5 constructor:

function HttpRequest() {}

Both result in the same output when used in the following manner:

const req = new HttpRequest();console.log(HttpRequest);console.log(typeof req.constructor);console.log(req.constructor.name);
 

Constructors

ES6 style classes allow for a constructor to be defined as follows:

  • within the class definition, create a function called constructor
  • no function keyword is required
  • the constructor has access to this which represents the instance that is created
class HttpRequest {    constructor(method, url) {        this.method = method;        this.url = url;    }}

The above code is mostly the same as this ES5 function that can be used as a constructor:

function HttpRequest(method, url) {   this.method = method;   this.url = url;}

We'll see later that subclass constructors must call super before using this.

 

Methods in ES5

In ES5, to add a method to the prototype, we'd have to do something like this:

function HttpRequest(method, url) {   this.method = method;   this.url = url;}
HttpRequest.prototype.makeRequest = function() {    return this.method + ' ' + this.url + ' HTTP/1.1';}
 

Methods in ES6

In ES6, we can define methods directly in the class definition, and they will show up in the instances' prototype →

class HttpRequest {  constructor(method, url) {    this.method = method;    this.url = url;  }  makeRequest() {    return this.method + ' ' + this.url + ' HTTP/1.1';  }}
  • note that there are no commas between method and constructor definitions
  • again, you do not have to use the keyword, function
  • methods, of course, can reference this, and if the method is called within the context of an instance, then thisrefers to the instance
 

ES6 Methods Continued

Note that creating these methods in ES6 style classes is actually just adding to the prototype! →

const req = new HttpRequest('GET', 'http://foo.bar/baz');console.log(req.makeRequest());console.log(Object.getPrototypeOf(req).makeRequest);
 

Inheritance

Use extends to inherit from a class! (read: set up a prototype chain)

class Element {    constructor(name) {        this.name = name;     }}
class ImgElement extends Element {    // make sure to call super before using this    // within subclass    constructor(url) {        super('img');        this.url = url;    }}
const img = new ImgElement('http://foo.bar/baz.gif');console.log(img.name);console.log(img.url);
 

Calling Super Constructor

In the previous example, super was used to call the base class constructor. →

  • super must be called in your subclass constructor if…
  • you use this within your constructor
  • (it's essentially initializing this properties the way that the superclass would
  • super must be called before using this within a subclass
 

High Level Summary

Every object in JavaScript links to another object called its [[prototype]]. →

  • when a property cannot be found in the original object, it goes up the prototype chain
  • objects can be given prototypes in 3 ways:
    1. Object.create
    2. constructor functions
    3. ES6 Classes
    4. (there's also something called __proto__ that allows direct access to a [[prototype]], but its use is discouraged)
prototypes
需要做网站?需要网络推广?欢迎咨询客户经理 13272073477