यह मान्य है और स्ट्रिंग "10"
को जावास्क्रिप्ट में लौटाता है ( अधिक उदाहरण यहां ):
console.log(++[[]][+[]]+[+[]])
क्यों? यहां क्या हो रहा है?
यह मान्य है और स्ट्रिंग "10"
को जावास्क्रिप्ट में लौटाता है ( अधिक उदाहरण यहां ):
console.log(++[[]][+[]]+[+[]])
क्यों? यहां क्या हो रहा है?
जवाबों:
यदि हम इसे विभाजित करते हैं, तो गड़बड़ इसके बराबर होती है:
++[[]][+[]]
+
[+[]]
जावास्क्रिप्ट में, यह सच है कि +[] === 0
। +
किसी चीज़ को संख्या में परिवर्तित करता है, और इस स्थिति में यह नीचे आएगा +""
या 0
(नीचे विनिर्देश विवरण देखें)।
इसलिए, हम इसे सरल बना सकते हैं (इस पर ++
पहले से निर्भरता है +
):
++[[]][0]
+
[0]
क्योंकि [[]][0]
इसका अर्थ है: पहले तत्व को प्राप्त करें [[]]
, यह सच है कि:
[[]][0]
आंतरिक सरणी ( []
) लौटाता है । संदर्भों के कारण यह कहना गलत है [[]][0] === []
, लेकिन चलो A
गलत नोटेशन से बचने के लिए आंतरिक सरणी को कॉल करें ।
++
इससे पहले कि इसके ऑपरेंड का अर्थ है "एक के बाद एक वेतन वृद्धि और बढ़ा हुआ परिणाम लौटाएं"। तो (या ) के ++[[]][0]
बराबर है ।Number(A) + 1
+A + 1
फिर, हम गंदगी को और अधिक सुपाच्य बनाने में मदद कर सकते हैं। के []
लिए वापस स्थानापन्न करें A
:
(+[] + 1)
+
[0]
+[]
सरणी को संख्या में समेटने से पहले 0
, इसे पहले एक स्ट्रिंग में ले जाने की आवश्यकता होती है, जो कि ""
फिर से है। अंत में, 1
जोड़ा जाता है, जिसके परिणामस्वरूप 1
।
(+[] + 1) === (+"" + 1)
(+"" + 1) === (0 + 1)
(0 + 1) === 1
आइए इसे और भी सरल करें:
1
+
[0]
इसके अलावा, यह जावास्क्रिप्ट में सच है: [0] == "0"
क्योंकि यह एक तत्व के साथ एक सरणी में शामिल हो रहा है। जुड़ने से अलग हुए तत्वों का संघटन होगा ,
। एक तत्व के साथ, आप यह तर्क दे सकते हैं कि इस तर्क का परिणाम पहले तत्व में ही होगा।
इस मामले में, +
दो ऑपरेंड देखता है: एक संख्या और एक सरणी। अब यह दोनों को एक ही प्रकार में ले जाने की कोशिश कर रहा है। सबसे पहले, सरणी को स्ट्रिंग में ले जाया जाता है "0"
, अगले, संख्या को एक स्ट्रिंग ( "1"
) में coerced किया जाता है । नंबर +
स्ट्रिंग ===
स्ट्रिंग ।
"1" + "0" === "10" // Yay!
के लिए विशिष्टता विवरण +[]
:
यह काफी भूलभुलैया है, लेकिन ऐसा करने के लिए +[]
, पहले इसे स्ट्रिंग में परिवर्तित किया जा रहा है क्योंकि यही +
कहता है:
11.4.6 यूनरी + संचालक
Unary + ऑपरेटर अपने ऑपरेंड को संख्या प्रकार में परिवर्तित करता है।
उत्पादन UnaryExpression: + UnaryExpression का मूल्यांकन निम्नानुसार किया जाता है:
UnrExpression के मूल्यांकन का परिणाम होने दें।
ToNumber (GetValue (expr)) पर लौटें।
ToNumber()
कहते हैं:
वस्तु
निम्नलिखित चरण लागू करें:
PrimValue को ToPrimitive (इनपुट तर्क, संकेत स्ट्रिंग) होने दें।
ToString (primValue) पर लौटें।
ToPrimitive()
कहते हैं:
वस्तु
ऑब्जेक्ट के लिए एक डिफ़ॉल्ट मान लौटाएं। किसी वस्तु का डिफ़ॉल्ट मान [[DefaultValue]] आंतरिक विधि को कॉल करके प्राप्त किया जाता है, जो वैकल्पिक संकेत PreferredType से गुजरता है। [[DefaultValue]] आंतरिक पद्धति के व्यवहार को इस विनिर्देश द्वारा परिभाषित किया गया है जो M.१२. 8.1 में सभी देशी ECMAScript वस्तुओं के लिए है।
[[DefaultValue]]
कहते हैं:
8.12.8 [[DefaultValue]] (संकेत)
जब [[DefaultValue]] O का आंतरिक तरीका संकेत स्ट्रिंग के साथ कहा जाता है, तो निम्नलिखित कदम उठाए जाते हैं:
आज्ञा देना पुख्ता करने का परिणाम है [[जाओ]] तर्क के साथ ऑब्जेक्ट O की आंतरिक विधि "स्ट्रींग"।
यदि IsCallable (inString) सत्य है, तो
ए। चलो [[कॉल]] toString की आंतरिक विधि को कॉल करने का परिणाम है, ओ के साथ यह मान और एक खाली तर्क सूची है।
ख। यदि str एक आदिम मान है, तो str वापस लौटें।
.toString
एक सरणी के का कहना है:
15.4.4.2 Array.prototype.toString ()
जब स्ट्रिंग विधि को कहा जाता है, तो निम्नलिखित कदम उठाए जाते हैं:
इस मान पर ToObject को कॉल करने का परिणाम होने दें।
चलिए फंक को [[शामिल हों] तर्क के साथ सरणी की आंतरिक विधि "जॉइन" कहते हैं।
यदि IsCallable (func) गलत है, तो फंक को मानक निर्मित विधि Object.prototyp.toString (15.2.4.2) होने दें।
कॉल करने के परिणाम को लौटाएँ [[कॉल]] इस मूल्य और खाली तर्क सूची के रूप में एफ़सीआर प्रदान करने की आंतरिक विधि।
तो +[]
नीचे आता है +""
, क्योंकि [].join() === ""
।
फिर से, इस +
रूप में परिभाषित किया गया है:
11.4.6 यूनरी + संचालक
Unary + ऑपरेटर अपने ऑपरेंड को संख्या प्रकार में परिवर्तित करता है।
उत्पादन UnaryExpression: + UnaryExpression का मूल्यांकन निम्नानुसार किया जाता है:
UnrExpression के मूल्यांकन का परिणाम होने दें।
ToNumber (GetValue (expr)) पर लौटें।
ToNumber
के रूप में परिभाषित किया ""
गया है:
StringNumericLiteral का एमवी ::: [खाली] 0 है।
तो +"" === 0
, और इस प्रकार +[] === 0
।
true
जब मूल्य और प्रकार दोनों समान हों। 0 == ""
रिटर्न true
(एक ही प्रकार के रूपांतरण के बाद), लेकिन 0 === ""
यह false
(समान प्रकार नहीं)।
1 + [0]
, नहीं "1" + [0]
, क्योंकि उपसर्ग ( ++
) ऑपरेटर हमेशा एक नंबर देता है। Bclary.com/2004/11/07/#a-11.4.4
++[[]][0]
वास्तव में देता है 1
, लेकिन ++[]
एक त्रुटि फेंकता है। यह उल्लेखनीय है क्योंकि ऐसा लगता है कि ++[[]][0]
नीचे उबाल आता है ++[]
। क्या आपको शायद इस बात का कोई अंदाजा है कि ++[]
कोई त्रुटि क्यों होती ++[[]][0]
है जबकि ऐसा नहीं है?
PutValue
है कि प्रीफ़िक्स ऑपरेशन में कॉल (ES3 शब्दावली में, 8.7.2) में समस्या है । PutValue
एक संदर्भ की आवश्यकता है जबकि []
एक अभिव्यक्ति अपने आप में एक संदर्भ का उत्पादन नहीं करती है। एक चर संदर्भ युक्त एक अभिव्यक्ति (कहते हैं कि हम पहले परिभाषित किया गया था var a = []
तो ++a
काम करता है) या एक वस्तु (जैसे [[]][0]
) की संपत्ति का उपयोग एक संदर्भ पैदा करता है। सरल शब्दों में, उपसर्ग ऑपरेटर न केवल एक मूल्य पैदा करता है, उसे उस मूल्य को डालने के लिए कहीं न कहीं आवश्यकता भी होती है।
var a = []; ++a
, a
1. निष्पादित करने के बाद ++[[]][0]
, [[]]
एक्सप्रेशन द्वारा बनाई गई सरणी में अब इंडेक्स 0. में सिर्फ नंबर 1 होता है, ++
ऐसा करने के लिए एक संदर्भ की आवश्यकता होती है।
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]
फिर हमारे पास एक स्ट्रिंग समवर्ती है
1+[0].toString() = 10
===
बजाय लिखने के लिए स्पष्ट नहीं होगा =>
?
निम्नलिखित इस प्रश्न का उत्तर देने वाले ब्लॉग पोस्ट से अनुकूलित है जिसे मैंने पोस्ट किया था जबकि यह प्रश्न अभी भी बंद था। लिंक ECMAScript 3 युक्ति के (HTML प्रति) हैं, फिर भी आज के आमतौर पर उपयोग किए जाने वाले वेब ब्राउज़रों में जावास्क्रिप्ट के लिए आधार रेखा है।
सबसे पहले, एक टिप्पणी: इस तरह की अभिव्यक्ति किसी भी (साने) उत्पादन वातावरण में कभी नहीं दिखाई देने वाली है और यह केवल एक अभ्यास के रूप में किसी भी उपयोग के लिए है कि पाठक कितनी अच्छी तरह से जावास्क्रिप्ट के गंदे किनारों को जानता है। सामान्य सिद्धांत जो जावास्क्रिप्ट ऑपरेटरों को स्पष्ट रूप से प्रकारों के बीच परिवर्तित करते हैं, उपयोगी है, क्योंकि कुछ सामान्य रूपांतरण हैं, लेकिन इस मामले में बहुत विस्तार नहीं है।
अभिव्यक्ति ++[[]][+[]]+[+[]]
शुरू में थोपने और अस्पष्ट लग सकती है, लेकिन वास्तव में अलग-अलग अभिव्यक्तियों में अपेक्षाकृत आसान है। नीचे मैंने स्पष्टता के लिए केवल कोष्ठक जोड़ा है; मैं आपको विश्वास दिलाता हूं कि वे कुछ भी नहीं बदल सकते हैं, लेकिन अगर आप यह सत्यापित करना चाहते हैं कि तब समूह ऑपरेटर के बारे में पढ़ने के लिए स्वतंत्र महसूस करें । इसलिए, अभिव्यक्ति को अधिक स्पष्ट रूप से लिखा जा सकता है
( ++[[]][+[]] ) + ( [+[]] )
इसे तोड़कर, हम इसका +[]
मूल्यांकन करके इसे सरल बना सकते हैं 0
। अपने आप को संतुष्ट करने के लिए कि यह सच क्यों है, एकरी + संचालक की जाँच करें और थोड़े यातना भरे मार्ग का अनुसरण करें जो कि समाप्त होने वाले तमोप्रधान को खाली स्ट्रिंग में परिवर्तित कर देता है, जिसे बाद में ToNumber0
द्वारा बदल दिया जाता है । अब हम प्रत्येक उदाहरण के लिए स्थानापन्न कर सकते हैं :0
+[]
( ++[[]][0] ) + [0]
पहले से ही सरल। जैसा ++[[]][0]
कि, उपसर्ग वृद्धि वेतन संचालक ( ++
) का एक संयोजन है , जो एकल तत्व के साथ एक सरणी को परिभाषित करने वाला एक सरणी शाब्दिक है जो स्वयं एक खाली सरणी है ( [[]]
) और एक संपत्ति अभिगमक ( [0]
) सरणी शाब्दिक द्वारा परिभाषित सरणी पर कहा जाता है।
तो, हम [[]][0]
बस []
और हम ++[]
, सही कर सकते हैं? वास्तव में, यह मामला नहीं है क्योंकि मूल्यांकन ++[]
एक त्रुटि फेंकता है, जो शुरू में भ्रामक लग सकता है। हालाँकि, इस बारे में थोड़ा ++
स्पष्ट हो जाता है कि प्रकृति स्पष्ट है: इसका उपयोग एक चर (जैसे ++i
) या एक वस्तु संपत्ति (जैसे ++obj.count
) बढ़ाने के लिए किया जाता है । न केवल यह एक मूल्य का मूल्यांकन करता है, यह कहीं न कहीं उस मूल्य को संग्रहीत भी करता है। के मामले में ++[]
, यह नए मूल्य (जो भी हो सकता है) को रखने के लिए कहीं नहीं है क्योंकि अद्यतन करने के लिए एक वस्तु संपत्ति या चर का कोई संदर्भ नहीं है। विशिष्ट शब्दों में, यह आंतरिक पुटवेल ऑपरेशन द्वारा कवर किया गया है , जिसे उपसर्ग वृद्धि वेतन संचालक द्वारा कहा जाता है।
तो फिर, क्या करता ++[[]][0]
है? खैर, इसी तरह के तर्क से +[]
, आंतरिक सरणी में परिवर्तित हो जाता है 0
और यह मान 1
हमें अंतिम मूल्य देने के लिए बढ़ जाता है 1
। 0
बाहरी सरणी में गुण का मान अद्यतन किया जाता है 1
और संपूर्ण अभिव्यक्ति इसका मूल्यांकन करती है 1
।
यह हमें छोड़ देता है
1 + [0]
... जो अतिरिक्त ऑपरेटर का एक सरल उपयोग है । दोनों ऑपरेंड को पहले आदिम में परिवर्तित किया जाता है और यदि या तो आदिम मान एक स्ट्रिंग है, तो स्ट्रिंग संयोजन का प्रदर्शन किया जाता है, अन्यथा संख्यात्मक जोड़ किया जाता है। [0]
धर्मान्तरित "0"
, इसलिए स्ट्रिंग संघनन का उपयोग किया जाता है, उत्पादन "10"
।
एक अंतिम एक तरफ के रूप में, कुछ है कि तुरंत स्पष्ट नहीं हो सकता है कि दोनों में से किसी एक अधिभावी है toString()
या valueOf()
करने के तरीकों Array.prototype
अभिव्यक्ति का परिणाम बदल जाएगा, क्योंकि दोनों की जाँच की और जब एक आदिम मूल्य में एक वस्तु परिवर्तित मौजूद होने पर किया जाता है। उदाहरण के लिए, निम्नलिखित
Array.prototype.toString = function() {
return "foo";
};
++[[]][+[]]+[+[]]
... का उत्पादन करता है "NaNfoo"
। ऐसा क्यों होता है पाठक के लिए एक अभ्यास के रूप में छोड़ दिया जाता है ...
आइए इसे सरल बनाएं:
++[[]][+[]]+[+[]] = "10"
var a = [[]][+[]];
var b = [+[]];
// so a == [] and b == [0]
++a;
// then a == 1 and b is still that array [0]
// when you sum the var a and an array, it will sum b as a string just like that:
1 + "0" = "10"
यह एक ही लेकिन थोड़ा छोटा मूल्यांकन करता है
+!![]+''+(+[])
इसलिए इसका मूल्यांकन किया जाता है
+(true) + '' + (0)
1 + '' + 0
"10"
तो अब तुम मिल गया है, यह एक कोशिश:
_=$=+[],++_+''+$
"10"
+ [] ० का मूल्यांकन करता है ... [...] फिर समन (+ ऑपरेशन) कुछ भी इसके साथ सरणी सामग्री को उसके स्ट्रिंग प्रतिनिधित्व में परिवर्तित करता है जिसमें अल्पविराम से जुड़े तत्व शामिल होते हैं।
ऐरे के इंडेक्स लेने जैसे कुछ भी (ऑपरेशन की तुलना में ग्रेटर प्राथमिकता है) क्रमिक है और कुछ भी दिलचस्प नहीं है।
शायद अंकों के बिना "10" में किसी अभिव्यक्ति का मूल्यांकन करने के सबसे कम संभव तरीके हैं:
+!+[] + [+[]]
// "10"
-~[] + [+[]]
// "10"
// ========== स्पष्टीकरण ========== \\
+!+[]
: +[]
0. में !0
रूपांतरित करता है true
। +true
1. में परिवर्तित
होता है -~[]
= -(-1)
जो 1 है
[+[]]
: +[]
0. [0]
में कनवर्ट करता है 0 एक एकल तत्व 0 के साथ एक सरणी है।
तब JS 1 + [0]
इस प्रकार Number + Array
अभिव्यक्ति का मूल्यांकन करता है । फिर ECMA विनिर्देश काम करता है: +
ऑपरेटर toString()/valueOf()
आधार Object
प्रोटोटाइप से कार्यों को कॉल करके दोनों ऑपरेंड को एक स्ट्रिंग में परिवर्तित करता है । यह एक योज्य फ़ंक्शन के रूप में कार्य करता है यदि किसी अभिव्यक्ति के दोनों ऑपरेंड केवल संख्याएं हैं। चाल यह है कि सरणियाँ आसानी से अपने तत्वों को एक संक्षिप्त स्ट्रिंग प्रतिनिधित्व में बदल देती हैं।
कुछ उदाहरण:
1 + {} // "1[object Object]"
1 + [] // "1"
1 + new Date() // "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"
एक अच्छा अपवाद है कि दो Objects
अतिरिक्त परिणाम NaN
:
[] + [] // ""
[1] + [2] // "12"
{} + {} // NaN
{a:1} + {b:2} // NaN
[1, {}] + [2, {}] // "1,[object Object]2,[object Object]"
+ '' या + [] 0 का मूल्यांकन करता है।
++[[]][+[]]+[+[]] = 10
++[''][0] + [0] : First part is gives zeroth element of the array which is empty string
1+0
10
[]
के बराबर नहीं है ""
। पहले तत्व को निकाला जाता है, फिर उसके द्वारा परिवर्तित किया जाता है ++
।
उस के कदम से कदम, +
एक संख्या के लिए मूल्य और यदि आप एक खाली सरणी में जोड़ते हैं +[]
... जैसा कि यह खाली है और इसके बराबर है 0
, यह होगा
तो वहाँ से, अब अपने कोड में देखो, यह है ++[[]][+[]]+[+[]]
...
और उनके बीच प्लस ++[[]][+[]]
+ है[+[]]
तो ये [+[]]
वापस आ जाएंगे [0]
क्योंकि उनके पास एक खाली सरणी है जो 0
दूसरे सरणी के अंदर परिवर्तित हो जाती है ...
तो जैसा कि कल्पना है, पहला मान 2-आयामी सरणी है जिसमें एक सरणी अंदर है ... इसलिए [[]][+[]]
समान [[]][0]
होगा जो वापस आ जाएगी []
...
और अंत में ++
इसे रूपांतरित करें और इसे बढ़ाएं 1
...
तो आप कल्पना कर सकते हैं, 1
+ "0"
होगा "10"
...
+[]
एक खाली सरणी को0
... फिर एक दोपहर बर्बाद ...;)