Bảng điều khiển JavaScript của Chrome có lười biếng trong việc đánh giá mảng không?


126

Tôi sẽ bắt đầu với mã:

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

Đơn giản phải không? Đáp lại điều này, Fireorms nói:

["hi"]
["bye"]

Tuyệt vời, nhưng bảng điều khiển JavaScript của Chrome (7.0.517.41 beta) cho biết:

["bye"]
["bye"]

Tôi đã làm điều gì sai, hay bảng điều khiển JavaScript của Chrome đặc biệt lười biếng trong việc đánh giá mảng của tôi?

nhập mô tả hình ảnh ở đây


1
Tôi quan sát hành vi tương tự trong Safari - vì vậy đây có thể là một thứ webkit. Khá ngạc nhiên. Tôi gọi nó là một lỗi.
Lee

@mplungjan - Điều đó không đúng. dòng đầu tiên khai báo một mảng "cũ đơn giản" với một phần tử duy nhất tại chỉ mục 0. Dòng thứ ba chỉ cần gán một giá trị mới cho phần tử đó. cả hai trường hợp đang làm việc với một mảng được lập chỉ mục số đơn giản.
Lee

Nếu đây là một lỗi, tại sao lỗi này không được tìm thấy và sửa chữa là ngoài tầm hiểu biết của tôi.
phân cực

7
Đối với tôi nó trông giống như một lỗi. Trên Linux Opera và Firefox hiển thị kết quả mong đợi, Chrome và các trình duyệt dựa trên Webkit khác thì không. Bạn có thể muốn báo cáo vấn đề với các nhà phát triển Webkit: webkit.org/quality/reporting.html
tec

2
kể từ tháng 3 năm 2016, vấn đề này không còn nữa.
kmonsoor

Câu trả lời:


69

Cảm ơn các bình luận, tec. Tôi đã có thể tìm thấy một lỗi Webkit chưa được xác nhận hiện có giải thích vấn đề này: https://bugs.webkit.org/show_orms.cgi?id=35301 (EDIT: hiện đã được sửa!)

Dường như có một số tranh luận liên quan đến mức độ lỗi của nó và liệu nó có thể sửa được không. Nó có vẻ như hành vi xấu với tôi. Điều này đặc biệt gây rắc rối cho tôi bởi vì, trong Chrome ít nhất, nó xảy ra khi mã nằm trong các tập lệnh được thực thi ngay lập tức (trước khi trang được tải), ngay cả khi bảng điều khiển được mở, bất cứ khi nào trang được làm mới. Gọi console.log khi giao diện điều khiển chưa hoạt động chỉ dẫn đến một tham chiếu đến đối tượng đang được xếp hàng, chứ không phải đầu ra mà giao diện điều khiển sẽ chứa. Do đó, mảng (hoặc bất kỳ đối tượng nào), sẽ không được đánh giá cho đến khi bàn điều khiển sẵn sàng. Nó thực sự là một trường hợp đánh giá lười biếng.

Tuy nhiên, có một cách đơn giản để tránh điều này trong mã của bạn:

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

Bằng cách gọi toString, bạn tạo một biểu diễn trong bộ nhớ sẽ không bị thay đổi bởi các câu lệnh sau, bảng điều khiển sẽ đọc khi nó sẵn sàng. Đầu ra của bàn điều khiển hơi khác so với việc truyền trực tiếp đối tượng, nhưng có vẻ chấp nhận được:

hi
bye

1
Trên thực tế, với các mảng kết hợp hoặc các đối tượng khác, đây có thể là một vấn đề thực sự, vì toString không tạo ra bất cứ thứ gì có giá trị. Có một công việc dễ dàng cho các đối tượng nói chung?
Eric Mickelsen

29
JSON.opesify ()
Draineton

1
webkit đã cập nhật một bản vá cho việc này vài tháng trước
antony.trupe

1
làm điều này: console.log (JSON.parse (JSON.opesify (s));
Lee Comstock

Tôi chỉ muốn đề cập rằng trong phiên bản Chrome hiện tại, bảng điều khiển bị trì hoãn và xuất ra các giá trị sai một lần nữa (hoặc nó đã từng đúng). Ví dụ, tôi đã ghi nhật ký một mảng và xuất hiện giá trị hàng đầu sau khi đăng nhập, nhưng nó đã hiển thị mà không có giá trị xuất hiện. Đề xuất toString () của bạn thực sự hữu ích trong việc đến nơi tôi cần để xem các giá trị.
Nicholas R. Grant

21

Từ lời giải thích của Eric, đó là do console.log()được xếp hàng và nó in một giá trị sau của mảng (hoặc đối tượng).

Có thể có 5 giải pháp:

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

Bạn có thể sao chép một mảng với Array#slice:

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

Một chức năng mà bạn có thể sử dụng thay vì console.logkhông có vấn đề này như sau:

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);
};

