Question:
As soon as I add the below code to my html page, I get:
Line: 4
Error: Object doesn't support the property or method "exec".
This is the prototype that causes the bug:
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
And this is the minimum code required to reproduce the error (Browser in question: 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('É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>
I tried renaming the prototype function, just in case it conflicts with any jquery prototype, but that doesn't help at all.
Because this is going to add an enumerable item to every single object. Sizzle (which jQuery uses) uses object literals to configure their selector parsing. When it loops these config objects to get all tokens, it doesn't expect your function. In this case, it's probably trying to use your function as a RegExp
.
Imagine this 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);
If you have added anything to Object
's prototype that can't be used as a number, you will get NaN
for your result.
A good solution to this problem is to add the allKeys
function to Object
instead of Object.prototype
. This mimics 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
You may be able to overcome this side-effect by using defineProperty
which allows for setting descriptors.
Object.defineProperty(Object.prototype, 'propName', {value: 'your value', enumerable: false});
Because jQuery doesn't bog down its code with checks for .hasOwnProperty()
when enumerating objects.
To do so, is to place guards against bad coding practices. Rather than weigh down their code to accommodate such practices, they require that their users adhere to good practices, like never putting enumerable properties on Object.prototype
.
In other words... Don't add enumerable properties to Object.prototype
unless you want all your code to run guards against those properties, and you never want to enumerate inherited properties.
FWIW, if you really want to call methods on plain objects, just make a constructor so that you have an intermediate prototype object that can be safely extended.
function O(o) {
if (!(this instanceof O))
return new O(o)
for (var p in o)
this[p] = o[p]
}
O.prototype.allKeys = function() {
// ...
};
Now you create your objects like this:
var foo = O({
foo: "foo",
bar: "bar",
baz: "baz"
});
...and the Object.prototype
remains untouched, so plain objects are still safe. You'll just need to use the .hasOwnProperty()
guard when enumerating your O
objects.
for (var p in foo) {
if (foo.hasOwnProperty(p)) {
// do stuff
}
}
And with respect to JSON data being parsed, you can use a reviver function to swap out the plain objects with your O
object.
var parsed = JSON.parse(jsondata, function(k, v) {
if (v && typeof v === "object" && !(v instanceof Array)) {
return O(v)
}
return v
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With