जावास्क्रिप्ट में 2 == [2] क्यों है?


164

मुझे हाल ही में पता चला है कि 2 == [2]जावास्क्रिप्ट में। जैसा कि यह पता चला है, इस quirk के कुछ दिलचस्प परिणाम हैं:

var a = [0, 1, 2, 3];
a[[2]] === a[2]; // this is true

इसी तरह, निम्नलिखित काम करता है:

var a = { "abc" : 1 };
a[["abc"]] === a["abc"]; // this is also true

यहां तक ​​कि अभी भी अजनबी, यह भी काम करता है:

[[[[[[[2]]]]]]] == 2; // this is true too! WTF?

ये व्यवहार सभी ब्राउज़रों के अनुरूप हैं।

किसी भी विचार क्यों यह एक भाषा की विशेषता है?

यहाँ इस "सुविधा" के अधिक पागल परिणाम हैं:

[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!

var a = [0];
a == a // true
a == !a // also true, WTF?

ये उदाहरण jimbojw http://jimbojw.com प्रसिद्धि के साथ-साथ walkeyerobot द्वारा भी पाए गए ।

जवाबों:


134

आप ECMA-spec (ECMA-262 के प्रासंगिक अनुभाग, आपकी समस्या के लिए तीसरा संस्करण: 11.9.3, 9.1, 8.6.2.6) में तुलना एल्गोरिथ्म देख सकते हैं।

यदि आप शामिल किए गए सार एल्गोरिदम को वापस जेएस में अनुवाद करते हैं, तो मूल्यांकन करते समय क्या होता 2 == [2]है मूल रूप से यह:

2 === Number([2].valueOf().toString())

जहाँ valueOf()सरणियों के लिए सरणी स्वयं लौटती है और एक-तत्व सरणी का स्ट्रिंग-प्रतिनिधित्व एकल तत्व का स्ट्रिंग प्रतिनिधित्व है।

यह तीसरा उदाहरण भी बताता है क्योंकि [[[[[[[2]]]]]]].toString()अभी भी सिर्फ स्ट्रिंग है 2

जैसा कि आप देख सकते हैं, इसमें काफी पीछे का जादू शामिल है, यही वजह है कि मैं आमतौर पर केवल सख्त समानता ऑपरेटर का उपयोग करता हूं ===

पहले और दूसरे उदाहरण का अनुसरण करना आसान है क्योंकि संपत्ति के नाम हमेशा तार होते हैं, इसलिए

a[[2]]

के बराबर है

a[[2].toString()]

जो बस है

a["2"]

ध्यान रखें कि किसी भी एरे-मैजिक के होने से पहले भी संख्यात्मक कुंजियों को संपत्ति के नाम (यानी स्ट्रिंग्स) के रूप में माना जाता है।


10

इसका कारण ==ऑपरेटर के निहित प्रकार रूपांतरण है ।

[२] संख्या के साथ तुलना करने पर संख्या २ में बदल जाती है। +[2] पर अपर संचालक का प्रयास करें ।

> +[2]
2

अन्य कह रहे हैं [2] एक स्ट्रिंग में परिवर्तित कर दिया गया है। +"2"यह भी संख्या 2 है
dlamblin

1
दरअसल, यह इतना आसान नहीं है। [२] स्ट्रिंग में बदल दिया गया है, लेकिन करीब होगा, पर एक नज़र है ecma-international.org/ecma-262/5.1/#sec-11.9.3
neo

10
var a = [0, 1, 2, 3];
a[[2]] === a[2]; // this is true

समीकरण के दाईं ओर, हमारे पास एक [2] है, जो मान के साथ एक संख्या प्रकार लौटाता है। बाईं ओर, हम पहले एक एकल ऑब्जेक्ट के साथ एक नया सरणी बना रहे हैं। फिर हम एक [( सरणी यहाँ है]]। मुझे यकीन नहीं है कि यह एक स्ट्रिंग या संख्या का मूल्यांकन करता है। 2, या "2"। सबसे पहले स्ट्रिंग केस लेते हैं। मेरा मानना ​​है कि एक ["2"] एक नया चर पैदा करेगा और अशक्त होगा। null! == 2. तो चलो यह मान लेते हैं कि यह वास्तव में एक संख्या में परिवर्तित हो रहा है। एक [२] २ लौटेगा। २ और २ प्रकार के मेल (इसलिए === काम करता है) और मूल्य। मुझे लगता है कि यह संक्षेप में सरणी को एक संख्या में परिवर्तित कर रहा है क्योंकि [मान] एक स्ट्रिंग या संख्या की अपेक्षा करता है। ऐसा लगता है कि संख्या अधिक पूर्वता लेती है।

एक साइड नोट पर, मुझे आश्चर्य है कि उस पूर्वता को कौन निर्धारित करता है। क्या इसलिए कि [2] में एक नंबर है क्योंकि यह पहला आइटम है, इसलिए यह एक नंबर पर धर्मान्तरित होता है? या क्या यह है कि किसी सरणी को [सरणी] में पास करते समय यह सरणी को पहले संख्या में बदलने की कोशिश करता है, फिर स्ट्रिंग। कौन जानता है?

var a = { "abc" : 1 };
a[["abc"]] === a["abc"];

इस उदाहरण में, आप abc नामक एक सदस्य के साथ एक ऑब्जेक्ट बना रहे हैं। समीकरण का दाहिना भाग बहुत सरल है; यह a.abc के बराबर है। यह रिटर्न 1. बाईं ओर पहले ["एबीसी"] का शाब्दिक सरणी बनाता है। फिर आप नए बनाए गए सरणी में पास करके किसी ऑब्जेक्ट पर एक चर खोजते हैं। चूंकि यह एक स्ट्रिंग की अपेक्षा करता है, यह सरणी को स्ट्रिंग में परिवर्तित करता है। यह अब एक ["एबीसी"] का मूल्यांकन करता है, जो १. १ और १ के समान हैं (यही कारण है कि === काम करता है) और समान मूल्य।

[[[[[[[2]]]]]]] == 2; 

यह केवल एक अंतर्निहित रूपांतरण है। === इस स्थिति में काम नहीं करेगा क्योंकि एक प्रकार का बेमेल है।


पूर्वता के बारे में आपके प्रश्न का उत्तर: सरणी ==पर लागू होता ToPrimitive()है, जो बदले में इसकी toString()विधि को लागू करता है , इसलिए आप वास्तव में जो तुलना 2करते हैं वह स्ट्रिंग की संख्या है "2"; एक स्ट्रिंग और संख्या के बीच तुलना स्ट्रिंग को परिवर्तित करके की जाती है
क्रिस्टोफ़

8

इस ==मामले के लिए, यही कारण है कि डग क्रॉकफोर्ड हमेशा उपयोग करने की सलाह देते हैं ===। यह कोई निहित प्रकार रूपांतरण नहीं करता है।

के साथ उदाहरणों के लिए ===, समानता ऑपरेटर को कॉल करने से पहले निहित प्रकार रूपांतरण किया जाता है।


7
[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!

यह दिलचस्प है, ऐसा नहीं है कि [0] वास्तव में सच और झूठ दोनों है

[0] == true // false

यदि यह () ऑपरेटर है तो यह जावास्क्रिप्ट के प्रसंस्करण का मज़ेदार तरीका है।


4
वास्तव में, यह मज़ेदार तरीका ==काम करता है; यदि आप एक वास्तविक स्पष्ट डाली (यानी उपयोग करें Boolean([0])या !![0]), आप उस मिल जाएगा [0]करने के लिए मूल्यांकन करेंगे trueजे एस में, किसी भी वस्तु माना जाता है: एकदम सही ढंग से बूलियन संदर्भों मेंtrue
क्रिस्टोफ

6

एक आइटम की एक सरणी को आइटम के रूप में माना जा सकता है।

यह बतख टाइपिंग के कारण है। चूंकि "2" == 2 == [2] और संभवतः अधिक।


4
क्योंकि वे टाइप में मेल नहीं खाते हैं। पहले उदाहरण में, बाईं ओर पहले का मूल्यांकन किया जाता है, और वे प्रकार में मिलान को हवा देते हैं।
श्वेना

8
इसके अलावा, मुझे नहीं लगता कि बतख-टाइपिंग यहां सही शब्द है। ==तुलना करने से पहले ऑपरेटर द्वारा किए गए निहित प्रकार रूपांतरण के साथ यह करना अधिक है ।
चेतन एस

14
इसका डक टाइपिंग से कोई लेना-देना नहीं है, बल्कि कमजोर टाइपिंग के साथ इसका मतलब है कि निहितार्थ रूपांतरण
क्रिस्टोफ़

@ चेतन: उसने क्या कहा;)
क्रिस्टोफ़

2
चेतन और क्रिस्टोफ ने क्या कहा।
टिम डाउन

3

अन्य उत्तरों में थोड़ा विवरण जोड़ने के लिए ... Arraya की तुलना करते समय Number, जावास्क्रिप्ट के Arrayसाथ परिवर्तित हो जाएगा parseFloat(array)। आप इसे कंसोल में स्वयं आज़मा सकते हैं (जैसे फायरबग या वेब इंस्पेक्टर) यह देखने के लिए कि विभिन्न Arrayमूल्यों को किस रूप में परिवर्तित किया जाता है।

parseFloat([2]); // 2
parseFloat([2, 3]); // 2
parseFloat(['', 2]); // NaN

के लिए Arrayहै, parseFloatपर कार्रवाई निष्पादित Arrayके पहले सदस्य, और छोड बाकी।

संपादित करें: क्रिस्टोफ के विवरण के अनुसार, यह हो सकता है कि यह आंतरिक रूप से लंबे समय तक फॉर्म का उपयोग कर रहा है, लेकिन परिणाम लगातार समान हैं parseFloat, इसलिए आप हमेशा parseFloat(array)शॉर्टहैंड के रूप में उपयोग कर सकते हैं यह जानने के लिए कि यह कैसे रूपांतरित होगा।


2

आप हर मामले में 2 वस्तुओं की तुलना कर रहे हैं .. न ही उपयोग ==, यदि आप तुलना करने की सोच रहे हैं, तो आप === को ध्यान में रख रहे हैं और == नहीं। == अक्सर पागल प्रभाव दे सकते हैं। भाषा में अच्छे हिस्सों की तलाश करें :)


