一個「詭異」的console.log()結果

一個詭異的結果

在前端開發中,咱們都少不了用console.log來輸出變量和調試。但是在使用它的過程當中,偶爾也會出現一些讓咱們很費解的行爲,以下圖:前端

圖片描述

是否是很詭異:)node

這個詭異的行爲是怎麼來的呢?其實怪我「斷章取義」了。控制檯上我執行的代碼實際上是:chrome

圖片描述

注意到展開的數組旁邊有一個小i,將鼠標移動上去,會出現提示文字:數組

Value below was evaluated just now.

簡單翻譯一下就是:下面展現的值,是剛剛解釋執行(evaluated,或者你能夠用你熟悉的eval函數來理解這個單詞)的結果。瀏覽器

這句話該怎麼理解呢?注意到:小i這個提示僅在咱們展開數組的時候纔會出現,而展開前控制檯上展現的確實是一個空數組[]。所以咱們能夠將展開先後認爲是這個值的兩個狀態。函數

如今,咱們定位到了這個詭異的行爲是和展開相關的,那麼讓咱們來實驗一下展開這個操做會對log出來的值產生什麼影響呢?咱們將一行一行地在控制檯執行下面三行js代碼:ui

var a = []
console.log(a)
a.push(1, 2, 3, 4, 5)

在執行a.push(1)以前展開[],會獲得下面的結果,應該算是一個預期結果:lua

圖片描述

而在執行a.push(1)以後再展開[],就會獲得一開始我給你們看的「詭異」結果了。spa

圖片描述

如今回過頭來看 Value below was evaluated just now. 這句話,其實說的就是,展開後的值,實際上是在你點擊展開小三角的時候,才 「eval」 出來的。prototype

再看下面一個例子:

圖片描述

圖中的結果,我是在執行了a = [1, 2, 3, 4, 5]這句話以後才點擊的小三角,然而結果卻沒有變成「詭異」結果的樣子。這是由於後面我更新數組是直接採用變量賦值的方法,至關於修改了變量a的引用,就和變量的引用賦值同樣,是影響不了a以前所引用的數組的。

也就是說,console.log()在展開時用於eval的,是變量指向的引用而不是變量自己。

試試其餘瀏覽器

console.log的這個行爲並非chrome限定,在Firefox和Safari中你都能獲得一樣的行爲。

Firefox:

圖片描述

Safari中因爲console.log展不開,所以使用console.dir來展開:

圖片描述

Edge下就不是這樣子了233:

圖片描述

因此若是你不想要這個「詭異」的結果的話,能夠用 Edge 調試哦:)

若是不用Edge呢?

一個正常的思路是,若是可以讓console.log直接輸出就是展開的,那麼這個行爲就不會詭異了。

不過很不幸,我沒有找到可以作這件事情的API。不過,咱們能夠藉助console.group這個方法,本身造一個展開的結構出來。

/**
 * expandLog
 *
 * @desc 自動展開的 console.log,實現參考:
 * https://stackoverflow.com/questions/10464844/is-there-a-way-to-auto-expand-objects-in-chrome-dev-tools#27610197
 * @author leuisken <leuisken@foxmail.com>
 * @param {Object} obj 須要 log 的對象
 */
function expandLog(obj) {
    if (Array.isArray(obj)) {
        obj.forEach((value, index) => {
            console.group(`${index} : ${type(value)}`);
            expandLog(value);
            console.groupEnd();
        });
    }
    else if (isPlainObject(obj)) {
        Object.keys(obj)
            .forEach(key => {
                const value = obj[key];
                console.group(`${key} : ${type(value)}`);
                expandLog(value);
                console.groupEnd();
            });
    }
    else {
        console.log(obj);
    }
    return;

    /**
     * type
     *
     * @desc 針對部分常見類型給予更好的輸出方式
     * @param {Object} obj 傳入的對象
     * @return {string} 類型字符串
     */
    function type(obj) {
        const typeofResult = typeof obj;

        if (typeofResult !== 'object') {
            return typeofResult;
        }
        else if (obj === null) {
            return 'null';
        }
        else if (Array.isArray(obj)) {
            return 'Array';
        }
        else if (obj instanceof RegExp) {
            return 'RegExp';
        }
        else if (obj instanceof Date) {
            return 'Date';
        }
        return 'Object';
    }

    /**
     * isPlainObject
     *
     * @desc 即:jQuery.isPlainObject
     * @param {Object} obj 傳入的對象
     * @return {boolean} 是否爲 PlainObject
     */
    function isPlainObject(obj) {
        if (!obj
            || obj.toString() !== '[object Object]'
            || obj.nodeType
            || obj.setInterval
        ) {
            return false;
        }

        if (obj.constructor
            && !obj.hasOwnProperty('constructor')
            && !obj.constructor.prototype.hasOwnProperty('isPrototypeOf')
        ) {
            return false;
        }

        let key;
        for (key in obj) {}

        return key === undefined || obj.hasOwnProperty(key);
    }
}

這裏寫這個方法只是示個意,估計通常也不會有誰這麼作。。其實很無奈,就和console.logconsole.dir沒有提供默認展開的API同樣,console.group也沒有提供默認收起的API。。。。

題外話

在搜索可否默認展開console.log結果的時候,在Stack Overflow上無心間搜到了這樣一個結果。

https://stackoverflow.com/que...

原來 chrome 的 console,也有本身的 console 啊。。

相關文章
相關標籤/搜索