1
\$\begingroup\$

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>

  1. Can I invoke calcCircleArea from the HTML code to render the given output?

  2. Can the else condition in calcCircleArea have better code?

\$\endgroup\$
4
\$\begingroup\$

To answer your questions directly:

  1. Yes.
  2. 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

  1. it's <!DOCTYPE html> with all-capital DOCTYPE
  2. 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>
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ window is a dictionary, so I used this bracket notation \$\endgroup\$ – overexchange Dec 12 '15 at 23:41
  • 2
    \$\begingroup\$ @overexchange JavaScript does not have "dictionaries", just objects. window is an object. \$\endgroup\$ – Flambino Dec 12 '15 at 23:42
  • \$\begingroup\$ Python and Javascript memory model is same. Dictionaries of Dictionaries. I can realise this, when I enumerate the properties of an object. \$\endgroup\$ – overexchange Dec 13 '15 at 0:10
  • 2
    \$\begingroup\$ @overexchange You can enumerate properties on anything in JavaScript because everything is an object. And yet there's still nothing called a "dictionary" in JavaScript. Sure it may work the same, but it's still not called a dictionary. Or, you can call it a dictionary, but then you can also call it a hash, or a map. It's still an object, though. And you still use dot-notation unless you have good reason not to. \$\endgroup\$ – Flambino Dec 13 '15 at 1:05
2
\$\begingroup\$

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>
| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

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>

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.