Reputation: 195
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
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
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
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:
sc
which is 0
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.
score()
is just like global scope(in second snippet),return function(correct){...}
is just like the nested counter()
function in second snippet.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
Reputation: 43479
nextQuestion()
to begin processvar n = Math.floor(Math.random() * questions.length);
selects random question from your questions
listdisplayQuestion()
shows information from second new Question()
parameter as possible answers. Third parameter is correct answer.prompt('Please select the correct answer.');
and if it's not exit
, then comparing answer with correct value.keepScore(correct)
to add score to final result.nextQuestion();
Upvotes: 1