Javascript में valueOf () बनाम .String ()


115

जावास्क्रिप्ट में प्रत्येक ऑब्जेक्ट में एक valueOf () और toString () विधि होती है। मुझे लगता है कि जब भी एक स्ट्रिंग रूपांतरण के लिए बुलाया जाता है, तो स्ट्रैसिंग () विधि का उपयोग किया जाता है, लेकिन जाहिर तौर पर इसे valueOf () द्वारा ट्रम्प किया जाता है।

उदाहरण के लिए, कोड

var x = {toString: function() {return "foo"; },
         valueOf: function() {return 42; }};
window.console.log ("x="+x);
window.console.log ("x="+x.toString());

छप जाएगा

x=42
x=foo

यह मुझे पीछे की तरफ से टकराता है .. अगर x एक जटिल संख्या थी, उदाहरण के लिए, मैं चाहता हूं कि मुझे इसकी परिमाण देना होगा, लेकिन जब भी मैं एक स्ट्रिंग में बदलना चाहता हूं तो मुझे "a + bi" जैसा कुछ चाहिए। और मैं एक स्ट्रिंग में निहित संदर्भों में toString () को स्पष्ट रूप से कॉल नहीं करना चाहता।

क्या यह सिर्फ ऐसा ही है?


6
क्या आपने कोशिश की window.console.log (x);या alert (x);?
Li0liQ

5
वे क्रमशः "ऑब्जेक्ट" और "फू" देते हैं। मजेदार चीजें।
ब्रेनजाम

वास्तव में, सतर्क (एक्स); "फू", और window.console.log (x) देता है; फायरबग में "foo {}" और क्रोम कंसोल में संपूर्ण ऑब्जेक्ट देता है।
ब्रेनजम

फ़ायरफ़ॉक्स में 33.0.2 alert(x)प्रदर्शित fooऔर window.console.log(x)प्रदर्शित करता है Object { toString: x.toString(), valueOf: x.valueOf() }
जॉन सोंडरसन

जवाबों:


107

क्यों ("x =" + x) "x = मान" देता है और "x = toring" निम्नलिखित नहीं है। "+" का मूल्यांकन करते समय, जावास्क्रिप्ट पहले ऑपरेंड्स के आदिम मूल्यों को इकट्ठा करता है, और फिर यह तय करता है कि प्रत्येक प्राइमेटिव के प्रकार के आधार पर इसके अलावा या अवकलन को लागू किया जाना चाहिए।

तो, यह है कि आप कैसे सोचते हैं कि यह काम करता है

a + b:
    pa = ToPrimitive(a)
    if(pa is string)
       return concat(pa, ToString(b))
    else
       return add(pa, ToNumber(b))

और वास्तव में यही होता है

a + b:
    pa = ToPrimitive(a)
    pb = ToPrimitive(b)*
    if(pa is string || pb is string)
       return concat(ToString(pa), ToString(pb))
    else
       return add(ToNumber(pa), ToNumber(pb))

यही है ,String को मान के परिणाम पर लागू किया जाता है, आपकी मूल वस्तु पर नहीं।

आगे के संदर्भ के लिए, ECMAScript भाषा विनिर्देश में परिवर्धन संचालक (+) अनुभाग 11.6.1 देखें ।


* जब स्ट्रिंग संदर्भ में कहा जाता है, ToPrimitive, toString का आह्वान करता है , लेकिन यहां ऐसा नहीं है, क्योंकि '+' किसी भी प्रकार के संदर्भ को लागू नहीं करता है।


3
"वास्तव में" ब्लॉक रीड "यदि सशर्त नहीं होना चाहिए (यदि पीए स्ट्रिंग है और पीबी स्ट्रिंग है)"? Ie "&&" के बजाय "|| ?
ब्रेनजम

3
मानक निश्चित रूप से "या" (लिंक देखें) कहता है।
user187291

2
हां, यह बिल्कुल सही है - सहमति में अन्य प्रकारों पर तार दिए जाते हैं। यदि कोई ऑपरेंड एक स्ट्रिंग है, तो पूरी चीज को एक स्ट्रिंग के रूप में संक्षिप्त किया जाएगा। अच्छा उत्तर।
devios1

76

इससे पहले कि मैं इसका उत्तर दूं, थोड़ा और विस्तार से देखें:

var x = {
    toString: function () { return "foo"; },
    valueOf: function () { return 42; }
};

alert(x); // foo
"x=" + x; // "x=42"
x + "=x"; // "42=x"
x + "1"; // 421
x + 1; // 43
["x=", x].join(""); // "x=foo"

