John'Sters
John'Sters

Reputation: 195

Need some clarification on a JavaScript lesson. (Constructor Functions)

I have the following code I need some clarification with. I want to understand it perfectly before I move on. I know the example might be silly and I am sure there is a lot of better ways to solve the problem but for the sake of this lesson the person used this example.

All I need is some clarification on how exactly the flow of the score function works and onwards. Where are the values coming from? How is it adding up each time the person gives the right answer? I basically want to understand how this code generates the number to display to the console every time the user inputs a true value into the alert. I am sorry if I'm not coming through clearly, I just need to understand how the code works from function score() and onwards. I could not for the life of me figure it out. Where is sc getting its values from, and where does it pass it too and; and; and. Is there anyone that's willing to give me a layout of how this code fits together. I would be eternally grateful.

(function() {
    function Question(question, answers, correct) {
        this.question = question;
        this.answers = answers;
        this.correct = correct;
    }

    Question.prototype.displayQuestion = function() {
        console.log(this.question);

        for (var i = 0; i < this.answers.length; i++) {
            console.log(i + ': ' + this.answers[i]);
        }
    }

    Question.prototype.checkAnswer = function(ans, callback) {
        var sc;

        if (ans === this.correct) {
            console.log('Correct answer!');
            sc = callback(true);
        } else {
            console.log('Wrong answer. Try again :)');
            sc = callback(false);
        }

        this.displayScore(sc);
    }

    Question.prototype.displayScore = function(score) {
        console.log('Your current score is: ' + score);
        console.log('------------------------------');
    }


    var q1 = new Question('Is JavaScript the coolest programming language in the world?',
                          ['Yes', 'No'],
                          0);

    var q2 = new Question('What is the name of this course\'s teacher?',
                          ['John', 'Micheal', 'Jonas'],
                          2);

    var q3 = new Question('What does best describe coding?',
                          ['Boring', 'Hard', 'Fun', 'Tediuos'],
                          2);

    var questions = [q1, q2, q3];

    function score() {
        var sc = 0;
        return function(correct) {
            if (correct) {
                sc++;
            }
            return sc;
        }
    }
    var keepScore = score();


    function nextQuestion() {

        var n = Math.floor(Math.random() * questions.length);
        questions[n].displayQuestion();

        var answer = prompt('Please select the correct answer.');

        if(answer !== 'exit') {
            questions[n].checkAnswer(parseInt(answer), keepScore);

            nextQuestion();
        }
    }

    nextQuestion();

})();

Upvotes: 1

Views: 223

Answers (4)

ray
ray

Reputation: 27245

The slightly tricky bit for the uninitiated here is that the score function returns a function and makes use of a closure.

function score() {
  var sc = 0;
  return function(correct) {
    if (correct) {
      sc++;
    }
    return sc;
  }
}

This line:

var sc = 0;

creates a variable for holding the current score and then returns a function you can call to increment that variable. The variable can't be declared inside the function because it would get recreated every time the function was called:

// can't do this. sc gets recreated and reset on every call:
function score (correct) {
  var sc = 0;
  if (correct) {
    sc++
  }
  return sc;
}

But you also don't want the variable available to everyone, so you don't want to do this either:

// don't want this either.
// score shouldn't be modifiable outside of our keepScore method.
let sc = 0;

function score (correct) {
  if (correct) {
    sc++;
  }
  return sc;
}

// this approach works, but now cheaters can set
// the score outside our scorekeeping function:
sc = 999999; // oh noes! hacks!

So what can we do? We can:

First: Create the variable within a function scope, where it's protected from outside shenanigans:

function score () {
  var sc = 0; // unavailable outside this function
}

Second: Create another function inside the first that can modify the variable:

function score () {
  var sc = 0; // unavailable outside this function

  // a new function that has access to the outer function's sc variable.
  function(correct) {
    if (correct) {
      sc++;
    }
    return sc;
  }
}

Finally, returning the inner function from score() gives outsiders a way to register correct answers without exposing the score variable itself:

function score () {
  var sc = 0; // unavailable outside this function

  // return this function to the caller and voila!
  // they now have a self-contained scorekeeper function.
  return function(correct) {
    if (correct) {
      sc++;
    }
    return sc;
  }
}

So now when you do this:

var keepScore = score();

keepScore is that inner function with its own internal sc variable.

If you had a multiplayer edition, you could get one for each player:

