11

मैं एक गेम बना रहा हूं, और मुझे एक समस्या आई है ... जब मैं सहेजने की कोशिश करता हूं, जेएसओएन विफल रहता है और रिपोर्ट करता है कि परिपत्र संदर्भ कहीं और किया जा रहा है। मुझे नहीं लगता कि यह वास्तव में है, मैं इसे नहीं देख सकता, तो क्या कोई एल्गोरिदम या कुछ भी है जो मुझे बता सकता है कि यह कहां है (किस वस्तु और सामान के बीच)? साथ ही, क्या एक JSON विकल्प है जो परिपत्र संदर्भ सहेज सकता है? मैं एक node.js सर्वर चला रहा हूं, मैंने this देखा, लेकिन मैं इसे काम नहीं कर सकता (यह मेरे कोड में एक मॉड्यूल के रूप में नहीं बनाया जा सकता है)।क्या जावास्क्रिप्ट में परिपत्र संदर्भ का परीक्षण करने का कोई तरीका है?

उत्तर

12

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

तो सर्कुलर संदर्भ को संग्रहीत करने के बजाय, आप ऑब्जेक्ट में केवल एक पॉइंटर स्टोर करते हैं। सूचक कुछ ref : '#path.to.object' जैसा होगा जो कि जब आप deserialize करते हैं तो हल किया जा सकता है ताकि आप संदर्भ को वास्तविक वस्तु पर वापस इंगित कर सकें। आपको क्रमबद्ध करने में सक्षम होने के लिए केवल क्रमबद्धता पर संदर्भ तोड़ने की आवश्यकता है।

खोज जावास्क्रिप्ट में एक परिपत्र संदर्भ रिकर्सिवली अस्थायी सरणी में प्रत्येक z के लिए पहचान ऑपरेटर (यानी strict comparison operator) === साथ सभी वस्तुओं (for (x in y) के साथ), दुकान x एक सरणी में के माध्यम से पुनरावृत्ति और तुलना प्रत्येक x द्वारा किया जा सकता। जब भी x === z सत्य के बराबर होता है, तो संदर्भ को x पर प्लेसहोल्डर के साथ प्रतिस्थापित करें जिसे ऊपर उल्लिखित ref पर क्रमबद्ध किया जाएगा।

पर "का दौरा किया" वस्तुओं एक सरणी रखने के लिए एक वैकल्पिक करने के लिए "कलंक" वस्तुओं आप इस बहुत भोली उदाहरण की तरह उन पर एक संपत्ति की स्थापना, द्वारा के माध्यम से पुनरावृति है:

for (x in y) { 
    if (x.visited) { 
     continue; 
    } 

    x.visited = true; 
} 
+3

tainting के लिए +1 :) – dinjas

8

कोई अच्छा है वस्तुओं में परिपत्र का पता लगाने का तरीका लेकिन ऑब्जेक्ट पेड़ को चलकर और संदर्भों की जांच करके यह संभव है। मैं एक नोड-पैदल समारोह है कि अगर एक नोड पहले से ही अपने माता-पिता

function isCircularObject(node, parents){ 
    parents = parents || []; 

    if(!node || typeof node != "object"){ 
     return false; 
    } 

    var keys = Object.keys(node), i, value; 

    parents.push(node); // add self to current path  
    for(i = keys.length-1; i>=0; i--){ 
     value = node[keys[i]]; 
     if(value && typeof value == "object"){ 
      if(parents.indexOf(value)>=0){ 
       // circularity detected! 
       return true; 
      } 
      // check child nodes 
      if(arguments.callee(value, parents)){ 
       return true; 
      } 

     } 
    } 
    parents.pop(node); 
    return false; 
} 

के रूप में इस्तेमाल किया गया है और उपयोग isCircularObject(obj_value) होगा जहां फ़ंक्शन true अगर घेरा मौजूद है और false नहीं तो पहचानने का प्रयास भी ऊपर पके हुए।

// setup test object 
var testObj = { 
    property_a:1, 
    property_b: { 
     porperty_c: 2 
     }, 
    property_d: { 
     property_e: { 
      property_f: 3 
      } 
     } 
    } 

console.log(isCircularObject(testObj)); // false 

// add reference to another node in the same object 
testObj.property_d.property_e.property_g = testObj.property_b; 
console.log(isCircularObject(testObj)); // false 

// add circular node 
testObj.property_b.property_c = testObj.property_b; 
console.log(isCircularObject(testObj)); // true 

प्रमुख मुद्दा जा रहा है कि एक वस्तु मूल्य अगर यह एक ही वस्तु संदर्भ है और नहीं है जब यह किसी अन्य वस्तु (यहां तक ​​कि पूरी तरह से समान हैं) है एक अन्य मूल्य केवल साथ बराबर है।

+0

बहुत बहुत धन्यवाद! – corazza

1

मैं इस बारे में सोच रहा था कि आप अपने अन्य प्रश्न से प्रारंभिक कोड के आधार पर क्या हासिल करने की कोशिश कर रहे हैं। ऐसा कुछ क्यों न करें।

Player = function() 
{ 
    this.UnitTypeXpower = 2 
    this.UnitTypeYpower = 7 

} 

UnitTypeXAdd = function(owner) 
{ 
    owner.UnitTypeXpower++; 
} 

इस तरह आपको एक परिपत्र संदर्भ का उपयोग करने की आवश्यकता नहीं है और यह वही काम पूरा करता है।

