问题
This was a very difficult to put into words but I am working on a mad libs challenge and the goal is to prompt the user 3 times with a question to add a noun, verb and adjective into the text field. In those prompt messages there will be a variable with the number of questions for the user to answer. So after every prompt they successfully answer the next prompt will read 1 less question left. What I dont understand is why the code below will not work...
var questions = 3;
var questionsLeft = " (" + questions + " questions left)"
var adjective = prompt('Please type an adjective' + questionsLeft);
questions -= 1;
var verb = prompt('Please type a verb' + questionsLeft);
questions -= 1;
var noun = prompt('Please type a noun' + questionsLeft);
alert('All done. Ready for the message?');
var sentence = "<h2>There once was a " + adjective;
sentence += ' programmer who wanted to use JavaScript to ' + verb;
sentence += ' the ' + noun + '.</h2>';
document.write(sentence);
In order for this program to work you must state questionsLeft = " (" + questions + " questions left)"
again after each prompt... I thought by just adding questions -= 1
after every prompt that it would work, but I just want to understand why that is?
Final product that works is below so you can see the difference clearly...
var questions = 3;
var questionsLeft = " (" + questions + " questions left)"
var adjective = prompt('Please type an adjective' + questionsLeft);
questions -= 1;
questionsLeft = " (" + questions + " questions left)"
var verb = prompt('Please type a verb' + questionsLeft);
questions -= 1;
questionsLeft = " (" + questions + " questions left)"
var noun = prompt('Please type a noun' + questionsLeft);
alert('All done. Ready for the message?');
var sentence = "<h2>There once was a " + adjective;
sentence += ' programmer who wanted to use JavaScript to ' + verb;
sentence += ' the ' + noun + '.</h2>';
document.write(sentence);
回答1:
That's because when you create a string, like this:
" (" + questions + " questions left)"
the string will have no dependence to the variable it contains.
The variable's value is substituted at the time when the string created.
To work around this, can defer the substitution until it is really needed (but that might cause repetitive strings):
var questions = 3;
var adjective = prompt('Please type an adjective (' + questions + " questions left)");
questions -= 1;
var verb = prompt('Please type a verb (' + questions + " questions left)");
questions -= 1;
var noun = prompt('Please type a noun (' + questions + " questions left)");
alert('All done. Ready for the message?');
var sentence = "<h2>There once was a " + adjective;
sentence += ' programmer who wanted to use JavaScript to ' + verb;
sentence += ' the ' + noun + '.</h2>';
document.write(sentence);
Alternatively, you can do something nice with ES6 tagged template literals:
const templateCreator = (strings, ...indices) => (...substitutions) => strings.slice(1).reduce((acc, string, index) => acc + substitutions[indices[index]] + string, strings[0])
var questions = 3;
var templateFunction = templateCreator `Please type a${0} (${1} questions left)`
var adjective = prompt(templateFunction('n adjective', questions));
questions -= 1;
var verb = prompt(templateFunction(' verb', questions));
questions -= 1;
var noun = prompt(templateFunction(' noun', questions));
alert('All done. Ready for the message?');
var sentence = "<h2>There once was a " + adjective;
sentence += ' programmer who wanted to use JavaScript to ' + verb;
sentence += ' the ' + noun + '.</h2>';
document.write(sentence);
By using the above solution, you can have a reusable template in the form of a function, that you can call as many times as you want, with different arguments.
That's what template literals are for.
But note that we still have passed the questions
variable in every call of templateFunction
.
Finally, we can go one more step further and create another function, that wraps templateFunction
, and has the questions
variable as a closure:
const templateCreator = (strings, ...indices) => (...substitutions) => strings.slice(1).reduce((acc, string, index) => acc + substitutions[indices[index]] + string, strings[0])
var questions = 3;
var templateFunction = wordClass => (templateCreator `Please type a${0} (${1} questions left)`)(wordClass, questions)
var adjective = prompt(templateFunction('n adjective'));
questions -= 1;
var verb = prompt(templateFunction(' verb'));
questions -= 1;
var noun = prompt(templateFunction(' noun'));
alert('All done. Ready for the message?');
var sentence = "<h2>There once was a " + adjective;
sentence += ' programmer who wanted to use JavaScript to ' + verb;
sentence += ' the ' + noun + '.</h2>';
document.write(sentence);
回答2:
When you do an assignment like
var questionsLeft = " (" + questions + " questions left)";
it copies the value of questions
into the new string. There's no automatic link with the variable, changing the variable doesn't change what's in the string.
Similarly, if you do something like
var a = 1;
var b = a;
a = 2;
console.log(a, b);
This will log 2, 1
, not 2, 2
. Reassigning a
doesn't have any effect on b
.
回答3:
When you write this:
questionsLeft = " (" + questions + " questions left)"
You are creating a new string at the time that line runs. You are not binding the variable "questions" to it, you are saying to insert the current value of questions there. After that line has run, the value of questionsLeft
is just a single string, it will never change unless and until some code changes its value.
Think of it this way, by analogy: If I tell you there are 5 different colored boxes stacked up, and my favorite color is the color of the one on top, and the top one is blue, then you know my favorite color is blue. If I later rearrange the boxes so that the red one is on top, my favorite color of course has not changed, it is still blue even though now the top box is red.
I hope that analogy made sense...
回答4:
It's because an assignment (such as questionsLeft = /* anything*/
) is only evaluated at the exact moment that line of code runs, and not again.
questionsLeft = " (" + questions + " questions left)"
will cause the code to concatenate a few strings together, and whatever the final result is, that gets assigned to questionsLeft
. questions
was needed in order to do this calculation, but as soon as the calculation is done, there's no longer any association between questions
and questionsLeft
.
There are techniques to try to do the sort of thing you're thinking of, but the code for them will not be nearly as straightforward as the code you've written. This idea is called "Reactive Programming", with the most popular example being a library called rx.js. Perhaps one day we'll get a feature like this baked into a language (i like Paul Stovell's speculation on a Destiny Operator), but it doesn't exist today.
回答5:
You can modify your original code slightly to get it to work. Notice you don't really need the var questionsleft:
var questions = 3;
var adjective = prompt('Please type an adjective --' + questions + ' questions left');
questions -= 1;
var verb = prompt('Please type a verb --' + questions + ' questions left');
questions -= 1;
var noun = prompt('Please type a noun --' + questions + ' questions left');
alert('All done. Ready for the message?');
var sentence = "<h2>There once was a " + adjective;
sentence += ' programmer who wanted to use JavaScript to ' + verb;
sentence += ' the ' + noun + '.</h2>';
document.write(sentence);
来源:https://stackoverflow.com/questions/59887370/why-must-you-state-a-variable-again-after-every-prompt-message-if-you-are-are-su