BaldEagle
BaldEagle

Reputation: 1018

Why do javascript calls to console.log affect logic flow?

In Javascript, I'm using console.log for debugging. It would seem that once I have the logic worked out, I could confidently take out the debugging statements without affecting logic. Here's evidence to the contrary. (Said tentatively, because I'm probably wrong on that count!) First, the program; please look for the long series of asterisks. (I'm going through a text book on my own; this is an assignment there, but you're not helping me with homework. I'm trying to understand the language!)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Why is call to console.log needed?</title>
    <script type="text/javascript">
        /** IterAbstract: Constructor for a means of visiting every object in
         * a collection.
         *
         * @param arr: the collection, either an ArraySeq or a RangeSeq
         * @returns
         *   method getNext returns (1) (most likely) a member of arr;
         *     possibly the value undefined, if that's in the collection, or
         *     (2) the value undefined, an tentative indication that there
         *     are no further elements.
         *   method getAtEnd returns (1) true, if no further elements remain,
         *     or (2) false, if further elements remain to be sent.
         * @constructor IterAbstract
         * Users should use only the two methods.
         */
        function IterAbstract (arr) {
            this.arr = arr;
            this.last = this.arr.length - 1;
            this.lastSent = null;
            this.atEnd = false;
        }
            /* *************************
               The focus is the call to console.log at the start of the
               function following these comments.
               ************************* */
            /* The expected behavior of this file is that when function
               logFive receives a two-element array, there will be three calls
               to getNext. The first call uses the return commented "return
               for the first element"; it returns the first element of the
               array and logFive puts it on console.log. The second call uses
               the return commented "return for all other elements"; that
               return makes all other elements available for logFive to record
               on console.log. The third call creates the unambiguous "we're
               past the end of file" signal (a combination of this.atEnd=true
               and returning the undefined value); it uses the return
               commented "after-the-end return".

               When logFive gets an array [1, 2], we expect to see two calls
               to console.log, one with each element.

               When logFive gets an array [null, undefined], we expect the same,
               though with different (and admittedly squirrely) elements
               demonstrated.

               Good: When the subject call to console.log occurs (not commented
               out), the expected behavior occurs. Though there are interspersed
               lines starting "getNext", we see rows with the four expected
               outputs: 1, 2, null, and undefined.

               Bad: When we comment out the subject call to console.log, we see
               three of the four expected rows: 1, 2, and null.

               There is a series of three other calls to console.log designed
               to show us which exit the code uses. They have been commented out
               before; let's uncomment the them.

               Good: Though there are interspersed lines starting "getNext", we
               see the four expected rows. The code is using the expected
               returns.

               Why do these debugging calls to console.log affect actual logic
               flow? (he he) Where's the user error here?
            */
        IterAbstract.prototype.getNext = function () {
            console.log("getNext   this.lastSent=" + this.lastSent +
                    "   this.atEnd=" + this.atEnd);
            if (this.lastSent === this.last) {
                //console.log("getNext after-the-end return   this.lastSent="+
                //        this.lastSent+"   this.last="+this.last);
                this.atEnd = true;
                return undefined; // after-the-end return
            }
            if (this.lastSent === null) {
                this.lastSent = 0;
                //console.log("getNext first exit   element=0");
                return this.arr[0]; // return for the first element
            }
            var sent=this.lastSent+1;
            //console.log("getNext normal exit   element="+sent);
            return this.arr[++this.lastSent]; // return for all other elements
        };
        IterAbstract.prototype.getAtEnd = function () { return this.atEnd; };
        /** logFive: Shows the first five elements of a collection on
         * console.log; less, if the collection has less.
         * @param arr: the collection, either an ArraySeq or a RangeSeq
         */
        function logFive (arr) {
            var iterArr = new IterAbstract(arr);
            /*console.log("after Constructor   iterArr.arr="+iterArr.arr+
                    "   iterArr.last="+iterArr.last+
                    "   iterArr.lastSent="+iterArr.lastSent+
                    "   iterArr.atEnd="+iterArr.atEnd); */
            var response;
            for (i=0; i < 5; i++) {
                //console.log("start iter   i="+i+
                //        "   iterArr.atEnd="+iterArr.atEnd);
                response = iterArr.getNext();
                /*console.log("iter i="+i+"   response="+response+
                        "   iterArr.atEnd="+iterArr.atEnd);*/
                if (response === undefined && iterArr.getAtEnd()) return;
                //console.log("iter normal exit");
                console.log(response);
        }   }
        function ArraySeq ( arr ) {
            return arr;
        }
        console.log ("logFive(new ArraySeq([1, 2]));");
        logFive(new ArraySeq([1, 2]));
        // → 1
        // → 2
        console.log ("logFive(new ArraySeq([null,undefined]);");
        logFive(new ArraySeq([null,undefined]));
        // → null
        // → undefined
    </script>
</head>
<body>
<p>Output for this program goes to console.log.</p>

<p>In Firefox, press Ctrl-Shift-K to see that output.</p>

<p>In Chrome, press Ctrl-Shift-J to see that output.</p>
</body>
</html>

The output of that code is:

logFive(new ArraySeq([1, 2]));
getNext   this.lastSent=null   this.atEnd=false 
1 
getNext   this.lastSent=0   this.atEnd=false 
2 
getNext   this.lastSent=1   this.atEnd=false 
logFive(new ArraySeq([null,undefined]); 
getNext   this.lastSent=null   this.atEnd=false 
null 
getNext   this.lastSent=0   this.atEnd=false 
undefined 
getNext   this.lastSent=1   this.atEnd=false

Sweet! Almost done. That's just what I want, except for the lines starting "getNext". Let's comment out that call to console.log at the beginning of getNext. Then, the output becomes:

logFive(new ArraySeq([1, 2])); 
1 
2 
logFive(new ArraySeq([null,undefined]); 
null

Hey! No fair. I need that other line, "undefined". Where'd it go?

Maybe if I can figure out what return statement was used each time, I'll learn something interesting. I'll uncomment the three calls to console.log at each exit point in getNext. Now, the output becomes:

logFive(new ArraySeq([1, 2])); 
getNext first exit   element=0 
1 
getNext normal exit   element=1 
2 
getNext after-the-end return   this.lastSent=1   this.last=1 
logFive(new ArraySeq([null,undefined]); 
getNext first exit   element=0 
null 
getNext normal exit   element=1 
undefined 
getNext after-the-end return   this.lastSent=1   this.last=1

Sigh. If I can put debugging statements in, I can get the right output, except for the extra junk.

Wassup? Thanks in advance!

Upvotes: 2

Views: 404

Answers (1)

Maxime Peloquin
Maxime Peloquin

Reputation: 925

Firefox groups both null and undefined together. Putting another console.log in between them force them to no longer by grouped. Grouping is there to help keep the console cleaner when multiple similar logs are displayed, like in a loop for example.

Here Firefox logs everything on a separate line because no line back to back are similar : enter image description here

But for some reason it considers null and undefined similar enough to be grouped together when they are back to back :

enter image description here

Your output is correct. The way Firefox displays it is not.

Upvotes: 2

Related Questions