toStringसमारोह है नहीं "मात" द्वारा valueOfसामान्य रूप में। ECMAScript मानक वास्तव में इस प्रश्न का बहुत अच्छा जवाब देता है। हर वस्तु की एक [[DefaultValue]]संपत्ति होती है, जिसकी गणना ऑन-डिमांड होती है। इस संपत्ति के लिए पूछते समय, दुभाषिया यह भी बताता है कि यह किस तरह के मूल्य की उम्मीद करता है। यदि संकेत है String, तो toStringपहले उपयोग किया जाता है valueOf। लेकिन, अगर संकेत है Number, तोvalueOf पहले इस्तेमाल किया जाएगा। ध्यान दें कि यदि केवल एक मौजूद है, या यह एक गैर-आदिम रिटर्न करता है, तो यह आमतौर पर दूसरे को दूसरी पसंद के रूप में बुलाएगा।

+ऑपरेटर हमेशा संकेत प्रदान करता है Number, भले ही पहले संकार्य एक स्ट्रिंग मान है। हालांकि यह xअपने Numberप्रतिनिधित्व के लिए पूछता है , क्योंकि पहले ऑपरेंड से एक स्ट्रिंग लौटाता है [[DefaultValue]], यह स्ट्रिंग संघनन करता है।

यदि आप यह गारंटी देना चाहते हैं कि toStringस्ट्रिंग संघनन के लिए कहा जाता है, तो एक सरणी और .join("")विधि का उपयोग करें।