// each gets their own instance of
// that inner function with its own
// independent sc variable.
const playerOneScore = score();
const playerTwoScore = score();

playerOneScore(true); // 1
playerOneScore(true); // 2

playerTwoScore(false); // 0
playerTwoScore(true); // 1

I think once you understand this bit, the rest will make a lot more sense.

Upvotes: 1

R.C.VISHNU Vardhan
R.C.VISHNU Vardhan

Reputation: 381

Here the score function is a Closure

The state of sc inside score is intialised when score is invoked.

var keepScore = score();

Now the keepScore contains the function which is returned by the closure that is

function (correct) {           
        if (correct) {
            sc++; // it has a "closure" over it
        }
        return sc;
    }

which accepts a boolean value. that is, keepScore(true) or keepScore(false)

Now this keepScore function is passed to check answer

questions[n].checkAnswer(parseInt(answer), keepScore);

In check anwer, if the answer is correct it will pass true to the keepScore

callback(true) <=> keepScore(true)

If you want to have more clarity on Closure you can go through this article

https://github.com/getify/You-Dont-Know-JS/blob/master/up%20%26%20going/ch2.md#closure

Upvotes: 1

Maheer Ali
Maheer Ali

Reputation: 36574

sc gets its value due to closure. When ever the nested function which is returned by score() and the if statement if (correct) is true then score gets incremented. Consider a general snippet below.

function score(){
  let sc = 0;
  return function(correct){
    if(correct){
      
      sc++; //this is the same variable is upper scope.
      console.log(sc)
    }
    return sc;
  }
}

let temp = score();

temp(true);
temp(true);

So in the above snippet the outer function score is only called once and does two things:

  • initialize a variable sc which is 0
  • return function in which sc will refer to the sc variable created in upper scope.

The use this is just to prevent making of global variable

Consider another simple snippet.

let count = 0;

function counter(){
  count++;
  console.log(count)
}

counter();
counter();
counter();

In the above snippet the parent scope is global scope and child scope is scope of function. Now the variable count is present inside the the the counter()(even though its not declared inside counter()) due to closure.

  • Now in the first snippet the function score() is just like global scope(in second snippet),
  • In first snippet the returned function is return function(correct){...} is just like the nested counter() function in second snippet.
  • And sc variable is just like count variable.

Could you maybe just explain the prototype.checkAnswer a little bit more

This method takes two arguments. ans and callback.

Consider the line in function nextQuestion()

questions[n].checkAnswer(parseInt(answer), keepScore);

Now if you notice two parameters are passed to function. One is number of correct answer and other is a callback(a function which is passed to another function be be called later).

We are passing keepScore which I explained above is the function returned by score() i.e function(correct){...}. Now this function is called inside the prototype method. This callback returns the value of sc which is declared in its parent function score. And then its displayed using this.displayScore

Now the thing you maybe confused with is the variable sc in the prototype.checkAnswer. That variable is just to prevent the double writing of this.displayScore();

The function can be written as:

Question.prototype.checkAnswer = function(ans, callback) {
    if (ans === this.correct) {
        console.log('Correct answer!');
        this.displayScore(callback(true));
    } else {
        console.log('Wrong answer. Try again :)');
        this.displayScore(callback(false));
    }        
}

Even if we declare the other variable sc in prototype.checkAnswer it has not relation with the variable sc inside score(). They are completely different variables because the variables declared with var have function scope.

Conisder the snippet:

function score(){
  var sc = 0;
  return function(){
    sc++;
    console.log(`I am sc of score: ${sc}`);
    return sc;
  }
}

let temp = score();

function wrapper(callback){
  var sc = 0;
  console.log(`I am sc of wrapper: ${sc}`);
  callback();
}

wrapper(temp)
wrapper(temp)
wrapper(temp)
wrapper(temp)

Upvotes: 1

Justinas
Justinas

Reputation: 43479

  1. It's self-executing wrapper function. It automatically calls nextQuestion() to begin process
  2. var n = Math.floor(Math.random() * questions.length); selects random question from your questions list
  3. displayQuestion() shows information from second new Question() parameter as possible answers. Third parameter is correct answer.
  4. User inputs it's answer in prompt('Please select the correct answer.'); and if it's not exit, then comparing answer with correct value.
  5. After showing answer code calls keepScore(correct) to add score to final result.
  6. [GOTO #2] using nextQuestion();

Upvotes: 1

Related Questions