Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript Object-Oriented JavaScript (2015) Practice Project Project Overview

Devjyoti Ghosh
Devjyoti Ghosh
2,422 Points

Question regarding "this" when used in prototype Inheritance?

I have 4 js files for which I adding them below. In my opinion the relevant parts are in the first 2

quiz.js

function Quiz() {
  this.questions = []; // takes the question objects and stores them in array
  this.questionNumber = 0;
  this.score = 0;
}

Quiz.prototype.add = function(question) {
  this.questions.push(question);
};

//display next question in quiz ui!!!!!!!!!!!!!

Quiz.prototype.nextQuestion = function () {
  let currentQuestion = this.questions[this.questionNumber];
};

// ******checks if answer is correct>score++******

Quiz.prototype.checkAnswer = function(buttonId) {
  let trueOrFalse0 = this.questions[this.questionNumber].trueOrFalse0;
  let trueOrFalse1 = this.questions[this.questionNumber].trueOrFalse1;
  this.buttonId = buttonId;
  if(buttonId === 'guess0') {
    if(this.trueOrFalse0){
      this.score+=1;
    }
  }else {
    if(this.trueOrFalse1) {
      this.score+=1;
    }
  }
};

// On clicking SelectAnswer button > checkAnswer > questionNumber++ > check if last question> display next question???

Quiz.prototype.SelectAnswer = function(buttonId) {
  this.checkAnswer(buttonId);
  this.questionNumber+=1;
  console.log(this.questionNumber + ' '+ this.questions.length);
  if(this.questionNumber === this.questions.length) {
    window.open('file:///C:/Users/Dev/Documents/Code/Javascript/Object%20Oriented%20Programming%20Project/4_Awesome%20Quiz/1_Quiz_Start/answer.html');
    return this.score;
  }
};

quiz_ui.js

function QuizUi(){
  Quiz.call(this);
}
QuizUi.prototype = Object.create(Quiz.prototype);

QuizUi.prototype.renderQuestion = function(questionElement, choice0, choice1) {
  //console.log(this.questions[0]);
  console.log(this.questions[this.questionNumber].ques); /* this.questions[] not working here coming as undefined; Also questionNumber is only storing the default value  in constructor Quiz i.e 0 */
  questionElement.innerHTML = quiz.questions[this.questionNumber].ques; // if i use quiz.quiestions it works but only for quiz.questions[0] and not for quiz.questions[1].
  choice0.innerHTML = quiz.questions[this.questionNumber].choice0;
  choice1.innerHTML = quiz.questions[this.questionNumber].choice1;
};

app.js (might not be as important)

let question1 = new Question('Who was Obama?' , 'President' , true  ,'Prime Minister' , false);
let question2 = new Question('Do I know JS?', 'Yes', false, 'No', true);

let quiz = new Quiz();

quiz.add(question1);
quiz.add(question2);

let quizui = new QuizUi();

let questionElement = document.getElementById('question');
let choice0 = document.getElementById('choice0');
let choice1 = document.getElementById('choice1');

quizui.renderQuestion(questionElement, choice0, choice1);

let button0 = document.getElementById('guess0');
  button0.onclick = ()=>  {
  quiz.SelectAnswer(button0);
  quizui.renderQuestion(questionElement, choice0, choice1);
};
let button1 = document.getElementById('guess1');
    button1.onclick = ()=>  {
    quiz.SelectAnswer(button1);
    quizui.renderQuestion(questionElement, choice0, choice1);
};

question.js(not as important)

function Question(ques, choice0, trueOrFalse0, choice1, trueOrFalse1) {
  this.ques = ques;
  this.choice0 = choice0;
  this.trueOrFalse0 = trueOrFalse0;
  this.choice1 = choice1;
  this.trueOrFalse1 = trueOrFalse1;
}

My question is in Quizui which uses prototype Inheritance from Quiz constructor in quiz.js inside the renderQuestion function this.question[] is showing as undefined and the other values are showing as the default values in Quiz constructor. They aren't being updated when they are worked upon in Quiz.add() , Quiz.checkAnswer(), Quiz.selectAnswer().

I am not sure if the question makes much sense let me know if it doesn't I will try improving it.

Charles Wanjohi
Charles Wanjohi
9,235 Points

Prototypal inheritance makes your QuizUi inherit the properties of the Quiz.You would still need to assign values to the instance of the QuizUi ...not instance of Quiz as the two would be different e.g.

let quizUi=new QuizUi();
quizUi.add();
quizUi.checkAnswer();
quizUi.selectAnswer();

I have answered this question in the context of your question.The implementation of QuizUi to inherit from Quiz may however not be the best approach

2 Answers

Hi,

The issue is that at the moment you are calling quizui.renderQuestions() the quizui object does not contain any questions yet. Yes you inherited the questions property from the Quiz parent class, but not its values. So when you are updating the Quiz object with quiz.add(question1) you are only updating the quiz object, but not the quizui object which is why you are getting an undefined error.

You can fix this pretty easily with two different ways.

  1. Instead of adding to your quiz, add the questions to your quizui object
let question1 = new Question("What is my name?", "Alex", true, "Ash", false);
let question1 = new Question("What is my age?", 24, true, 23, false);

let quizui = new QuizUI();
//This is possible because you inherited all functions and properties from Quiz
//but now you simply add the questions to the questions property of QuizUI :)
quizui.add(question1);
quizui.add(question2);

//now you can call and the undefined error should be gone
quizui.renderQuestion()
  1. Or this way you will need to modify the constructor of QuizUI