0

प्रश्न के EDIT अनुभाग के लिए स्पष्टीकरण :

पहला उदाहरण

[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!

क्रिस्टोफ के उत्तर के अनुसार पहले टाइपकास्ट [0] एक आदिम मान के लिए, हमारे पास "0" ( [0].valueOf().toString()) है

"0" == false

अब, नंबर और फिर स्ट्रिंग ("0") से नंबर तक बूलियन (गलत) टाइप करें

Number("0") == Number(false)
or  0 == 0 
so, [0] == false  // true

ifकथन के अनुसार, यदि स्वयं की स्थिति में स्पष्ट तुलना नहीं है, तो स्थिति सत्य मूल्यों के लिए मूल्यांकन करती है।

केवल 6 झूठे मूल्य हैं : गलत, अशक्त, अपरिभाषित, 0, NaN और खाली स्ट्रिंग ""। और जो कुछ भी मिथ्या मूल्य नहीं है वह एक सत्य मूल्य है।

चूंकि [0] कोई मिथ्या मूल्य नहीं है, यह एक ifसत्य मूल्य है, कथन कथन का सही मूल्यांकन करता है और निष्पादित करता है।


2 उदाहरण

var a = [0];
a == a // true
a == !a // also true, WTF?

मानों को पुन: टाइप करने के लिए आदिम,

    a = a
or  [0].valueOf().toString() == [0].valueOf().toString()
or  "0" == "0" // true; same type, same value


a == !a
or  [0].valueOf().toString() == [0].valueOf().toString()
or  "0" == !"0"
or  "0" == false
or  Number("0") == Number(false)
or  0 = 0   // true
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.