Chrome의 JavaScript 콘솔이 배열 평가에 대해 게으른가요?


126

코드부터 시작하겠습니다.

var s = ["hi"];
console.log(s);
s[0] = "bye";
console.log(s);

간단 하지요? 이에 대한 응답으로 Firebug는 다음과 같이 말합니다.

["hi"]
["bye"]

훌륭하지만 Chrome의 JavaScript 콘솔 (7.0.517.41 베타)은 다음과 같이 말합니다.

["bye"]
["bye"]

내가 뭔가 잘못했거나 Chrome의 자바 스크립트 콘솔이 내 배열을 평가하는 데 매우 게으른가요?

여기에 이미지 설명 입력


1
Safari에서 동일한 동작을 관찰하므로 아마도 웹킷 일 것입니다. 꽤 놀랍습니다. 나는 그것을 버그라고 부를 것입니다.
Lee

7
나에게 그것은 버그처럼 보입니다. Linux Opera 및 Firefox에서는 예상 결과가 표시되지만 Chrome 및 기타 Webkit 기반 브라우저에서는 표시되지 않습니다. Webkit 개발자에게 문제를보고 할 수 있습니다. webkit.org/quality/reporting.html
tec

2
2016 년 3 월 현재이 문제는 더 이상 없습니다.
kmonsoor

1
2020 년 4 월, Chrome에서이 문제가 발생합니다. 내 코드에서 Chrome의 버그로 판명 된 버그를 찾기 위해 2 시간을 낭비했습니다.
The Fox

1
또한 파란색 i아이콘의 툴팁에 "지금 바로 아래 값이 평가되었습니다."라고 표시되어 있습니다.
user4642212

답변:


69

의견 주셔서 감사합니다, tec. 이 문제를 설명하는 확인되지 않은 기존 Webkit 버그를 찾을 수있었습니다 : https://bugs.webkit.org/show_bug.cgi?id=35801 (편집 : 이제 수정되었습니다!)

버그의 정도와 수정 가능 여부에 대한 논쟁이있는 것 같습니다. 그것은 나에게 나쁜 행동처럼 보입니다. 특히 Chrome에서는 페이지가 새로 고쳐질 때마다 콘솔이 열려 있어도 즉시 실행되는 스크립트 (페이지가로드되기 전에)에 코드가있을 때 발생하기 때문에 특히 문제가되었습니다. 콘솔이 아직 활성화되지 않았을 때 console.log를 호출하면 콘솔에 포함될 출력이 아니라 대기열에있는 개체에 대한 참조 만 생성됩니다. 따라서 콘솔이 준비 될 때까지 어레이 (또는 객체)가 평가되지 않습니다. 정말 게으른 평가의 경우입니다.

그러나 코드에서이를 방지하는 간단한 방법이 있습니다.

var s = ["hi"];
console.log(s.toString());
s[0] = "bye";
console.log(s.toString());

toString을 호출하여 다음 명령문에 의해 변경되지 않는 메모리에 표현을 생성합니다.이 표현은 콘솔이 준비되면 읽을 것입니다. 콘솔 출력은 객체를 직접 전달하는 것과 약간 다르지만 허용되는 것 같습니다.

hi
bye

1
실제로 연관 배열 또는 기타 객체의 경우 toString이 값을 생성하지 않기 때문에 이것은 실제 문제가 될 수 있습니다. 일반적으로 개체에 대한 쉬운 해결 방법이 있습니까?
Eric Mickelsen

29
JSON.stringify ()
draeton

1
웹킷은 몇 달 전에이 패치를 착륙
antony.trupe