(ActionScript 3.0 +, हालांकि, के व्यवहार को थोड़ा संशोधित करता है । यदि या तो ऑपरेंड एक है String, तो यह इसे एक स्ट्रिंग संघातक संचालक के रूप में मानेगा और Stringकॉल करने पर संकेत का उपयोग करेगा [[DefaultValue]]। इसलिए, AS3 में, यह उदाहरण "फू, एक्स" फू, फू देता है। = x, foo1, 43, x = foo "।)


1
यह भी ध्यान दें कि यदि valueOfया toStringगैर-आदिम वापस लौटाते हैं, तो उन्हें अनदेखा किया जाता है। यदि न तो अस्तित्व में है, न ही आदिम लौटाता है, तो TypeErrorफेंक दिया जाता है।
बेचेरी

1
धन्यवाद बचेरी, यह जवाब का कैलिबर है जिसकी मैं उम्मीद कर रहा था। लेकिन x + "x =" नहीं होना चाहिए; उपज "42x ="? और x + "1"; उपज 421? इसके अलावा, क्या आपके पास ECMAScript मानक के संबंधित भाग के लिए एक URL है?
ब्रेनजम

2
वास्तव में, '+' संकेत का उपयोग नहीं करता है ($ 11.6.1 देखें) इसलिए ToPrimitive invokes [[DefaultValue]](no-hint), जो इसके बराबर है [[DefaultValue]](number)
user187291

9
यह अंतर्निहित दिनांक वर्ग के मामले में प्रतीत नहीं होता है। ("" + new Date(0)) === new Date(0).toString()। दिनांक ऑब्जेक्ट हमेशा ऐसा लगता है toString()जब वह किसी चीज़ में जोड़ा जाता है।
19oz में kpozin

7
+1 & Thx! मुझे आपका ब्लॉग-पोस्ट मिला, जिसमें आप इस उत्तर पर विस्तार से चर्चा करना चाहते हैं और इसे यहाँ लिंक / साझा करना चाहते हैं। यह इस उत्तर के लिए वास्तव में सहायक था (दिमित्री ए। सोशनिकोव की टिप्पणी सहित)।
गीतालीलाब

1

TLDR

प्रकार का ज़बरदस्ती, या अंतर्निहित प्रकार रूपांतरण, कमजोर टाइपिंग को सक्षम करता है और पूरे जावास्क्रिप्ट में उपयोग किया जाता है। अधिकांश ऑपरेटर (सख्त समानता ऑपरेटरों के उल्लेखनीय अपवाद के साथ ) ===और !==, और मूल्य की जांच के संचालन (जैसे। if(value)...), उन लोगों को दिए गए मानों को एकरूप कर देंगे, यदि उन मानों के प्रकार तुरंत संचालन के अनुकूल नहीं हैं।

किसी मूल्य का ज़बरदस्त इस्तेमाल करने वाला सटीक तंत्र उस मूल्यांकन पर निर्भर करता है जिसका मूल्यांकन किया जा रहा है। प्रश्न में, अतिरिक्त ऑपरेटर का उपयोग किया जा रहा है।

इसके अलावा ऑपरेटर पहले यह सुनिश्चित करेगा कि दोनों ऑपरेंड प्राइमेटिव हैं, जो इस मामले में, कॉलिंग valueOfमेथड शामिल है । toStringविधि इस उदाहरण में नहीं बुलाया क्योंकि ओवरराइड है valueOfवस्तु पर विधि xएक आदिम मान देता है।

फिर, क्योंकि प्रश्न में एक ऑपरेंड एक स्ट्रिंग है, दोनों ऑपरेंड को स्ट्रिंग्स में परिवर्तित किया जाता है। यह प्रक्रिया अमूर्त, आंतरिक संचालन ToString(नोट: पूंजीकृत) का उपयोग करती है, और toStringऑब्जेक्ट पर विधि (या इसके प्रोटोटाइप श्रृंखला) से अलग है।

अंत में, परिणामी तार समवर्ती होते हैं।

विवरण

जावास्क्रिप्ट में हर भाषा प्रकार के अनुरूप हर कंस्ट्रक्टर फंक्शन ऑब्जेक्ट के प्रोटोटाइप पर (जैसे। संख्या, बिगआईंट, स्ट्रिंग, बुलियन, सिंबल और ऑब्जेक्ट), दो तरीके हैं: valueOfऔरtoString

इसका उद्देश्य valueOfकिसी वस्तु के साथ जुड़े हुए आदिम मूल्य (यदि यह एक है) को पुनः प्राप्त करना है। यदि किसी वस्तु में अंतर्निहित आदिम मूल्य नहीं है, तो वस्तु बस वापस आ जाती है।

यदि valueOfएक आदिम के खिलाफ आह्वान किया जाता है, तो आदिम सामान्य तरीके से ऑटो-बॉक्स होता है, और अंतर्निहित आदिम मूल्य वापस आ जाता है। ध्यान दें कि तार के लिए, अंतर्निहित आदिम मूल्य (यानी द्वारा लौटाए गए मूल्य valueOf) स्ट्रिंग का प्रतिनिधित्व है।

निम्न कोड से पता चलता है कि valueOfविधि एक आवरण वस्तु से अंतर्निहित आदिम मूल्य लौटाती है, और यह दिखाती है कि कैसे असंबद्ध वस्तु उदाहरण जो कि आदिम के अनुरूप नहीं हैं, वापस लौटने के लिए कोई आदिम मूल्य नहीं है, इसलिए वे बस खुद को वापस करते हैं।

console.log(typeof new Boolean(true)) // 'object'
console.log(typeof new Boolean(true).valueOf()) // 'boolean'
console.log(({}).valueOf()) // {} (no primitive value to return)

के प्रयोजन के toString, दूसरे हाथ पर, एक वस्तु के एक स्ट्रिंग प्रतिनिधित्व वापसी है।

उदाहरण के लिए:

console.log({}.toString()) // '[object Object]'
console.log(new Number(1).toString()) // '1'

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

ज़बरदस्त प्रक्रिया के दौरान, रूपांतरण के दो तरीके हो सकते हैं:

  1. एक वस्तु का एक आदिम में रूपांतरण (जिसमें स्वयं एक प्रकार का रूपांतरण शामिल हो सकता है), और
  2. एक विशेष प्रकार का उदाहरण के लिए सीधी रूपांतरण, आदिम प्रकार से एक के एक निर्माता समारोह वस्तु का उपयोग कर (यानी। Number(), Boolean(), String()आदि)

एक आदिम में रूपांतरण

गैर-आदिम प्रकारों को संचालन के लिए प्राइमिटिव में परिवर्तित करने का प्रयास करते समय, अमूर्त ऑपरेशन ToPrimitiveको 'संख्या', या 'स्ट्रिंग' के वैकल्पिक "संकेत" के साथ कहा जाता है। यदि संकेत छोड़ा गया है, तो डिफ़ॉल्ट संकेत 'संख्या' है (जब तक कि @@toPrimitiveविधि को ओवरराइड नहीं किया गया है)। यदि संकेत 'स्ट्रिंग' है, तो toStringपहले की कोशिश की जाती है, और valueOfअगर toStringएक आदिम वापस नहीं आया तो दूसरा । एल्स, इसके विपरीत। संकेत रूपांतरण का अनुरोध करने वाले ऑपरेशन पर निर्भर करता है।

इसके अलावा ऑपरेटर कोई संकेत नहीं देता है, इसलिए valueOfपहले प्रयास किया जाता है। घटाव ऑपरेटर 'संख्या' के संकेत की आपूर्ति करता है, इसलिए valueOfपहले प्रयास किया जाता है। केवल उन स्थितियों में मैं पा सकता हूं जिनमें संकेत 'स्ट्रिंग' हैं:

  1. Object#toString
  2. अमूर्त ऑपरेशन ToPropertyKey, जो एक तर्क को एक मूल्य में परिवर्तित करता है जिसे संपत्ति कुंजी के रूप में उपयोग किया जा सकता है

प्रत्यक्ष प्रकार रूपांतरण

प्रत्येक ऑपरेटर के पास अपना ऑपरेशन पूरा करने के अपने नियम हैं। प्रत्येक ऑपरेटर ToPrimitiveएक आदिम है यह सुनिश्चित करने के लिए सबसे पहले ऑपरेटर का उपयोग करेगा ; फिर, यदि कोई ऑपरेंड एक स्ट्रिंग है, तो यह ToStringस्ट्रिंग के कॉन्सेप्टेशन व्यवहार को प्रदान करने के लिए जानबूझकर प्रत्येक ऑपरेंड पर अमूर्त ऑपरेशन को लागू करेगा । यदि, ToPrimitiveचरण के बाद , दोनों ऑपरेंड स्ट्रिंग्स नहीं हैं, तो अंकगणित जोड़ दिया जाता है।

इसके अलावा, घटाव ऑपरेटर में अतिभारित व्यवहार नहीं होता है, और इसलिए toNumericप्रत्येक ऑपरेंड पर आह्वान करेगा जो पहले उन्हें उपयोग करने वाली प्राथमिकताओं में परिवर्तित कर देगा ToPrimitive

इसलिए:

 1  +  1   //  2                 
'1' +  1   // '11'   Both already primitives, RHS converted to string, '1' + '1',   '11'
 1  + [2]  // '12'   [2].valueOf() returns an object, so `toString` fallback is used, 1 + String([2]), '1' + '2', 12
 1  + {}   // '1[object Object]'    {}.valueOf() is not a primitive, so toString fallback used, String(1) + String({}), '1' + '[object Object]', '1[object Object]'
 2  - {}   // NaN    {}.valueOf() is not a primitive, so toString fallback used => 2 - Number('[object Object]'), NaN
+'a'       // NaN    `ToPrimitive` passed 'number' hint), Number('a'), NaN
+''        // 0      `ToPrimitive` passed 'number' hint), Number(''), 0
+'-1'      // -1     `ToPrimitive` passed 'number' hint), Number('-1'), -1
+{}        // NaN    `ToPrimitive` passed 'number' hint', `valueOf` returns an object, so falls back to `toString`, Number('[Object object]'), NaN
 1 + 'a'   // '1a'    Both are primitives, one is a string, String(1) + 'a'
 1 + {}    // '1[object Object]'    One primitive, one object, `ToPrimitive` passed no hint, meaning conversion to string will occur, one of the operands is now a string, String(1) + String({}), `1[object Object]`
[] + []    // ''     Two objects, `ToPrimitive` passed no hint, String([]) + String([]), '' (empty string)
 1 - 'a'   // NaN    Both are primitives, one is a string, `ToPrimitive` passed 'number' hint, 1-Number('a'), 1-NaN, NaN
 1 - {}    // NaN    One primitive, one is an object, `ToPrimitive` passed 'number' hint, `valueOf` returns object, so falls back to `toString`, 1-Number([object Object]), 1-NaN, NaN
[] - []    // 0      Two objects, `ToPrimitive` passed 'number' hint => `valueOf` returns array instance, so falls back to `toString`, Number('')-Number(''), 0-0, 0

ध्यान दें कि Dateआंतरिक वस्तु अद्वितीय है, इसमें डिफ़ॉल्ट @@toPrimitiveविधि को ओवरराइड करने के लिए एकमात्र आंतरिक है , जिसमें डिफ़ॉल्ट संकेत को 'स्ट्रिंग' ('संख्या' के बजाय) माना जाता है। ऐसा करने का कारण, Dateप्रोग्रामर की सुविधा के लिए, उदाहरणों को उनके संख्यात्मक मान के बजाय डिफ़ॉल्ट रूप से पठनीय स्ट्रिंग्स में अनुवाद करना है। आप @@toPrimitiveअपनी खुद की वस्तुओं का उपयोग करके ओवरराइड कर सकते हैं Symbol.toPrimitive

निम्न ग्रिड सार समानता ऑपरेटर ( ==) ( स्रोत ) के लिए ज़बरदस्ती परिणाम दिखाता है :

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

देखें भी

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