2010-10-30 36 views
87

Tôi sẽ bắt đầu bằng mã:Bàn điều khiển JavaScript của Chrome có lười biếng về việc đánh giá mảng không?

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

Đơn giản, đúng không? Để đối phó với điều này, Firebug 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"] 

Có tôi làm điều gì đó sai, hoặc là bảng điều khiển JavaScript của Chrome là đặc biệt lười biếng về đánh giá mảng của tôi?

+1

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

+0

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

+0

nếu đây là lỗi, tại sao lỗi này không được tìm thấy và cố định vượt quá khả năng hiểu của tôi. –

Trả lời

47

Cảm ơn nhận xét, 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_bug.cgi?id=35801 (EDIT: đã được sửa!)

Dường như có một số tranh luận liên quan đến lỗi của nó và 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 phiền toá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 hiện ngay lập tức (trước khi trang được tải), ngay cả khi bàn điều khiển đang 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 được kích hoạt chỉ dẫn đến tham chiếu đến đối tượng đang được xếp hàng đợi, không phải đầu ra của bảng đ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 đại diện trong ký ức đó sẽ không được thay đổi bởi câu sau đây, mà giao diện điều khiển sẽ đọc khi nó đã sẵn sàng. Giao diện điều khiển đầu ra là hơi khác nhau từ đi qua các đối tượng trực tiếp, nhưng có vẻ như chấp nhận được:

hi 
bye 
+1

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

+19

JSON.stringify() – draeton

+0

@draeton: Tuyệt. –

0

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

Một con đường xung quanh là bằng cách nhân bản mảng, đăng bản sao tươi thay vì:

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

Tốt, nhưng vì đó là bản sao nông, vẫn còn khả năng xảy ra sự cố tinh tế 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ự bây giờ.) 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] phải được sao chép [i]. –

+0

Không có lỗi, tôi đã thực hiện nó và nó đã được OK. clone [clone.length] giống hệt như clone [i], khi mảng bắt đầu với độ dài là 0, và lặp lại vòng lặp "i". Dù sao, không chắc chắn như thế nào nó sẽ hành xử với các đối tượng phức tạp nhưng IMO nó có giá trị một thử. Như tôi đã nói, đó không phải là giải pháp, đó là cách giải quyết vấn đề .. –

+0

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

5

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.log rằng khô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); 
}; 

Đối với trường hợp của các đối tượng, thật không may, phương pháp tốt nhất dường như là để gỡ lỗi đầu tiên với một trình duyệt không phải WebKit, hoặc để viết một chức năng 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, nơi để các phím không quan trọng và không có chức năng này, bạn luôn có thể làm:

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ả những phương pháp này rõ ràng là rất chậm, vì vậy ngay cả nhiều hơn so với bình thường console.log s, bạn phải loại bỏ chúng sau khi bạn hoàn tất gỡ lỗi.

15

Từ lời giải thích của Eric, đó là do console.log() đang được xếp hàng đợi và nó in 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 
1

này đã được trả lời, nhưng tôi sẽ thả câu trả lời của tôi anyway. 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 bị vấn đề này. Yêu cầu jQuery.

Chỉ thực hiện các phương thức log, warnerror, bạn sẽ phải thêm một số phương thức khác để có thể hoán đổi cho nhau với số console thông thường.

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

này đã được vá trong Webkit, tuy nhiên khi sử dụng Phản ứng khuôn khổ điều này xảy ra cho tôi trong một số trường hợp, nếu bạn có vấn đề như vậy chỉ cần sử dụng như những người khác đề nghị:

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

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 ReactSyntheticEvents. Ngay cả một 'JSON.parse (JSON.stringify (event))' không nhận được độ sâu/độ chính xác phù hợp. Tuyên bố gỡ lỗi là giải pháp thực sự duy nhất tôi đã tìm thấy để có được thông tin chi tiết chính xác. – CStumph

Các vấn đề liên quan