javascript - सरणी पुनरावृत्ति के साथ "के लिए... इन" का उपयोग क्यों एक बुरा विचार है?




arrays for-loop (16)

मुझे बताया गया है कि जावास्क्रिप्ट में सरणी के साथ for...in का उपयोग न करें। क्यों नहीं?



2016 तक (ईएस 6) हम सरणी पुनरावृत्ति for…of लिए उपयोग कर सकते हैं, क्योंकि जॉन स्लेगर्स ने पहले ही देखा है।

चीजों को स्पष्ट करने के लिए, मैं बस इस सरल प्रदर्शन कोड को जोड़ना चाहूंगा:

Array.prototype.foo = 1;
var arr = [];
arr[5] = "xyz";

console.log("for...of:");
var count = 0;
for (var item of arr) {
    console.log(count + ":", item);
    count++;
    }

console.log("for...in:");
count = 0;
for (var item in arr) {
    console.log(count + ":", item);
    count++;
    }

कंसोल दिखाता है:

for...of:

0: undefined
1: undefined
2: undefined
3: undefined
4: undefined
5: xyz

for...in:

0: 5
1: foo

दूसरे शब्दों में:

  • for...of 0 से 5 for...of गणना करता है, और Array.prototype.foo भी अनदेखा Array.prototype.foo । यह सरणी मान दिखाता है

  • for...in सूची में केवल 5 , अपरिभाषित सरणी अनुक्रमणिका को अनदेखा कर रहा है, लेकिन foo जोड़ रहा है। यह सरणी संपत्ति के नाम दिखाता है।


अन्य समस्याओं के अलावा, "for..in" वाक्यविन्यास शायद धीमा है, क्योंकि सूचकांक एक स्ट्रिंग है, पूर्णांक नहीं है।

var a = ["a"]
for (var i in a)
    alert(typeof i)  // 'string'
for (var i = 0; i < a.length; i++)
    alert(typeof i)  // 'number'

अपने आप for-in अंतर्निहित बयान "खराब अभ्यास" नहीं है, हालांकि इसका उपयोग गलत तरीके से किया जा सकता है , उदाहरण के लिए, सरणी या सरणी जैसी वस्तुओं पर पुनरावृत्ति करने के लिए।

for-in कथन का उद्देश्य ऑब्जेक्ट गुणों पर गिनना है । यह कथन प्रोटोटाइप श्रृंखला में जाएगा, विरासत गुणों पर भी गिनती होगी, ऐसी चीज जो कभी-कभी वांछित नहीं होती है।

इसके अलावा, पुनरावृत्ति के क्रम को spec द्वारा गारंटी नहीं दी जाती है, जिसका अर्थ है कि यदि आप किसी सरणी ऑब्जेक्ट को "पुन: सक्रिय" करना चाहते हैं, तो इस कथन के साथ आप यह सुनिश्चित नहीं कर सकते कि गुण (सरणी अनुक्रमणिका) संख्यात्मक क्रम में देखी जाएंगी।

उदाहरण के लिए, जेस्क्रिप्ट (आईई <= 8) में, ऐरे ऑब्जेक्ट्स पर भी गणना का क्रम परिभाषित किया गया है क्योंकि गुण बनाए गए थे:

var array = [];
array[2] = 'c';
array[1] = 'b';
array[0] = 'a';

for (var p in array) {
  //... p will be "2", "1" and "0" on IE
}

इसके अलावा, विरासत गुणों के बारे में बात करते हुए, यदि आप, उदाहरण के लिए, Array.prototype ऑब्जेक्ट (जैसे कुछ पुस्तकालयों को Array.prototype रूप में करते हैं) का विस्तार करते हैं, तो गुणों को भी समझा जाएगा:

Array.prototype.last = function () { return this[this.length-1]; };

for (var p in []) { // an empty array
  // last will be enumerated
}

जैसा कि मैंने सरणी या सरणी जैसी वस्तुओं पर फिर से शुरू करने से पहले कहा था, सबसे अच्छी चीज अनुक्रमिक लूप का उपयोग करना है , जैसे सादे-पुराने / लूप के लिए।

जब आप किसी ऑब्जेक्ट के केवल अपने गुणों (जो विरासत में नहीं हैं) की गणना करना चाहते हैं, तो आप hasOwnProperty विधि का उपयोग कर सकते हैं:

for (var prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    // prop is not inherited
  }
}

