Search code examples
javascriptecmascript-6prototypeprototypal-inheritance

Why is the prototype function not using the correct version of this?


I am trying to understand the JS prototype inheritance mechanism which uses. When I execute the below code in the browser( for example) the ironman.villain(); function fails to read the name property from this. I expect this to be the MarvelHero object, but it is actually a reference to the window. ironman.summarize() also fails for a similar reason. Why is this?

ironman.introduce(); and ironman.friends(); correctly reads the this.name variable and prints it.

var heroes = [];

// Hero constructor
function Hero (name, universe) {
  this.name = name;
  this.universe = universe;
  this.introduce = () => {
    return `I am ${this.name} and I belong to the ${this.universe} universe.`;
  }
  heroes.push(this);
}

// MarvelHero constructor
function MarvelHero (name) {
  this.universe = 'Marvel Comics';
  Hero.call(this, name, this.universe);
  this.friends = () => {
    return `${this.name} has friends: ${heroes.filter(hero => hero instanceof MarvelHero && hero.name !== this.name).map(hero => hero.name).join(', ')}.`;
  };
}

// DCHero constructor
function DCHero (name) {
  this.universe = 'DC Comics';
  Hero.call(this, name, this.universe);
  this.friends = () => {
    return `${this.name} has friends: ${heroes.filter(hero => hero instanceof DCHero && hero.name !== this.name).map(hero => hero.name).join(', ')}.`;
  };
}

// Parent prototype
Hero.prototype.summarize = () => {
  return `${this.name} => ${this.universe}`;
}

// Inherit from Hero's prototype
MarvelHero.prototype = Object.create(Hero.prototype);
DCHero.prototype = Object.create(Hero.prototype);

// Assign constructor prototypes to self
MarvelHero.prototype.constructor = MarvelHero;
DCHero.prototype.constructor = DCHero;

// MarvelHero prototype
MarvelHero.prototype.villain = () => {
  return `${this.name} has Loki`;
}

// DCHero prototype
DCHero.prototype.villain = () => {
  return `${this.name} has The Joker`;
}

let ironman = new MarvelHero('Ironman');
let captain = new MarvelHero('Captain America');
let spiderman = new MarvelHero('Spiderman');
let hulk = new MarvelHero('The Hulk');
let thor = new MarvelHero('Thor');
let doctor = new MarvelHero('Doctor Strange');
let panther = new MarvelHero('Black Panther');

let batman = new DCHero('Batman');
let superman = new DCHero('Superman');
let wonder = new DCHero('Wonder Woman');
let aquaman = new DCHero('Aquaman');

ironman.introduce();
ironman.friends();
ironman.villain();
ironman.summarize();

batman.introduce();
batman.friends();
batman.villain();
batman.summarize();

Solution

  • Your functions are defined on the global scope, and therefore this refer to the window object.

    In order for this to refer to a parent, you need them to be part of an object

    {
         DChero : function(name){}
    }
    

    Or wrap your code in parentheses and use it as an iife