admin管理员组

文章数量:1297013

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('&Eacute;tage');
        // 

        $("#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.

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('&Eacute;tage');
        // http://stackoverflow./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.

Share Improve this question edited Feb 18, 2013 at 17:13 Pointy 414k62 gold badges594 silver badges627 bronze badges asked Feb 18, 2013 at 17:11 Stefan SteigerStefan Steiger 82.4k70 gold badges400 silver badges457 bronze badges 2
  • Possible dupe stackoverflow./questions/1827458/… – zs2020 Commented Feb 18, 2013 at 17:20
  • Your question has been sufficiently answered. Feel free to accept one of the answers :) – benekastah Commented Feb 18, 2013 at 17:59
Add a ment  | 

3 Answers 3

Reset to default 6

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 overe 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 acmodate 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
});

本文标签: Why does this JavaScript prototype function break jQueryStack Overflow