+0

मेरे द्वारा उपयोग की जाने वाली वास्तविक वस्तुएं अधिक जटिल हैं, मैंने प्लेसहोल्डर के रूप में वहां बिजली डाली है, क्योंकि पूरे कोड को रखना एक बुरा विचार होगा ... – corazza

4

यह एंड्रिस के उत्तर का एक छोटा सा विस्तार है जो आपको बताता है कि पहला परिपत्र तत्व कहां है, इसलिए आप इसके अनुसार सौदा कर सकते हैं।

function findCircularObject(node, parents, tree){ 
    parents = parents || []; 
    tree = tree || []; 

    if (!node || typeof node != "object") 
     return false; 

    var keys = Object.keys(node), i, value; 

    parents.push(node); // add self to current path 
    for (i = keys.length - 1; i >= 0; i--){ 
     value = node[keys[i]]; 
     if (value && typeof value == "object") { 
      tree.push(keys[i]); 
      if (parents.indexOf(value) >= 0) 
       return true; 
      // check child nodes 
      if (arguments.callee(value, parents, tree)) 
       return tree.join('.'); 
      tree.pop(); 
     } 
    } 
    parents.pop(); 
    return false; 
} 

यदि आप स्ट्रिंग नहीं चाहते हैं, तो पेड़ सरणी अनावश्यक है।बस परिपत्र वस्तु खुद या

return parents.pop(); 
अपनी मूल के लिए

के लिए

return value; 

करने के लिए मूल समारोह बदल जाते हैं।

1

यहां कोड है जिसका उपयोग मैं परिपत्र संदर्भों का पता लगाने के लिए कर रहा हूं, यह accepted answer by asbjornu में सुझाई गई तकनीक का उपयोग करता है, जिससे प्रत्येक मान चल रहा है और इसका संदर्भ एक सरणी में बनाए रखा जाता है ताकि अगले मूल्य की तुलना की जा सके उन लोगों के साथ पहले चला गया।

function isCircular(obj, arr) { 
    "use strict"; 

    var type = typeof obj, 
     propName, 
     //keys, 
     thisVal, 
     //iterKeys, 
     iterArr, 
     lastArr; 

    if (type !== "object" && type !== "function") { 
     return false; 
    } 

    if (Object.prototype.toString.call(arr) !== '[object Array]') { 
    //if (!Array.isArray(arr)) { 
     type = typeof arr; // jslint sake 
     if (!(type === "undefined" || arr === null)) { 
      throw new TypeError("Expected attribute to be an array"); 
     } 

     arr = []; 
    } 

    arr.push(obj); 
    lastArr = arr.length - 1; 

    for (propName in obj) { 
    //keys = Object.keys(obj); 
    //propName = keys[iterKeys]; 
    //for (iterKeys = keys.length - 1; iterKeys >= 0; iterKeys -= 1) { 
     thisVal = obj[propName]; 
     //thisVal = obj[keys[iterKeys]]; 
     type = typeof thisVal; 

     if (type === "object" || type === "function") { 
      for (iterArr = lastArr; iterArr >= 0; iterArr -= 1) { 
       if (thisVal === arr[iterArr]) { 
        return true; 
       } 
      } 

      // alternative to the above for loop 
      /* 
      if (arr.indexOf(obj[propName]) >= 0) { 
       return true; 
      } 
      */ 

      if (isCircular(thisVal, arr)) { 
       return true; 
      } 

     } 
    } 

    arr.pop(); 

    return false; 
} 

इस कोड jsfiddle है, जहां आप अपने आप के लिए यह परीक्षण कर सकते हैं पर उपलब्ध है। मैंने jsperf पर कुछ प्रदर्शन परीक्षण भी चलाए हैं।

Array.indexOf केवल जावास्क्रिप्ट 1.6 के रूप में पेश किया गया था, को देखने के MDN page

Array.isArray केवल जावास्क्रिप्ट 1.8.5 के रूप में पेश किया गया था, को देखने के MDN page

Object.keys केवल जावास्क्रिप्ट 1.8.5 के रूप में पेश किया गया था, MDN page

देखना

यह भी ध्यान देने योग्य है कि arguments.callee नामित मज़े का उपयोग करने के लिए सख्त मोड में सख्त मोड में बहिष्कृत और वर्जित है ctions

+0

आपका कोड इस तरह सरल हो सकता है: { JSON.stringify (obj); सच वापसी; } पकड़ो (ई) { झूठी वापसी; } –

+0

ओपी ने लिखा: 'मैं एक खेल बना रहा हूं, और मुझे एक समस्या आई है ... जब मैं सहेजने की कोशिश करता हूं, जेएसओएन विफल रहता है और रिपोर्ट करता है कि सर्कुलर संदर्भ कहीं और किया जा रहा है। मुझे नहीं लगता कि यह वास्तव में है, मैं इसे नहीं देख सकता, तो क्या कोई एल्गोरिदम या कुछ भी है जो मुझे बता सकता है कि यह कहां है (किस वस्तु और सामान के बीच)? साथ ही, क्या एक JSON विकल्प है जो परिपत्र संदर्भ सहेज सकता है? मैं एक node.js सर्वर चला रहा हूं, मैंने इसे देखा, लेकिन मैं इसे काम नहीं कर सकता (यह मेरे कोड में एक मॉड्यूल के रूप में नहीं बनाया जा सकता है)। ' – Xotic750

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