क्या क्रोम का जावास्क्रिप्ट ऐरे के मूल्यांकन के बारे में आलसी है?


126

मैं कोड के साथ शुरू करूँगा:

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

सरल, सही? इसके जवाब में, फायरबग कहता है:

["hi"]
["bye"]

अद्भुत, लेकिन क्रोम का जावास्क्रिप्ट कंसोल (7.0.517.41 बीटा) कहता है:

["bye"]
["bye"]

क्या मैंने कुछ गलत किया है, या Chrome की जावास्क्रिप्ट कंसोल मेरे सरणी का मूल्यांकन करने के बारे में असाधारण रूप से आलसी है?

यहाँ छवि विवरण दर्ज करें


1
मैं सफारी में एक ही व्यवहार का पालन करता हूं - इसलिए यह शायद एक वेबकिट है। बहुत आश्चर्य हुआ। मैं इसे बग कहूंगा।
ली

7
मेरे लिए यह एक बग की तरह लग रहा है। लिनक्स ओपेरा और फ़ायरफ़ॉक्स पर अपेक्षित परिणाम प्रदर्शित करते हैं, क्रोम और अन्य वेबकिट-आधारित ब्राउज़र नहीं करते हैं। आप इस मुद्दे को वेबकिट देवों को रिपोर्ट करना चाह सकते हैं: webkit.org/quality/reporting.html
tec

2
मार्च 2016 तक, यह मुद्दा नहीं है।
20on पर kmonsoor

1
अप्रैल 2020, क्रोम में यह समस्या है। मेरे कोड में बग की तलाश में 2 घंटे बर्बाद हो गए जो क्रोम में बग बन गए।
फॉक्स

1
यह भी ध्यान देने योग्य है कि नीले iआइकन का टूलटिप कहता है "अभी नीचे मूल्य का मूल्यांकन किया गया था।"
user4642212

जवाबों:


69

टिप्पणी के लिए धन्यवाद, tec। मैं एक मौजूदा अपुष्ट Webkit बग को खोजने में सक्षम था जो इस मुद्दे की व्याख्या करता है: https://bugs.webkit.org/show_bug.cgi?id=35801 (EDIT: now fixed!)

प्रतीत होता है कि यह कितना बग है और क्या यह ठीक है के संबंध में कुछ बहस हुई। यह मुझे बुरा व्यवहार लगता है। यह मुझे विशेष रूप से परेशान कर रहा था क्योंकि, क्रोम में कम से कम, यह तब होता है जब कोड उन लिपियों में रहता है जो तुरंत निष्पादित की जाती हैं (पृष्ठ लोड होने से पहले), यहां तक ​​कि जब भी पृष्ठ ताज़ा होता है, तब कंसोल खुला रहता है। कंसोल को कॉल करना। जब कंसोल अभी तक सक्रिय नहीं है, तो परिणाम केवल ऑब्जेक्ट के संदर्भ में होता है, न कि आउटपुट में कंसोल शामिल होगा। इसलिए, सरणी (या किसी भी वस्तु) का मूल्यांकन तब तक नहीं किया जाएगा, जब तक कि कंसोल तैयार न हो जाए। यह वास्तव में आलसी मूल्यांकन का मामला है।

हालाँकि, आपके कोड में इससे बचने का एक सरल तरीका है:

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

String को कॉल करके, आप स्मृति में एक प्रतिनिधित्व बनाते हैं जो कि निम्नलिखित कथनों द्वारा परिवर्तित नहीं किया जाएगा, जो कंसोल तैयार होने पर पढ़ेगा। कंसोल आउटपुट सीधे ऑब्जेक्ट को पास करने से थोड़ा अलग है, लेकिन यह स्वीकार्य लगता है:

hi
bye

1
वास्तव में, साहचर्य सरणियों या अन्य वस्तुओं के साथ, यह एक वास्तविक समस्या हो सकती है, क्योंकिस्ट्रिंग मूल्य का कुछ भी उत्पादन नहीं करता है। क्या सामान्य रूप से वस्तुओं के लिए एक आसान काम है?
एरिक मिकेलसेन

29
JSON.stringify ()
draeton

1
वेबकिट ने कुछ महीनों पहले इसके लिए एक पैच उतारा
antony.trupe

1
यह करें: कंसोल.लॉग (JSON.parse (JSON.stringify (s));
ली कॉमस्टॉक

मैं सिर्फ यह उल्लेख करना चाहता था कि वर्तमान क्रोम संस्करण में कंसोल को देरी हो रही है और मान फिर से गलत हो रहा है (या यह कभी सही था)। उदाहरण के लिए, मैं एक सरणी को लॉग कर रहा था और इसे लॉग करने के बाद शीर्ष मान को पॉपअप कर रहा था, लेकिन यह बिना पॉपअप मूल्य के दिखा रहा था। आपका TheString () सुझाव वास्तव में जहाँ मुझे मूल्यों को देखने के लिए प्राप्त करने की आवश्यकता थी, वहां पहुंचने में मददगार था।
निकोलस आर। ग्रांट

21

एरिक के स्पष्टीकरण से, यह 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);
};