और कुछ लोग Object.prototype से सीधे विधि को कॉल करने की सलाह देते हैं ताकि समस्याएं Object.prototype से बच सकें यदि कोई व्यक्ति हमारे ऑब्जेक्ट में hasOwnProperty नाम की एक संपत्ति जोड़ता है:

for (var prop in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
    // prop is not inherited
  }
}

आपको केवल संपत्ति सूचियों पर for(var x in y) उपयोग करना चाहिए, वस्तुओं पर नहीं (ऊपर वर्णित अनुसार)।


इस तथ्य के अलावा कि ... के for सभी समरूप गुणों पर लूप in (जो "सभी सरणी तत्वों" के समान नहीं है!), http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf देखें http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf , सेक्शन 12.6.4 (5 वां संस्करण) या 13.7.5.15 (7 वां संस्करण):

गुणों की गणना करने के यांत्रिकी और आदेश ... निर्दिष्ट नहीं है ...

(जोर मेरा।)

इसका मतलब है कि यदि कोई ब्राउज़र चाहता था, तो वह उस क्रम में गुणों के माध्यम से जा सकता था जिसमें उन्हें डाला गया था। या संख्यात्मक क्रम में। या व्याख्यात्मक क्रम में (जहां "30" "4" से पहले आता है! सभी ऑब्जेक्ट कुंजियों को ध्यान में रखें - और इस प्रकार, सभी सरणी अनुक्रमणिका - वास्तव में तार हैं, जिससे कुल समझ मिलती है)। यह बाल्टी द्वारा उनके माध्यम से जा सकता है, अगर यह हैश टेबल के रूप में वस्तुओं को लागू किया। या उसमें से कोई भी ले लो और "पीछे की ओर" जोड़ें। एक ब्राउज़र यादृच्छिक रूप से फिर से शुरू हो सकता है और ईसीएमए -262 अनुपालनशील हो सकता है, जब तक कि यह प्रत्येक संपत्ति को एक बार देखे।

व्यावहारिक रूप से, अधिकांश ब्राउज़र वर्तमान में लगभग उसी क्रम में पुनरावृत्त करना पसंद करते हैं। लेकिन कुछ भी नहीं कह रहा है कि उन्हें करना है। यह कार्यान्वयन विशिष्ट है, और किसी भी समय बदल सकता है अगर एक और तरीका कहीं अधिक कुशल पाया गया।

किसी भी तरह से, इसके for ... इसमें आदेश का कोई अर्थ नहीं है। यदि आप आदेश के बारे में परवाह करते हैं, तो इसके बारे में स्पष्ट रहें और एक इंडेक्स के साथ नियमित रूप for लूप का उपयोग करें।


इसके अलावा, अर्थशास्त्र के कारण, मार्गों के for, in मार्ग (यानी किसी भी अन्य जावास्क्रिप्ट ऑब्जेक्ट के समान) अन्य लोकप्रिय भाषाओं के साथ गठबंधन नहीं है।

// C#
char[] a = new char[] {'A', 'B', 'C'};
foreach (char x in a) System.Console.Write(x); //Output: "ABC"

// Java
char[] a = {'A', 'B', 'C'};
for (char x : a) System.out.print(x);          //Output: "ABC"

// PHP
$a = array('A', 'B', 'C');
foreach ($a as $x) echo $x;                    //Output: "ABC"

// JavaScript
var a = ['A', 'B', 'C'];
for (var x in a) document.write(x);            //Output: "012"

एक महत्वपूर्ण पहलू यह है कि for...in केवल उस ऑब्जेक्ट में निहित गुणों पर पुनरावृत्त होता है जिसमें उनकी समृद्ध संपत्ति विशेषता सत्य पर सेट होती है। तो यदि कोई व्यक्ति किसी ऑब्जेक्ट पर पुन: प्रयास करने का प्रयास करता है for...in तो उसके मनमानी गुण विशेषता को गलत होने पर मनमानी गुणों को याद किया जा सकता है। सामान्य ऐरे ऑब्जेक्ट्स के लिए समृद्ध गुण विशेषता को बदलना बहुत संभव है ताकि कुछ तत्वों की गणना न हो। हालांकि आम तौर पर संपत्ति गुण किसी ऑब्जेक्ट के भीतर फ़ंक्शन गुणों पर लागू होते हैं।

कोई गुणों की 'मूल्यवान संपत्ति विशेषता' के मूल्य की जांच कर सकता है:

myobject.propertyIsEnumerable('myproperty')

या सभी चार संपत्ति विशेषताओं को प्राप्त करने के लिए:

