2013-02-18 2 views
6

Вопрос:Почему функция JavaScript-прототипа ломает jQuery?

Как только я добавляю ниже код на HTML-страницу, я получаю:

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

Это прототип, который вызывает ошибку:

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 

И это минимальный код, необходимый для воспроизведения ошибки (браузер, о котором идет речь: 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> 

Я попытался переименовать функцию прототипа, на всякий случай, если он конфликтует с любым прототипом jquery, но это совсем не помогает.

+0

Возможно боян http://stackoverflow.com/questions/1827458/prototyping-object-in-javascript-breaks-jquery – zsong

+0

Ваш вопрос достаточно ответил. Не стесняйтесь принять один из ответов :) – benekastah

ответ

6

Потому что это добавит перечислимый элемент к каждому объекту. Sizzle (который использует jQuery) использует объектные литералы для настройки синтаксического анализа селектора. Когда он заполняет эти объекты конфигурации, чтобы получить все токены, он не ожидает вашей функции. В этом случае, вероятно, вы пытаетесь использовать свою функцию как RegExp.

Представьте себе такой сценарий:

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

Если вы добавили ничего Object «s прототип, который не может быть использован в качестве номера, вы получите NaN для вашего результата.

Хорошим решением этой проблемы является добавление функции allKeys к Object вместо Object.prototype. Это имитирует 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

Я собираюсь с [системой] (http://stackoverflow.com/users/1917390/the-system) на этом. Плохая идея добавить свойства к любому натурному прототипу. jQuery не нарушает функции прототипа так же, как реализация javascript для циклов 'for ... in' - goofy. – benekastah

+1

Да, поскольку в цикле тупой, я понял, что когда я написал этот код. Это, кстати, почему я придумал метод allKeys. Ну да, если другие просто не проверяют hasOwnProperty, тогда это действительно не очень хорошая идея. Я даю вам преимущество и принимаю ответ :) –

3

Поскольку Jquery не увязнуть свой код с проверками на .hasOwnProperty() при перечислении объектов.

Для этого необходимо разместить защитников от плохой практики кодирования. Вместо того, чтобы взвешивать свой код для обеспечения такой практики, они требуют, чтобы их пользователи придерживались хороших практик, например, никогда не добавляя перечислимые свойства на Object.prototype.

Другими словами ... Не добавлять перечисляемые свойства Object.prototype, если вы не хотите весь ваш код для запуска охранников против этих свойств, и вы никогда не хотите перечислить наследуемые свойства.


FWIW, если вы действительно хотите, чтобы вызвать методы на простых объектах, просто сделать конструктор, так что у вас есть промежуточный объект-прототип, который может быть безопасно продлен.

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

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

Теперь вы создаете свои объекты, как это:

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

... и Object.prototype остается нетронутым, так что простые объекты по-прежнему безопасны. Вам нужно будет использовать защитный код .hasOwnProperty() при перечислении объектов O.

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

И по отношению к данным JSON разбираемый, вы можете использовать функцию Reviver обменивать на простые объекты с O объекта.

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

Хорошо, но тип моего перечислимого ассоциативного массива, созданного через JSON, кажется, является объектом. –

+0

@ Quandary: Я не уверен, что вы имеете в виду. Когда вы вызываете все объекты в среде, чтобы наследовать свойство * (это то, что происходит при расширении «Object.prototype») *, тогда все итерации унаследованных свойств будут возникать в нем. –

+0

Да, это смысл прототипа. Но вы, по сути, говорите, что мне приходится писать статический метод, передавать объект как переменную в него и принимать плохие методы, только потому, что jQuery использует неправильные методы кодирования, а не наоборот. –

3

Вы можете быть в состоянии преодолеть этот побочный эффект, используя defineProperty, который позволяет устанавливать дескрипторы.

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

Следует отметить, что это только в современных браузерах. –

+0

Правда. Представлено в JavaScript 1.8.5. – marekful

+0

Если бы это работало везде ... Такое приятное решение. – benekastah

Смежные вопросы