वस्तुओं के मामले में, दुर्भाग्यवश, सबसे अच्छा तरीका सबसे पहले एक गैर-वेबकिट ब्राउज़र के साथ डिबग करना या क्लोन करने के लिए एक जटिल फ़ंक्शन लिखना प्रतीत होता है। यदि आप केवल साधारण वस्तुओं के साथ काम कर रहे हैं, जहां कुंजियों का क्रम मायने नहीं रखता है और कोई कार्य नहीं हैं, तो आप हमेशा कर सकते हैं:

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

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

ये सभी विधियां स्पष्ट रूप से बहुत धीमी हैं, इसलिए सामान्य console.logएस की तुलना में भी अधिक , आपको डीबगिंग करने के बाद उन्हें बंद करना होगा।


2

यह वेबकिट में पैच कर दिया गया है, हालांकि कुछ परिस्थितियों में मेरे लिए रिएक्ट फ्रेमवर्क का उपयोग करते समय, यदि आपके पास ऐसी समस्याएं हैं, तो दूसरों के सुझाव के अनुसार उपयोग करें:

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

2
पुष्टि कर सकते हैं। जब ReactSyntheticEvents लॉग आउट करने की कोशिश कर रहा है, तो यह सचमुच सबसे खराब है। यहां तक JSON.parse(JSON.stringify(event))कि सही गहराई / सटीकता नहीं मिलती है। सही विवरण प्राप्त करने के लिए डिबगर कथन एकमात्र वास्तविक समाधान है।
CStumph

1

यह पहले से ही उत्तर दिया गया है, लेकिन मैं अपना जवाब वैसे भी छोड़ दूंगा। मैंने एक साधारण कंसोल आवरण लागू किया है जो इस समस्या से ग्रस्त नहीं है। JQuery की आवश्यकता है।

यह केवल लागू करता है log, warnऔर errorविधियाँ, आपको एक नियमित रूप से विनिमेय होने के लिए कुछ और जोड़ना होगा 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.length] क्लोन [i] होना चाहिए।
एरिक मिकेलसन

कोई त्रुटि नहीं, मैंने इसे निष्पादित कर दिया है और यह ठीक था। क्लोन [clone.length] बिल्कुल क्लोन [i] की तरह है, जैसा कि सरणी 0 की लंबाई के साथ शुरू होती है, और इसलिए लूप इटेटर "i" करता है। वैसे भी, यह सुनिश्चित नहीं है कि यह जटिल वस्तुओं के साथ कैसे व्यवहार करेगा लेकिन IMO यह एक कोशिश के लायक है। जैसे मैंने कहा, यह कोई हल नहीं है, यह समस्या का एक तरीका है ..
शैडो विजार्ड इयर फॉर यू

@ शादो विजार्ड: शुभ बिंदु: क्लोन.लॉन्ग हमेशा मेरे बराबर होगा। यह वस्तुओं के लिए काम नहीं करेगा। शायद "प्रत्येक के लिए" के साथ एक समाधान है।
एरिक मिकेलसेन

वस्तुओं आप यह मतलब है? var s = {param1: "hi", param2: "आप कैसे हैं?" }; अगर ऐसा है तो मैंने अभी परीक्षण किया है और जब आपने ["param1"] = "बाय" किया है; यह उम्मीद के मुताबिक ठीक काम कर रहा है। क्या आप "वस्तुओं के लिए काम नहीं करेंगे" का उदाहरण पोस्ट कर सकते हैं? मैं देखूंगा और उस एक पर भी चढ़ने की कोशिश करूंगा।
शैडो विजार्ड इयर फॉर यू यू

@ शादो विज़ार्ड: जाहिर है, आपका फ़ंक्शन गुणों को क्लोन करने में विफल हो जाएगा और किसी भी संपत्ति पर एक लंबी संपत्ति के बिना काम नहीं करेगा। वेबकिट बग सभी वस्तुओं को प्रभावित करता है, न कि केवल सरणियाँ।
एरिक मिकेलसेन

0

अब तक का सबसे छोटा समाधान सरणी या ऑब्जेक्ट प्रसार सिंटैक्स का उपयोग करना है ताकि लॉगिंग के समय के रूप में संरक्षित किए जाने वाले मानों का एक क्लोन प्राप्त किया जा सके:

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

हालाँकि, इसे उथली प्रति के रूप में चेतावनी दी जाती है, इसलिए किसी भी गहरे नेस्टेड गैर-आदिम मूल्यों को क्लोन नहीं किया जाएगा और इस प्रकार कंसोल में उनकी संशोधित स्थिति में दिखाया गया है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.