Object.getOwnPropertyDescriptor(myobject,'myproperty')

यह ईसीएमएस्क्रिप्ट 5 में उपलब्ध एक सुविधा है - पिछले संस्करणों में समृद्ध संपत्ति विशेषता के मूल्य को बदलना संभव नहीं था (यह हमेशा सत्य पर सेट किया गया था)।


के लिए समस्या for ... in ... - और यह केवल एक समस्या बन जाती है जब कोई प्रोग्रामर वास्तव में भाषा को समझ नहीं पाता है; यह वास्तव में एक बग या कुछ भी नहीं है - यह है कि यह किसी ऑब्जेक्ट के सभी सदस्यों (ठीक है, सभी गणित सदस्यों पर पुनरावृत्त करता है, लेकिन यह अब के लिए एक विवरण है)। जब आप किसी सरणी के अनुक्रमित गुणों पर पुनरावृत्ति करना चाहते हैं, तो चीजों को अर्थपूर्ण रूप से सुसंगत रखने का एकमात्र गारंटी तरीका एक पूर्णांक अनुक्रमणिका का उपयोग करना है (यानी, एक for (var i = 0; i < array.length; ++i) शैली पाश)।

किसी ऑब्जेक्ट में इसके साथ जुड़े मनमानी गुण हो सकते हैं। विशेष रूप से सरणी उदाहरण पर अतिरिक्त गुणों को लोड करने के बारे में कुछ भी भयानक नहीं होगा। कोड जो केवल अनुक्रमित सरणी जैसी गुणों को देखना चाहता है इसलिए एक पूर्णांक अनुक्रमणिका से चिपकना चाहिए । कोड जो पूरी तरह से अवगत है for ... in करता है और वास्तव में सभी गुणों को देखने की ज़रूरत है, ठीक है तो यह भी ठीक है।


क्योंकि ... के लिए उस वस्तु के माध्यम से गणना करता है जो सरणी रखता है, सरणी नहीं। अगर मैं सरणी प्रोटोटाइप श्रृंखला में कोई फ़ंक्शन जोड़ता हूं, तो उसे भी शामिल किया जाएगा। अर्थात

Array.prototype.myOwnFunction = function() { alert(this); }
a = new Array();
a[0] = 'foo';
a[1] = 'bar';
for(x in a){
 document.write(x + ' = ' + a[x]);
}

यह लिखेंगे:

0 = foo
1 = bar
myOwnFunction = function() { alert(this); }

और चूंकि आप कभी भी यह सुनिश्चित नहीं कर सकते कि प्रोटोटाइप श्रृंखला में कुछ भी जोड़ा नहीं जाएगा, केवल सरणी को गिनने के लिए लूप का उपयोग करें:

for(i=0,x=a.length;i<x;i++){
 document.write(i + ' = ' + a[i]);
}

यह लिखेंगे:

0 = foo
1 = bar

क्योंकि यह ऑब्जेक्ट फ़ील्ड के माध्यम से गणना करता है, इंडेक्स नहीं। आप सूचकांक "लंबाई" के साथ मूल्य प्राप्त कर सकते हैं और मुझे संदेह है कि आप यह चाहते हैं।


दो प्रकार के चर के साथ काम करता है: हैशटेबल (सहयोगी सरणी) और सरणी (गैर-सहयोगी)।

जावास्क्रिप्ट स्वचालित रूप से आइटम के माध्यम से गुजरने के तरीके को निर्धारित करेगा। इसलिए यदि आप जानते हैं कि आपकी सरणी वास्तव में गैर-सहयोगी है तो आप for (var i=0; i<=arrayLen; i++) उपयोग कर सकते हैं, और ऑटो-डिटेक्शन पुनरावृत्ति को छोड़ सकते हैं।

लेकिन मेरी राय में, इसके लिए उपयोग करना बेहतर है, उस ऑटो-डिटेक्शन के लिए आवश्यक प्रक्रिया बहुत छोटी है।

इसके लिए एक वास्तविक उत्तर इस बात पर निर्भर करेगा कि ब्राउज़र जावास्क्रिप्ट कोड को कैसे पार्स / व्याख्या करता है। यह ब्राउज़र के बीच बदल सकता है।

मैं अन्य प्रयोजनों के बारे में सोच नहीं सकता / इनके लिए उपयोग नहीं कर रहा हूं;

//Non-associative
var arr = ['a', 'b', 'c'];
for (var i in arr)
   alert(arr[i]);

