2013-03-13 12 views
6

मैं वैश्विक गुंजाइश और जावास्क्रिप्ट चर और उनके सामान्य undesirability साथ मुद्दों को समझने; और आप उन्हें हर जगह पाते हैं। (एक ब्राउज़र में) के बाद बराबर है: अपने चर जड़ को सौंपा गया है (:जावास्क्रिप्ट दायरा पूरी असाइनमेंट

var foo = 3; // foo === 3, window.foo === 3 
bazz = 10; // bazz === 10, window.bazz === 10 

वैश्विक क्षेत्र में वर कीवर्ड के साथ एक चर घोषणा कोड में कहीं भी एक वर के बिना यह घोषित करने के समान ही है खिड़की) वस्तु।

var _gaq = _gaq || []; 

... और मुझे तर्क का पालन करें कि _gaq उपयोग घोषित किया गया है, तो है कि, अगर नहीं एक के रूप में बनाना:

एक तकनीक मैं एक बहुत देखते हैं (Google Analytics सेट अप जैसे) यह है सरणी। यह लापरवाही कोडिंग को ग्लोबल वैरिएबल _gaq को पहले से निर्दिष्ट किसी भी मान को ओवरराइट नहीं करने की अनुमति देता है।

_gaq = _gaq || []; 

उन्होंने मुझे के बराबर लग रही:

क्या मुझे समझ नहीं आता क्यों कि यह एक त्रुटि फेंकता है _gaq _gaq का मूल्य लेना चाहिए या एक सरणी के रूप में initialised किया। लेकिन यह एक संदर्भ त्रुटि फेंकता है - मेरा सवाल है: वे अलग क्यों हैं?

+1

