2013-02-18 13 views
6

Domanda:Perché questa funzione del prototipo JavaScript interrompe jQuery?

Non appena aggiungo il codice qui sotto alla mia pagina html, ottengo:

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

Questo è il prototipo che causa l'errore:

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 

e questo è il codice minimo richiesto per riprodurre l'errore (Browser in questione: 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> 

Ho provato a rinominare la funzione prototipo, nel caso in cui fosse in conflitto con qualsiasi prototipo di jquery, ma questo non aiuta affatto.

+0

possibile vittima http://stackoverflow.com/questions/1827458/prototyping-object-in-javascript-breaks-jquery – zsong

+0

tua domanda è stata sufficientemente risposto. Sentitevi liberi di accettare una delle risposte :) – benekastah

risposta

6

Perché questo sta per aggiungere un oggetto enumerabile a ogni singolo oggetto. Sizzle (che utilizza jQuery) utilizza i letterali degli oggetti per configurare l'analisi del selettore. Quando esegue il ciclo di questi oggetti di configurazione per ottenere tutti i token, non si aspetta la tua funzione. In questo caso, probabilmente sta cercando di usare la tua funzione come RegExp.

Immaginate questo scenario:

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); 

Se è stato aggiunto nulla al Object s' prototipo che non può essere usato come un numero, si otterrà NaN per il vostro risultato.

Una buona soluzione a questo problema è aggiungere la funzione allKeys a Object anziché Object.prototype. Questo imita 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

Ho intenzione di con [il sistema] (http://stackoverflow.com/users/1917390/the-system) su questo. È una cattiva idea aggiungere proprietà a qualsiasi prototipo nativo. jQuery non interrompe le funzioni del prototipo quanto l'implementazione di javascript di 'for ... in' loop è stupida. – benekastah

+1

Sì, perché i loop sono stupidi, ho capito che quando ho scritto questo codice. È per questo motivo che mi è venuto in mente il metodo AllKeys. Beh sì, se altri semplicemente non controllano hasOwnProperty, allora non è davvero una buona idea. Ti sto dando un upvote e accetto la risposta :) –

3

Poiché jQuery non impantanarsi il suo codice con gli assegni per .hasOwnProperty() durante l'enumerazione degli oggetti.

Per fare ciò, è necessario posizionare le guardie contro le cattive pratiche di codifica. Piuttosto che appesantire il loro codice per adattarsi a tali pratiche, richiedono che i loro utenti aderiscano alle buone pratiche, come non mettere mai le proprietà enumerabili su Object.prototype.

In altre parole ... Non aggiungere le proprietà enumerabili a Object.prototype meno che non vogliate tutto il codice per eseguire protezioni contro tali proprietà, e si mai vuole enumerare le proprietà ereditate.


FWIW, se si vuole veramente chiamare metodi su oggetti semplici, basta fare un costruttore in modo da avere un oggetto prototipo intermedio che può essere estesa in modo sicuro.

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

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

Ora si crea gli oggetti in questo modo:

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

... e il Object.prototype rimane intatta, in modo da oggetti semplici sono ancora al sicuro. Dovrai solo usare la protezione .hasOwnProperty() quando si enumerano gli oggetti O.

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

E per quanto riguarda i dati JSON essere analizzato, è possibile utilizzare una funzione rianimatore di scambiare gli oggetti semplici con il O oggetto.

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

Bene, ma il tipo del mio array associativo enumerabile prodotto tramite JSON sembra essere oggetto. –

+0

@Quandary: non sono sicuro di cosa intendi. Quando si fa in modo che tutti gli oggetti nell'ambiente ereditino una proprietà * (che è ciò che accade quando si estrae 'Object.prototype') *, allora tutte le iterazioni delle proprietà ereditate entreranno in esso. –

+0

Sì, questo è il senso del prototipo. Ma quello che stai essenzialmente dicendo è che devo scrivere un metodo statico, passare l'oggetto come variabile ad esso, e quindi adottare le cattive pratiche, solo perché jQuery usa cattive pratiche di codifica, e non viceversa. –

3

Si può essere in grado di superare questo effetto collaterale utilizzando defineProperty che consente di impostare i descrittori.

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

Si dovrebbe sottolineare che questo è solo nei browser moderni. –

+0

Vero. Introdotto in JavaScript 1.8.5. – marekful

+0

Se solo questo funzionasse ovunque ... Una soluzione così bella. – benekastah

Problemi correlati