//Associative
var arr = {
   item1 : 'a',
   item2 : 'b',
   item3 : 'c'
};

for (var i in arr)
   alert(arr[i]);

मुझे नहीं लगता कि मुझे उदाहरण में जोड़ने के लिए बहुत कुछ है। ट्रिपिच का जवाब या सीएमएस का जवाब कुछ मामलों में for-in का उपयोग क्यों for-in जाना चाहिए।

हालांकि, मैं यह जोड़ना चाहता हूं कि आधुनिक ब्राउज़रों में एक विकल्प है जिसके for-in उन मामलों में उपयोग किया जा सकता है जहां for-in उपयोग नहीं किया जा सकता है। उस विकल्प के for-of :

for (var item of items) {
    console.log(item);
}

ध्यान दें :

दुर्भाग्यवश, इंटरनेट एक्सप्लोरर का कोई संस्करण इस सुविधा का समर्थन नहीं करता है ( एज 12+ करता है), इसलिए आपको थोड़ा सा इंतजार करना पड़ेगा जबतक कि आप इसे अपने क्लाइंट साइड प्रोडक्शन कोड में उपयोग नहीं कर सकते। हालांकि, यह आपके सर्वर की ओर जेएस कोड (यदि आप Node.js उपयोग करते हैं) में उपयोग करना सुरक्षित होना चाहिए।


यह जरूरी नहीं है कि आप जो कर रहे हैं उसके आधार पर), लेकिन सरणी के मामले में, अगर Array.prototype में कुछ जोड़ा गया है, तो आपको अजीब परिणाम मिलेंगे। जहां आप इस लूप को तीन बार चलाने की उम्मीद करेंगे:

var arr = ['a','b','c'];
for (var key in arr) { ... }

यदि helpfulUtilityMethod नामक फ़ंक्शन को Array के prototype में जोड़ा गया है, तो आपका लूप चार बार चलने वाला होगा: key 0 , 1 , 2 , और helpfulUtilityMethod । यदि आप केवल पूर्णांक की उम्मीद कर रहे थे, ओह।


सरणी तत्वों पर for..in करने के लिए आपको तीन कारणों का उपयोग क्यों नहीं करना चाहिए।

  • DontEnum एरे ऑब्जेक्ट के सभी और विरासत गुणों पर लूप होगा जो DontEnum नहीं हैं; इसका मतलब है कि अगर कोई विशिष्ट सरणी ऑब्जेक्ट में गुण जोड़ता है (इसके लिए वैध कारण हैं - मैंने स्वयं ऐसा किया है) या Array.prototype बदल दिया है (जिसे कोड में खराब अभ्यास माना जाता है जिसे अन्य स्क्रिप्ट के साथ अच्छी तरह से काम करना चाहिए) इन गुणों को भी फिर से चालू किया जाएगा; विरासत गुणों को hasOwnProperty() जांच करके बाहर रखा जा सकता है, लेकिन यह आपको सरणी ऑब्जेक्ट में सेट गुणों के साथ आपकी सहायता नहीं करेगा

  • for..in तत्व ऑर्डरिंग को संरक्षित करने की गारंटी नहीं है

  • यह धीमा है क्योंकि आपको सरणी ऑब्जेक्ट और इसकी संपूर्ण प्रोटोटाइप श्रृंखला के सभी गुणों को चलना होगा और फिर भी केवल संपत्ति का नाम प्राप्त होगा, यानी मूल्य प्राप्त करने के लिए, एक अतिरिक्त लुकअप की आवश्यकता होगी


for...in is useful when working on an object in JavaScript, but not for an Array, but still we can not say it's a wrong way, but it's not recommended, look at this example below using for...in loop:

let txt = "";
const person = {fname:"Alireza", lname:"Dezfoolian", age:35}; 
for (const x in person) {
    txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

OK, let's do it with Array now:

let txt = "";
const person = ["Alireza", "Dezfoolian", 35]; 
for (const x in person) {
   txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

As you see the result the same...

But let's try something, let's prototype something to Array ...

Array.prototype.someoneelse = "someoneelse";

Now we create a new Array();

let txt = "";
const arr = new Array();
arr[0] = 'Alireza';
arr[1] = 'Dezfoolian';
arr[2] = 35;
for(x in arr) {
 txt += arr[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 someoneelse

You see the someoneelse !!!... We actually looping through new Array object in this case!

So that's one of the reasons why we need to use for..in carefully, but it's not always the case...





for-loop