Problem
1. Problem Statement
Write a JavaScript function calcCircleArea(r) that takes as a parameter the radius of a circle and calculates and returns its area. Put the function in a file named circle-area.js. Write a HTML page circle-area.html that includes the script circle-area.js and calculates and prints in the page body the area of circles of size r=7, r=1.5 and r=20.
2. Expected Output
r = 7; area = 153.93804002589985 r = 1.5; area = 7.0685834705770345 r = 20; area = 1256.6370614359173
Solution:
function calcCircleArea(r){
if(r >= 0){
return window['Math']['PI'] * r * r;
}else{
return "wrong input";
}
}
document.write('r = 7; area = ' + window['calcCircleArea'](7) + "<br />");
document.write('r = 1.5; area = ' + window['calcCircleArea'](1.5) + "<br />");
document.write('r = 20; area = ' + window['calcCircleArea'](20) + "<br />");
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Circle area</title>
<script src="circle-area.js"></script>
</head>
<body>
</body>
</html>
-
Can I invoke
calcCircleArea
from the HTML code to render the given output? -
Can the
else
condition incalcCircleArea
have better code?
Solution
To answer your questions directly:
- Yes.
- Maybe. Depends on what you define as “better”.
First, though: Don’t use bracketed (aka subscripting) access to properties when regular dot notation works, e.g.:
window.Math.PI
is exactly the same as:
window['Math']['PI']
but a lot simpler.
Secondly, don’t bother with window
when there’s no need. window
is the global object in a browser’s JS runtime, so you can just say
Math.PI
The exception to this is if you have variable shadowing, but that’s outside the scope of this review.
Now, to tackle question 2 first, you could do this:
function calcCircleArea(r) {
if(r >= 0) {
return Math.PI * r * r;
}
return "invalid input";
}
It’s quite subtle, but instead of having an else
branch, you just.. don’t have an else
branch. If the first condition is true, the function returns the area – the rest of the function never runs. But if it’s false, we skip straight past the if
block. The point of this is to ensure that the function always returns something. It already did that – there was nothing wrong with your code – but here it’s even more explicit.
However, you could argue that handling invalid input is the less common option, hence that should be the thing to short-circuit the function, and return early:
function calcCircleArea(r) {
if(r < 0) {
return "invalid input";
}
return Math.PI * r * r;
}
In other words, you could assume good intentions (that the function will typically be called with proper arguments), and treat invalid input as the exception to the rule. However, you might be better served by having your function raise hell if its input is invalid. Right now you’re returning something regardless of input, but maybe you want to completely bail out, if things aren’t as expected (after all, a negative radius just doesn’t make sense, so there’s literally no good answer). Hence why I mentioned “exception to the rule”, since exceptions, as concept, are made for that purpose. Now, exceptions are also a topic on their own, but in this case you could do:
function calcCircleArea(r) {
if(r < 0) {
throw "Invalid input";
}
return Math.PI * r * r;
}
throw
works the same as return
in the sense that the function just stops. But the caller has to handle it differently, or – if it’s not handled – it’ll bubble up as an error. But I digress.
Now, for question 1, yes, you can call the function from the HTML. In fact, you really should do that here, since you’re currently using document.write
in a script that’s placed in the head
element. Since document.write
happens immediately, your HTML actually ends up looking like this:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Circle area</title>
r = 7; area = 153.93804002589985<br />r = 1.5; area = 7.0685834705770345<br/>r = 20; area = 1256.6370614359173
</head>
<body>
</body>
</html>
because the browser reads the scripts, and runs it. Obviously, this isn’t really valid HTML. Thankfully, a browser is very, very, very tolerant, and renders things just fine anyway, even if it makes no sense.
Now (still before actually answering your question), let me quickly point out that
- it’s
<!DOCTYPE html>
with all-capitalDOCTYPE
- since that doctype means HTML5, you don’t need XHTML-style self-closed tags like
<br />
. Just write<br>
.
Finally, to answer your question, remove the three document.write
lines from your script, and use this HTML instead:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Circle area</title>
<script src="circle-area.js"></script>
</head>
<body>
<p>
r = 7; area = <script>document.write(calcCircleArea(7));</script>
</p>
<p>
r = 1.5; area = <script>document.write(calcCircleArea(1.5));</script>
</p>
<p>
r = 20; area = <script>document.write(calcCircleArea(20));</script>
</p>
</body>
</html>
window['Math']['PI']
would be better written as window.Math.PI
. Furthermore, since all JavaScript code in a browser is run inside the window
scope, it is customary to omit window
, and just write Math.PI
. Similarly, window['calcCircleArea'](7)
is an awkward way of calling calcCircleArea(7)
. (You would only use window['property-name']
if property-name
were not a legal JavaScript identifier, such as a hyphenated string.)
Error reporting would be better done by throwing a RangeError
.
function calcCircleArea(r) {
if (r < 0) throw new RangeError('Radius cannot be negative');
return Math.PI * r * r;
}
Putting the document.write(…)
calls in circle-area.js
is not quite right. For maintainability, it would be better to put only function definitions into the .js
file. You don’t want the act of including the circle-area.js
to have the side-effect of writing something to the document. In this case, the document.write(…)
calls plop the output into the document’s <head>
, because that’s where the <script>
tag is. The <body>
is the more appropriate place for the content.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Circle area</title>
<script src="circle-area.js"></script>
</head>
<body>
<script type="text/javascript">
document.write('r = 7; area = ' + calcCircleArea(7) + "<br />");
document.write('r = 1.5; area = ' + calcCircleArea(1.5) + "<br />");
document.write('r = 20; area = ' + calcCircleArea(20) + "<br />");
</script>
</body>
</html>
First, I’m not sure why you’re using bracket notation for accessing stuff. Bracket notation is typically used when 1) the key is dynamic or 2) the property has an illegal character in its name. Use dot notation whenever possible.
Now your calculation is simply taking an r
and returning a result. You can simplify using a ternary. It’s also good to check if r
is also a number in the first place. isNaN
will be your friend.
As for document.write
, while there are valid uses for it, I suggest you avoid using it. Try other methods, like innerHTML
or creating an element using document.createElement
and stuffing text to it.
function calcCircleArea(r){
return (!isNaN(r) && r >= 0) ? Math.PI * r * r : 'wrong input';
}
function printResults(element, r){
element.innerHTML = 'r = ' + r + '; area = ' + calcCircleArea(r);
}
printResults(document.getElementById('results1'), 7)
printResults(document.getElementById('results2'), 1.5)
printResults(document.getElementById('results3'), 20)
<div id="results1"></div>
<div id="results2"></div>
<div id="results3"></div>