वे बिल्कुल समान नहीं हैं, जब आप ['delete'] (http://perfectionkills.com/understanding-delete/) को करने का प्रयास करते हैं तो वे व्यवहार में भिन्न होते हैं। – Bergi

+0

वैश्विक स्तर पर 'असाइन किए गए असाइनमेंट' का सर्वोत्तम स्पष्टीकरण और क्यों वे वैश्विक असाइनमेंट ('foo = 0'! =' Var foo = 0') के लिए अलग हैं) मैं यह देख सकता हूं ** [इस ब्लॉग पर] (http : //perfectionkills.com/understanding-delete/) ** जिसने मुझे आंतरिक अंतर को समझने में मदद की है। –

उत्तर

6

आप कभी भी पढ़ सकते हैं वेरिएबल जिन्हें घोषित नहीं किया गया है, और यही वह है जो आप पिछले मामले में _gaq || [] अभिव्यक्ति के साथ प्रयास कर रहे हैं।

इस मामले

_gaq = _gaq || []; 

_qaq में से पहले घोषित नहीं किया गया है और जब दाहिने हाथ की ओर (_gaq || []) का मूल्यांकन किया जाता है, यह त्रुटि फेंकता है।

यहाँ क्या इस मामले में हो रहा है के कदम विवरण द्वारा कदम है:

असाइनमेंट ऑपरेटर विनिर्देश के section 11.13.1 में वर्णित है:

The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:

1. Let lref be the result of evaluating LeftHandSideExpression .
2. Let rref be the result of evaluating AssignmentExpression .
...

LeftHandSideExpression_gaq है, AssignmentExpression_gqa || [] है।

तो पहले _qaq का मूल्यांकन किया गया है, जिसके परिणामस्वरूप unresolvable reference है, क्योंकि परिवर्तनीय _gaq घोषित नहीं किया गया है। यह मूल्यांकन त्रुटि नहीं फेंकता है।

फिर _gqa || [] का मूल्यांकन किया गया है। यह एक LogicalORExpression है और LogicalORExpression || LogicalANDExpression रूप section 11.11 में वर्णित है।इस मामले में, LogicalORExpression, बाएं हाथ की ओर, _gaq और LogicalANDExpression है, दाईं ओर, [] है।

1. Let lref be the result of evaluating LogicalORExpression .
2. Let lval be GetValue(lref) .
...

हम पहले से ही जानते हैं कि lref एक न सुलझा हुआ संदर्भ क्योंकि _gaq घोषित नहीं किया गया था हो जाएगा:
अभिव्यक्ति के रूप में इस मूल्यांकन किया जाता है। तो देता है एक नज़र क्या GetValue कर रही है है (section 8.7.1 में परिभाषित किया गया, V मूल्य GetValue के लिए पारित किया है):

1. If Type(V) is not Reference , return V .
2. Let base be the result of calling GetBase(V) .
3. If IsUnresolvableReference(V) , throw a ReferenceError exception.
...

आप देख सकते हैं, एक ReferenceError त्रुटि इस प्रक्रिया के तीसरे चरण में फेंक दिया जाता है, जिसमें असाइनमेंट के दाईं ओर मूल्यांकन करके निष्पादित किया जाता है, और यह वह जगह है जहां त्रुटि फेंक दी जाती है।

तो, यह var _gaq = _gaq || []; के साथ क्यों नहीं होता है?

यह पंक्ति:

var _gaq = _gaq || []; 

वास्तव में

var _gaq; 
_gaq = _gaq || []; 
कुछ hoisting [MDN] कहा जाता है की वजह से

है। इसका मतलब है कि _gaq का मूल्यांकन किया जाता है, तो यह परिणामस्वरूप अनारक्षित संदर्भ, लेकिन मूल्य undefined के साथ संदर्भ है।

(यदि चर _gaq पहले से ही घोषित किया जाता है (और संभवत: अपना महत्व होता है), तो var _gaq कोई असर नहीं होगा।)


आप एक समारोह अंदर से विश्व स्तर पर _gaq बनाने के लिए चाहते हैं, करते हैं

window._gaq = window._gaq || []; 
+0

यह सही दिखता है! "आप उन चरों को कभी नहीं पढ़ सकते हैं जिन्हें घोषित नहीं किया गया है", मैं इस कथन से सहमत नहीं हूं। उदाहरण के लिए 'window.x' ठीक पढ़ता है और' विंडो 'में परिवर्तनीय 'x' घोषित नहीं किया गया है (मान लीजिए कि हमने' window.x' को परिभाषित नहीं किया है) –

+0

@ बेंजामिन: लेकिन में इस मामले में, 'window.x'' विंडो 'ऑब्जेक्ट की एक संपत्ति है, एक चर नहीं। और यद्यपि वैश्विक चर वैश्विक वस्तु के गुण हैं, लेकिन यह नहीं है कि आप 'विंडो' ऑब्जेक्ट तक पहुंचते हैं, जब आप वैश्विक चर "अंतर्निहित" संदर्भित करते हैं, तो 'विंडो' के गुणों को केवल कार्य स्कोप श्रृंखला में रखा जाता है। –

+0

धन्यवाद जो इसे बहुत स्पष्ट बनाता है। हालांकि, यह सवाल सुझाता है: जब कोई 'foo = 10' लिखता है तो क्या 'जादू' होता है? यदि जावास्क्रिप्ट इंजन एक अंतर्निहित 'var foo' बनाता है या बनाता है बयान, तो यह मेरे उदाहरण के लिए ऐसा क्यों नहीं करता है? –

5

= के दाईं ओर var के साथ घोषित नहीं किया गया था, तो यह एक संदर्भ त्रुटि फेंक देगा। आप एक चर का संदर्भ देने की कोशिश कर रहे हैं जो मौजूद नहीं है। "जादू" बस गैर-मौजूद चर के बताए के लिए काम करता है।

यह सिर्फ x = y + 1 कह की तरह है; समस्या गैर मौजूदा x नहीं है, लेकिन गैर मौजूदा y

+0

धन्यवाद। इसलिए मैं समझता हूं कि इंजन चर और त्रुटियों के मान को काम करने का प्रयास करता है क्योंकि इसे घोषित नहीं किया गया है। यह समझ आता है। जहां मुझे यह गलत था कि ग्लोबल्स के लिए 'var' को छोड़ना उन्हें घोषित करने का एक लघुरूप/वैकल्पिक तरीका नहीं है, भले ही यह अधिकतर दिखता है। –

+1

@ भाग: नहीं, 'var' को छोड़कर शॉर्टेंड के रूप में ठीक है। समस्या एक अविकसित चर के लिए नहीं लिख रही है, यह एक पढ़ रहा है। जैसा कि इस उत्तर में उदाहरण में x x = y + 1'। 'y' वैश्विक बन जाएगा यदि' y' घोषित किया गया था, लेकिन ऐसा नहीं है क्योंकि यह एक त्रुटि फेंक देगा कि 'y' घोषित नहीं किया गया है। दाएं हाथ की तरफ पहले मूल्यांकन किया जाता है, इसलिए 'x = x || के मामले में 42; 'आप इसे बनाने से पहले' x' पढ़ने की कोशिश कर रहे हैं। –

+0

ऊपर देखें, लेकिन मुझे लगता है कि यह समझना वाकई महत्वपूर्ण है कि उछाल केवल तभी होता है जब आप वैश्विक स्कोप_ में var _even लिखते हैं, और इसलिए 'var x = x || 0' काम करता है लेकिन 'x = x || 0' doesn' टी। आप 'स्पष्ट रूप से' कह सकते हैं लेकिन जब तक आप इस फैशन में 'शॉर्ट सर्किटिंग' नहीं कर रहे हैं, तो यह _does_ प्रकट होता है कि एक वैश्विक var सिर्फ क्रूर है। इसलिए मेरा प्रारंभिक भ्रम। उत्तरों के लिए धन्यवाद। –

1

यह एक त्रुटि फेंक होगा, क्योंकि चर वर्तमान कार्यकारी के संदर्भ श्रृंखला में नहीं मिला है: यह स्पष्ट रूप सेwindow का हवाला देते हुए ution संदर्भ। एक वेरिएबल को एक्सेस करना जिसे हल नहीं किया जा सकता है, परिणामस्वरूप एक त्रुटि होगी।

_gaq = _gaq || []; 

यह, दूसरे हाथ पर, खिड़की वस्तु के एक सदस्य के रूप में यह देखने के लिए कोशिश कर रहा _gac को हल करने की कोशिश करेंगे, जो पता चला है वैश्विक संदर्भ 'होल्डर' वस्तु माना जाता है। इस मामले में अंतर यह है कि यह कोई त्रुटि नहीं फेंक देगा, लेकिन window._gaq अपरिभाषित वापस आ जाएगा, क्योंकि संपत्ति विंडो ऑब्जेक्ट में नहीं मिली है।

_gaq = window._gaq || []; 

तो, के बाद से वैश्विक संदर्भ वस्तु खिड़की है (जब ब्राउज़र के बारे में बात कर रहा), अगर _gaq परिभाषित किया गया है, इस दो कथनों में एक ही प्रभाव पड़ेगा। अंतर तब देखा जाएगा जब _gaq परिभाषित नहीं किया गया है, और विंडो ऑब्जेक्ट का उपयोग करके इसे एक्सेस करने से त्रुटि प्राप्त करने का लाभ हो सकता है।

1

यहां अंतर्निहित अवधारणा hoisting है और यह अक्सर अभ्यास में मुश्किल होती है। वैरिएबल को फ़ंक्शन के दायरे के शीर्ष पर परिभाषित किया जाता है जबकि असाइनमेंट अभी भी होता है जहां इसे परिभाषित किया जाता है।

इस var _gaq = _gaq के साथ वैरिएबल वास्तव में परिभाषित किया गया है असाइनमेंट के लिए कोड की वास्तविक पंक्ति निष्पादित की जाती है। इसका अर्थ यह है कि जब असाइनमेंट होता है तो वेरिएबल पहले से ही विंडो स्कोप पर होता है। _gaq के सामने var के बिना कोई hoisting होता है और इस प्रकार _gaq अभी तक मौजूद नहीं है जब असाइनमेंट संदर्भ त्रुटि उत्पन्न करता है।

आप कार्रवाई में यह देखने के लिए आप जाँच कर सकते हैं जब _gaq चर के साथ विंडो ऑब्जेक्ट में जोड़ा जाता है चाहते हैं निम्नलिखित:

function printIsPropOnWindow(propToCheck) 
{ 
    for (prop in window) 
    { 
     if (prop == propToCheck) 
     { 
      console.warn('YES, prop ' + prop + ' was on the window object'); 
      return; 
     } 
    } 
    console.warn('NO, prop ' + propToCheck + ' was NOT on the window object'); 
} 


try { 
    var _gaq = function() { 
     printIsPropOnWindow("_gaq"); 
     return a; 
    }(); 
} catch (ex) { 
    printIsPropOnWindow("_gaq"); 
} 
_gaq = "1"; 
printIsPropOnWindow("_gaq"); 

आप _gaq से पहले वर के साथ एक बार यह कोशिश है के रूप में और एक बार तो हटा दिया गया आप बहुत अलग परिणाम देखेंगे क्योंकि किसी के पास _gaq hoisted है और दूसरा नहीं है।

+0

धन्यवाद। मुझे लगता है कि मेरी मौलिक गलतफहमी यह है कि 'foo = true' लिखना' var foo = true' लिखने जैसा ही है, और पहले मामले में 'var' अंतर्निहित था। निष्पक्ष होने के लिए, मुझे लगता है कि यह काफी आम गलतफहमी है, क्योंकि ज्यादातर उद्देश्यों के लिए वे वही हैं। जैसा कि बर्गि बताते हैं, पहला खिड़की की संपत्ति बन जाता है ... और दूसरा दूसरा करता है। सिवाय इसके कि यह वास्तव में नहीं है: इसे हटाना असफल हो जाएगा। कुछ सूक्ष्म तरीके से वे अलग हैं। –

+1

@ पार्टी: * भिन्न अंतर में 'var foo = ...' और 'foo = ...' के बीच * अंतर * आपकी समस्या से कोई लेना देना नहीं है। केवल तथ्य यह है कि परिवर्तनीय घोषणाओं को फहराया जाता है। –

+0

@ पार्टी आर्क: फ़ेलिक्स क्लिंग सही है। घुमावदार परिवर्तन * जब * विंडो वस्तु में foo जोड़ा जाता है। और आपकी समस्या यह है कि आप खिड़की के दायरे में जोड़े जाने से पहले उस चर को असाइन करने का प्रयास कर रहे हैं ताकि लाइन त्रुटि के साथ विफल हो। यदि आपके पास var है तो यह फिसल गया है-> जिसका अर्थ है कि यह विंडो ऑब्जेक्ट में जो भी गुंजाइश है, उसकी शुरुआत में ही जोड़ा जाता है। – purgatory101

संबंधित मुद्दे