Thật không may, đối với trường hợp của các đối tượng, không may, phương pháp tốt nhất xuất hiện trước tiên là gỡ lỗi với trình duyệt không phải WebKit hoặc viết một hàm phức tạp để sao chép. Nếu bạn chỉ làm việc với các đối tượng đơn giản, trong đó thứ tự các phím không quan trọng và không có chức năng, bạn luôn có thể thực hiện:

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

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

Tất cả các phương thức này rõ ràng là rất chậm, do đó, thậm chí còn nhiều hơn so với các phương pháp bình thường console.log, bạn phải loại bỏ chúng sau khi bạn gỡ lỗi xong.


2

Điều này đã được vá trong Webkit, tuy nhiên khi sử dụng khung React, điều này xảy ra với tôi trong một số trường hợp, nếu bạn gặp vấn đề như vậy, hãy sử dụng như những người khác đề xuất:

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

2
Có thể xác nhận. Đây thực sự là điều tồi tệ nhất khi cố gắng đăng xuất ReactSy mergEvents. Ngay cả một JSON.parse(JSON.stringify(event))không có được độ sâu / độ chính xác đúng. Báo cáo trình gỡ lỗi là giải pháp thực sự duy nhất tôi tìm thấy để có cái nhìn sâu sắc chính xác.
CStumph

1

Điều này đã được trả lời, nhưng dù sao tôi cũng sẽ bỏ câu trả lời của mình. Tôi đã triển khai một trình bao bọc giao diện điều khiển đơn giản mà không gặp phải vấn đề này. Yêu cầu jQuery.

Nó chỉ thực hiện log, warnerrorcác phương pháp, bạn sẽ có thêm một số chi tiết để cho nó được hoán đổi cho nhau với một thường xuyên 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

Có vẻ như Chrome đang thay thế trong giai đoạn "biên dịch trước" bất kỳ trường hợp nào của "s" bằng con trỏ tới mảng thực tế.

Một cách xung quanh là bằng cách nhân bản mảng, đăng nhập bản sao mới thay thế:

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;
}

Điều đó tốt, nhưng vì đó là một bản sao nông, vẫn có khả năng xảy ra vấn đề tinh vi hơn. Và những gì về các đối tượng không phải là mảng? (Đó là những vấn đề thực sự hiện nay.) Tôi không nghĩ rằng những gì bạn đang nói về "biên dịch trước" là chính xác. Ngoài ra, có một lỗi trong mã: clone [clone.length] nên được sao chép [i].
Eric Mickelsen

Không có lỗi, tôi đã thực hiện nó và nó vẫn ổn. clone [clone.length] hoàn toàn giống như clone [i], vì mảng bắt đầu với độ dài bằng 0, và vòng lặp lặp "i" cũng vậy. Dù sao, không chắc nó sẽ hành xử thế nào với các đối tượng phức tạp nhưng IMO nó đáng để thử. Như tôi đã nói, đó không phải là một giải pháp, đó là cách giải quyết vấn đề ..
Shadow Wizard là Ear For You

@Shadow Wizard: Điểm hay: clone.length sẽ luôn bằng i. Nó sẽ không làm việc cho các đối tượng. Có lẽ có một giải pháp với "cho mỗi".
Eric Mickelsen

Đối tượng của bạn có nghĩa là gì? var s = {param1: "hi", param2: "bạn có khỏe không?" }; nếu vậy tôi chỉ thử nghiệm và khi bạn có s ["param1"] = "bye"; nó hoạt động tốt như mong đợi. Bạn có thể vui lòng gửi ví dụ về "nó sẽ không hoạt động cho các đối tượng" không? Tôi cũng sẽ thấy và cố gắng trèo lên cái đó.
Shadow Wizard là Ear For You

@Shadow Wizard: Rõ ràng, chức năng của bạn sẽ không sao chép các thuộc tính và sẽ không hoạt động trên bất kỳ đối tượng nào nếu không có thuộc tính độ dài. Lỗi webkit ảnh hưởng đến tất cả các đối tượng, không chỉ các mảng.
Eric Mickelsen

0

giải pháp ngắn nhất cho đến nay là sử dụng cú pháp trải rộng mảng hoặc đối tượng để có được một bản sao các giá trị được bảo tồn như trong thời gian đăng nhập, tức là:

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

tuy nhiên được cảnh báo vì nó là một bản sao nông, do đó, bất kỳ giá trị không nguyên thủy nào được lồng sâu sẽ không được sao chép và do đó được hiển thị ở trạng thái sửa đổi của chúng trong bảng điều khiển

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.