为什么 javascript 调用 console.log 会影响逻辑流?

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

在 Javascript 中,我使用 console.log 进行调试。似乎一旦我把逻辑搞定了,我就可以自信地把调试语句拿出来而不影响逻辑了。这是相反的证据。 (试探性地说,因为我在这方面可能是错误的!)首先,程序;请寻找一长串星号。 (我正在自己翻阅教科书;这是那里的作业,但您没有帮我做家庭作业。我正在努力理解这门语言!)

<!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>

该代码的输出是:

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

太棒了!快完成了。这正是我想要的,除了以 "getNext" 开头的行。让我们注释掉 getNext 开头对 console.log 的调用。然后,输出变为:

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

嘿!不公平。 我需要另一行,"undefined"。它去哪儿了?

也许如果我能弄清楚每次使用的 return 语句是什么,我会学到一些有趣的东西。我将在 getNext 的每个退出点取消注释对 console.log 的三个调用。现在,输出变为:

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

唉。如果我可以放入调试语句,我可以获得正确的输出,除了多余的垃圾。

怎么了?提前致谢!

Firefox 将 null 和 undefined 组合在一起。在它们之间放置另一个 console.log 会迫使它们不再分组。当显示多个类似的日志时,分组有助于保持控制台更清晰,例如在循环中。

这里 Firefox 将所有内容记录在单独的一行中,因为没有一行背靠背是相似的:

但出于某种原因,它认为 null 和 undefined 足够相似,可以在背靠背时归为一组:

你的输出是正确的。 Firefox 显示它的方式不是。