1
;을 console.log (JSON.parse (JSON.stringify (들)) :이 할
리 콤 스탁

현재 Chrome 버전에서 콘솔이 지연되고 값이 다시 잘못 출력된다는 점을 언급하고 싶었습니다. 예를 들어 배열을 로깅하고 로깅 한 후 최상위 값을 팝업했지만 팝업 된 값없이 표시되었습니다. toString () 제안은 값을 확인하는 데 필요한 위치를 찾는 데 정말 도움이되었습니다.
Nicholas R. Grant

21

Eric의 설명에 따르면 console.log() 대기열에 들어갔 기 배열 (또는 객체)의 나중 값을 인쇄합니다.

다음과 같은 5 가지 솔루션이 있습니다.

1. arr.toString()   // not well for [1,[2,3]] as it shows 1,2,3
2. arr.join()       // same as above
3. arr.slice(0)     // a new array is created, but if arr is [1, 2, arr2, 3] 
                    //   and arr2 changes, then later value might be shown
4. arr.concat()     // a new array is created, but same issue as slice(0)
5. JSON.stringify(arr)  // works well as it takes a snapshot of the whole array 
                        //   or object, and the format shows the exact structure

7

다음을 사용하여 어레이를 복제 할 수 있습니다 Array#slice.

console.log(s); // ["bye"], i.e. incorrect
console.log(s.slice()); // ["hi"], i.e. correct

console.log이 문제가없는 대신 사용할 수있는 기능 은 다음과 같습니다.

console.logShallowCopy = function () {
    function slicedIfArray(arg) {
        return Array.isArray(arg) ? arg.slice() : arg;
    }

    var argsSnapshot = Array.prototype.map.call(arguments, slicedIfArray);
    return console.log.apply(console, argsSnapshot);
};

개체의 경우 불행히도 가장 좋은 방법은 WebKit이 아닌 브라우저로 먼저 디버깅하거나 복제 할 복잡한 함수를 작성하는 것입니다. 키 순서가 중요하지 않고 기능이없는 단순한 객체로만 작업하는 경우 항상 다음을 수행 할 수 있습니다.

console.logSanitizedCopy = function () {
    var args = Array.prototype.slice.call(arguments);
    var sanitizedArgs = JSON.parse(JSON.stringify(args));

    return console.log.apply(console, sanitizedArgs);
};

이 모든 메서드는 분명히 매우 느리므로 일반적인 console.logs 보다 훨씬 더 많이 디버깅을 완료 한 후에 제거해야합니다.


2

이것은 Webkit에서 패치되었지만 React 프레임 워크를 사용할 때 이러한 문제가 발생하면 다른 사람들이 제안한대로 사용하면됩니다.

console.log(JSON.stringify(the_array));

2
확인할 수 있습니다. 이것은 ReactSyntheticEvents를 로그 아웃하려고 할 때 말 그대로 최악입니다. 심지어JSON.parse(JSON.stringify(event)) 올바른 깊이 / 정확성을 얻지 못합니다. 디버거 문은 올바른 통찰력을 얻기 위해 찾은 유일한 실제 솔루션입니다.
CStumph

1

이것은 이미 답변되었지만 어쨌든 내 답변을 삭제하겠습니다. 이 문제가 발생하지 않는 간단한 콘솔 래퍼를 구현했습니다. jQuery가 필요합니다.

그것은 단지 log , warnerror방법, 당신은 정기적으로 교환 할 순서를 좀 더 추가해야합니다 console.

var fixedConsole;
(function($) {
    var _freezeOne = function(arg) {
        if (typeof arg === 'object') {
            return $.extend(true, {}, arg);
        } else {
            return arg;
        }
    };
    var _freezeAll = function(args) {
        var frozen = [];
        for (var i=0; i<args.length; i++) {
            frozen.push(_freezeOne(args[i]));
        }
        return frozen;
    };
    fixedConsole = {
        log: function() { console.log.apply(console, _freezeAll(arguments)); },
        warn: function() { console.warn.apply(console, _freezeAll(arguments)); },
        error: function() { console.error.apply(console, _freezeAll(arguments)); }
    };
})(jQuery);

0

Chrome이 "사전 컴파일"단계에서 "s"의 모든 인스턴스를 포인터 로 대체하는 것 같습니다. 실제 배열에 대한 로 .

한 가지 방법은 어레이를 복제하고 대신 새 복사본을 기록하는 것입니다.

var s = ["hi"];
console.log(CloneArray(s));
s[0] = "bye";
console.log(CloneArray(s));

function CloneArray(array)
{
    var clone = new Array();
    for (var i = 0; i < array.length; i++)
        clone[clone.length] = array[i];
    return clone;
}

좋지만 얕은 카피이기 때문에 여전히 더 미묘한 문제의 가능성이 있습니다. 그리고 배열이 아닌 객체는 어떻습니까? (이것들이 이제 진짜 문제입니다.) "사전 컴파일"에 대해 당신이 말하는 것이 정확하다고 생각하지 않습니다. 또한 코드에 오류가 있습니다. clone [clone.length]는 clone [i]이어야합니다.
Eric Mickelsen

오류 없음, 내가 실행했고 괜찮 았습니다. clone [clone.length]는 배열이 길이 0으로 시작하므로 clone [i]와 똑같습니다. 루프 반복자 "i"도 마찬가지입니다. 어쨌든 복잡한 객체에서 어떻게 작동할지 확실하지 않지만 IMO는 시도해 볼 가치가 있습니다. 나는 해결책이 아니다 즉, 말했듯이,이 문제를 해결하는 방법입니다 ..
어둠의 마법사가 당신을 위해 귀입니다

@Shadow Wizard : 좋은 점 : clone.length는 항상 i와 같습니다. 개체에 대해서는 작동하지 않습니다. 아마도 "for each"솔루션이있을 것입니다.
Eric Mickelsen

당신이 이것을 의미하는 물건? var s = {param1 : "hi", param2 : "잘 지내세요?" }; 그렇다면 방금 테스트했으며 s [ "param1"] = "bye"; 예상대로 잘 작동합니다. "객체에 대해 작동하지 않습니다"의 예를 게시 해 주시겠습니까? 나는 그것도보고 올라 가려고 노력할 것이다.
Shadow Wizard is Ear For You

@Shadow Wizard : 분명히 함수는 속성을 복제하지 못하고 길이 속성이없는 개체에서는 작동하지 않습니다. 웹킷 버그는 배열뿐만 아니라 모든 객체에 영향을 미칩니다.
Eric Mickelsen

0

지금까지 가장 짧은 해결책은 배열 또는 객체 확산 구문을 사용하여 로깅시 보존 할 값의 복제본을 가져 오는 것입니다. 즉,

console.log({...myObject});
console.log([...myArray]);

그러나 얕은 복사를 수행하므로 경고가 있으므로 깊은 중첩 된 비 원시 값은 복제되지 않으므로 콘솔에 수정 된 상태로 표시됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.