2013-02-18 57 views
6

Pytanie:Dlaczego ta prototypowa funkcja JavaScript przełamuje jQuery?

Jak tylko dodać poniższy kod na mojej stronie html, mam:

Line: 4 
Error: Object doesn't support the property or method "exec". 

Jest to prototyp, który powoduje błąd:

Object.prototype.allKeys = function() { 
     var keys = []; 
     for (var key in this) 
     { 
      // Very important to check for dictionary.hasOwnProperty(key) 
      // otherwise you may end up with methods from the prototype chain.. 
      if (this.hasOwnProperty(key)) 
      { 
       keys.push(key); 
       //alert(key); 
      } // End if (dict.hasOwnProperty(key)) 

     } // Next key 

     keys.sort(); 
     return keys; 
    }; // End Extension Function allKeys 

I to jest minimalny kod wymagany do odtworzenia błędu (przeglądarka w pytaniu: IE9):

<!DOCTYPE html> 
<html> 
<head> 
    <title>TestPage</title> 

    <script type="text/javascript" src="jquery-1.9.1.min.js"></script> 


    <script type="text/javascript"> 
     /* 
     Object.prototype.getName111 = function() { 
      var funcNameRegex = /function (.{1,})\(/; 
      var results = (funcNameRegex).exec((this).constructor.toString()); 
      return (results && results.length > 1) ? results[1] : ""; 
     }; // End Function getName 
     */ 

     Object.prototype.allKeys = function() { 
      var keys = []; 
      for (var key in this) 
      { 
       // Very important to check for dictionary.hasOwnProperty(key) 
       // otherwise you may end up with methods from the prototype chain.. 
       if (this.hasOwnProperty(key)) 
       { 
        keys.push(key); 
        //alert(key); 
       } // End if (dict.hasOwnProperty(key)) 

      } // Next key 

      keys.sort(); 
      return keys; 
     }; // End Extension Function allKeys 

    </script> 

</head> 
<body> 

    <select id="selLayers" name="myddl"> 
     <option value="1">One</option> 
     <option value="2">Twooo</option> 
     <option value="3">Three</option> 
     <option value="4">Text1</option> 
     <option value="5">Text2</option> 
    </select> 


    <script type="text/javascript"> 

     //var dict = { "de": { "Text1": "Ersetzung 1", "Text2": "Ersetzung 2" }, "fr": { "Text1": "Replacement 1", "Text2": "Réplacement 2" }, "it": { "Text1": "Replacemente 1", "Text2": "Replacemente 2" }, "en": { "Text1": "Replacement 1", "Text2": "Replacement 2"} }; 
     /* 
     var languages = dict.allKeys(); 


     for (var j = 0; j < languages.length; ++j) 
     { 
      var strCurrentLanguage = languages[j]; 
      var dictReplacements = dict[strCurrentLanguage] 
      var keys = dictReplacements.allKeys(); 

      //alert(JSON.stringify(dictReplacements)); 
      //alert(JSON.stringify(keys)); 


      for (var i = 0; i < keys.length; ++i) { 
       var strKey = keys[i]; 
       var strReplacement = dictReplacements[strKey]; 

       alert(strKey + " ==> " + strReplacement); 
       //alert('#selLayers option:contains("' + strKey + '")'); 
       //$('#selLayers option:contains("' + strKey + '")').html(strReplacement); 
       //$('#selLayers option:contains("Text1")').html("foobar"); 


      }  
     } 
     */ 


     $('#selLayers option:contains("Twooo")').text('Fish'); 


     //alert(dict.allKeys());   
     //alert(dict["de"]["abc"]); 




     /* 

     $('#selLayers option[value=2]').text('Fish'); 
     $('#selLayers option:contains("Twooo")').text('Fish'); 
     $('#selLayers option:contains("Twooo")').html('&Eacute;tage'); 
     // http://stackoverflow.com/questions/7344220/jquery-selector-contains-to-equals 

     $("#list option[value=2]").text(); 

     $("#list option:selected").each(function() { 
      alert($(this).text()); 
     }); 

     $("#list").change(function() { 
      alert($(this).find("option:selected").text()+' clicked!'); 
     }); 

     */ 

    </script> 

</body> 
</html> 

Próbowałem zmienić nazwę funkcji prototypu, na wypadek, gdyby kolidowało ono z dowolnym prototypem jquery, ale to wcale nie pomaga.

+0

Możliwa dupe http://stackoverflow.com/questions/1827458/prototyping-object-in-javascript-breaks-jquery – zsong

+0

Twoje pytanie zostało wystarczająco odpowiedział. Przyjmij proszę jedną z odpowiedzi :) – benekastah

Odpowiedz

6

Ponieważ spowoduje to dodanie elementu przeliczalnego do każdego obiektu. Sizzle (którego używa jQuery) używa literałów obiektowych do skonfigurowania ich analizy selektora. Kiedy zapętla te obiekty konfiguracyjne, aby uzyskać wszystkie tokeny, nie oczekuje twojej funkcji. W tym przypadku prawdopodobnie próbuje użyć twojej funkcji jako RegExp.