function QuizUI(questions) {
       Quiz.call(this);
       this.questions = questions;
}

QuizUI.prototype = Object.create(Quiz.prototype);


///Then you can do
let quiz = new Quiz();
let question1 = new Question("What is my name?", "Alex", true, "Ash", false);
let question1 = new Question("What is my age?", 24, true, 23, false);

quiz.add(question1);
quiz.add(question2);

//now instantiate a quizui object, but pass the quiz object's questions property into the constructor
let quizui = new QuizUI(quiz.questions):

//this should work too now
quizui.renderQuestion();
Devjyoti Ghosh
Devjyoti Ghosh
2,422 Points

Thank you for the detailed answer. I am still unsure behind the reasoning though. Why is quizui inheriting all properties and functions but not the instances? But the other way around quizui instances can be used by the parent Quiz constructor?

Also in your second method do I have to initialize all the properties(i.e questions, questionNumber,score) with this after calling the parent Constructor if I want to use them in quizui?

Prototypal inheritance will only inherit properties and functions. Instances are never inherited. You need to think of your prototype or 'class' as a factory that can create as many instances as you want. Your factory can have sub factories that inherit all the functionality of the parent factory, but that does not mean you inherit what the parent factory created.

To your other question - if you want to use all the quiz property values, you need to pass them into the constructor of your quizui. If you do that however, it is recommended to do it like this:

function QuizUI(questions, questionNumber,score) {
       Quiz.call(this, questions, questionNumber,score);
}

This will automatically assign 'question', 'questionNumber' and 'score' to the properties of Quiz. I did not do that before as I did not pass in all 3 properties...It may have still worked, but not sure.

Devjyoti Ghosh
Devjyoti Ghosh
2,422 Points

I thought that the Quiz.call(this); would have added the instance of Quiz constructor. Why do I have to pass the properties as arguments to the call function to access the instance of Constructor class?

There is a different meaning to instance. An instance is a single object of a particular class that is saved in memory space. For example:

let quiz1 = new Quiz();
let quiz2 = new Quiz();

The above are two different Instances of the Quiz class. The only thing Quiz.call(this) is doing is to inherit the instance properties of Quiz which are questions, questionNumber and score. It does not however, inherit the values of those properties. Perhaps I can explain you inheritance with a more, clear example. The above Quiz example from the course is slightly confusing. Assume we want to create a Parent class called Vehicle and create two sub classes Car and Motorcycle

//CONSTRUCTOR: Vehicle
function Vehicle(wheels, color, maxSpeed) {
    this.wheels = wheels;
    this.color = color;
    this.maxSpeed = maxSpeed;
}

Vehicle.prototype.drive = function() {
      console.log(this.maxSpeed);
}


//CONSTRUCTOR: Car -- now we inherit everything from Vehicle and add some custom functionality only a car has
function Car(color, maxSpeed, doors) {
      // see how I pass parameters after the this parameter? -- here I assign 4 to the vehicles wheels property as each car
     // has 4 wheels. I also assign the color  and maxSpeed
      Vehicle.call(this, 4, color, maxSpeed);
      //now here after the Vehicle.call() I assign some custom properties to the car like door. As all cars have doors, but not all vehicles have doors
       this.doors = doors;
}

//inherit the prorotypal functions of Vehicle
Car.prototype = Object.create(Vehicle.prototype);

//Assigns some custom functionality to my car now like open all doors
Car.prototype.openAllDoors = function() {
       console.log("All " + this.doors + " doors are opened.");
}


//CONSTUCTOR: Motorcycle -- now we inherit everything from Vehicle again and add some custom functionality only a motorcycle has
function Motorcycle(color, maxSpeed, maxPassengers) {
         //this time I pass in 2 for the wheels as all motorcycles have 2 wheels
         Vehicle.call(this, 2, color, maxSpeed);
         this.maxPassengers = maxPassengers;
}

//again inherit all functions now from Vehicle
Motorcycle.prototype = Object.create(Vehicle.prototype);

//some customer functions only motorcycles have
Motorcycle.prototype.doWheely = function() {
        console.log(this.color + " Motorcycle is doing a wheely!!!!");
}



//Now I can create some instances here
let lambo = new Car("red", 200, 2);
let nissan = new Car("black", 120, 4);
lambo.drive() //possible because I inherited this from Vehicle -- will output 200
nissan.openAllDoors() //will output All 4 doors are opened
//but I also have access to properties like wheels
lambo.wheels //will be 4

let motorcycle1 = new Motorcycle("green", 300, 1);
motorcycle1.drive()

Bottom line: your instances above are lambo, nissan and motorcycle1

Devjyoti Ghosh
Devjyoti Ghosh
2,422 Points

Thank you, I understand the concept way better now. You went above and beyond of what I had asked for and I am really grateful for that.

Charles Wanjohi
Charles Wanjohi
9,235 Points

Prototypal inheritance makes your QuizUi inherit the properties of the Quiz.You would still need to assign values to the instance of the QuizUi ...not instance of Quiz as the two would be different e.g.

let quizUi=new QuizUi();
quizUi.add();
quizUi.checkAnswer();
quizUi.selectAnswer();

I have answered this question in the context of your question.The implementation of QuizUi to inherit from Quiz may however not be the best approach

Devjyoti Ghosh
Devjyoti Ghosh
2,422 Points

Thanks a lot this works. But why is quizui inheriting all properties and functions but not the instances? But the other way around quizui instances can be used by the parent Quiz constructor?