【问题标题】:Why does this JavaScript prototype function break jQuery?为什么这个 JavaScript 原型函数会破坏 jQuery?
【发布时间】:2013-02-03 04:47:39
【问题描述】:

问题:

只要将以下代码添加到我的 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 原型冲突,但这根本没有帮助。

【问题讨论】:

标签: javascript jquery xhtml


【解决方案1】:

因为 jQuery 在枚举对象时不会通过检查 .hasOwnProperty() 来阻止其代码。

这样做是为了防止不良编码实践。他们要求用户遵守良好的做法,而不是权衡他们的代码以适应这种做法,例如永远不要在Object.prototype 上放置可枚举的属性。

换句话说...不要向Object.prototype 添加可枚举属性,除非您希望所有代码都针对这些属性运行防护,并且您从不想要枚举继承的属性。 p>


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 保持不变,因此普通对象仍然是安全的。在枚举 O 对象时,您只需要使用 .hasOwnProperty() 保护即可。

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

【讨论】:

  • 很好,但是我通过 JSON 生成的可枚举关联数组的类型似乎是对象。
  • @Quandary:我不确定你的意思。当你让环境中的所有对象都继承一个属性(当你扩展Object.prototype时会发生这种情况),那么所有继承属性的迭代都会碰到它。
  • 没错,就是原型的意义。但是您本质上是说,我必须编写一个静态方法,将对象作为变量传递给它,然后采用不好的做法,只是因为 jQuery 使用了不好的编码做法,而不是相反。
  • @Quandary:将对象传递给函数到底是怎么回事?如果期望所有代码被要求测试每个对象的每个枚举的每个属性以查看该属性是否直接在对象上,您会如何认为这是一件好事?采用有效消除继承枚举的可能性的做法是一件好事,这可能非常有用?所有这些问题都可以通过采用良好实践来解决,例如不让环境中的所有对象都继承可枚举的属性。
【解决方案2】:

因为这将为每个对象添加一个可枚举项。 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 的原型中添加了不能用作数字的任何内容,您的结果将得到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

【讨论】:

  • 我将与the system 一起讨论这个问题。向任何原生原型添加属性是个坏主意。 jQuery 并没有破坏原型函数,因为 javascript 的 for...in 循环实现很愚蠢。
  • 是的,for in 循环很愚蠢,我在编写这段代码时意识到这一点。这就是我想出 allKeys 方法的原因。是的,如果其他人根本不检查 hasOwnProperty,那么这确实不是一个好主意。我给你一个赞成票并接受答案:)
【解决方案3】:

您可以通过使用允许设置描述符的defineProperty 来克服这种副作用。

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

【讨论】:

  • 是的。在 JavaScript 1.8.5 中引入。
  • +1 - 不错的发现。然而,就我而言,如果我只使用较新的浏览器,我无论如何都可以使用 object.keys 函数。但我不只使用较新的浏览器 [不是我不想] :)
猜你喜欢
  • 1970-01-01
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-16
  • 1970-01-01
  • 1970-01-01
  • 2021-09-09
相关资源
最近更新 更多