Wyobraźmy sobie następujący scenariusz:

var obj = { a: 1, b: 2, c: 3 }; 
var result = 0; 
for (var prop in obj) { 
    // On one of these iterations, `prop` will be "allKeys". 
    // In this case, `obj[prop]` will be a function instead of a number. 
    result += obj[prop] * 2; 
} 
console.log(result); 

Jeśli dodałeś niczego do prototypu Object „s, które nie mogą być wykorzystane jako liczbę, otrzymasz NaN dla swojego wyniku.

Dobrym rozwiązaniem tego problemu jest dodanie funkcji allKeys do Object zamiast Object.prototype. Ten naśladuje Object.keys:

Object.allKeys = function (obj) { 
    var keys = []; 
    for (var key in obj) 
    { 
     // Very important to check for dictionary.hasOwnProperty(key) 
     // otherwise you may end up with methods from the prototype chain.. 
     if (obj.hasOwnProperty(key)) 
     { 
      keys.push(key); 
      //alert(key); 
     } // End if (dict.hasOwnProperty(key)) 

    } // Next key 

    keys.sort(); 
    return keys; 
}; // End Extension Function allKeys 
+1

Mam zamiar zrobić to za pomocą [systemu] (http://stackoverflow.com/users/1917390/the-system). Nie jest dobrym pomysłem dodawanie właściwości do natywnego prototypu. jQuery nie łamie funkcji prototypowych tak bardzo jak implementacja javascript dla pętli 'for ... in' jest głupia. – benekastah

+1

Tak, bo w pętlach są głupkowate, zdałem sobie z tego sprawę, kiedy napisałem ten kod. To jest nawiasem mówiąc, dlaczego wymyśliłem metodę allKeys. No tak, jeśli inni po prostu nie sprawdzają hasOwnProperty, to naprawdę nie jest to dobry pomysł. Daję ci uprowadzenie i przyjmij odpowiedź :) –

3

Ponieważ jQuery nie zapełniać swój kod z kontrolami .hasOwnProperty() gdy wyliczanie obiektów.

W tym celu należy umieścić osłonę przed złymi metodami kodowania. Zamiast ważenia kodu w celu dostosowania się do takich praktyk, wymagają one, aby ich użytkownicy stosowali się do dobrych praktyk, np. Nigdy nie stawiając przeliczalnych właściwości na Object.prototype.

Innymi słowy ... Nie dodawaj właściwości przeliczalny na Object.prototype chyba że chcesz cały swój kod do uruchomienia strażników wobec tych właściwości, a ty nigdy chcą wyliczyć odziedziczone właściwości.


FWIW, jeśli naprawdę chcesz wywołać metody na zwykłym obiektów, wystarczy dokonać konstruktora tak, że masz pośrednią obiekt prototypowy, które mogą być bezpiecznie przedłużony.

function O(o) { 
    if (!(this instanceof O)) 
     return new O(o) 
    for (var p in o) 
     this[p] = o[p] 
} 

O.prototype.allKeys = function() { 
    // ... 
}; 

Teraz tworzenia przedmiotów tak:

var foo = O({ 
    foo: "foo", 
    bar: "bar", 
    baz: "baz" 
}); 

... i Object.prototype pozostaje nietknięte, więc zwykłe obiekty są nadal bezpieczne. Po wyliczeniu obiektów O wystarczy użyć osłony .hasOwnProperty().

for (var p in foo) { 
    if (foo.hasOwnProperty(p)) { 
     // do stuff 
    } 
} 

iw odniesieniu do danych JSON jest analizowany, można użyć funkcji Reviver zamienić na równinie obiektów z obiektem O.

var parsed = JSON.parse(jsondata, function(k, v) { 
    if (v && typeof v === "object" && !(v instanceof Array)) { 
     return O(v) 
    } 
    return v 
}); 
+0

Dobrze, ale typ mojej wyliczalnej tablicy asocjacyjnej wytworzonej przez JSON wydaje się być obiektem. –

+0

@Quandary: Nie jestem pewien co masz na myśli. Kiedy sprawisz, że wszystkie obiekty w środowisku odziedziczą właściwość * (co dzieje się, gdy rozszerzysz 'Object.prototype') *, wtedy wszystkie iteracje dziedziczonych właściwości będą na nią oddziaływać. –

+0

Tak, to jest sens prototypu. Ale zasadniczo mówisz, że muszę napisać metodę statyczną, przekazać obiekt jako zmienną do niej, a następnie przyjąć złe praktyki, tylko dlatego, że jQuery stosuje złe praktyki kodowania, a nie odwrotnie. –

3

Możesz być w stanie przezwyciężyć ten efekt uboczny przy użyciu defineProperty który pozwala na ustawienie deskryptorów.

Object.defineProperty(Object.prototype, 'propName', {value: 'your value', enumerable: false}); 
+0

Należy zauważyć, że jest to tylko w nowoczesnych przeglądarkach. –

+0

Prawda. Wprowadzono w JavaScript 1.8.5. – marekful

+0

Gdyby tylko to działało wszędzie .... Takie miłe rozwiązanie. – benekastah

Powiązane problemy