【问题标题】:Get original definition of redefined function获取重新定义函数的原始定义
【发布时间】:2012-02-08 13:15:14
【问题描述】:

我正在编写一个在页面上执行某些功能的小书签。作为此功能的一部分,我需要使用getElementsByClassName。但是,在测试过程中,我发现有几个网站已经将getElementsByClassName 重新定义为自定义方法。据推测,这样做是为了在所有浏览器中支持getElementsByClassName

自定义getElementsByClassName 的实现有点草率,在我的几个用例中都失败了。有什么办法可以得到getElementsByClassName的原始定义?

在 chrome javascript 控制台中:getElementsByClassName 指向本机函数。既然getElementsByClassName已经被重新定义了,有没有办法访问这个原生函数?

【问题讨论】:

  • 您可以预先缓存它,或者仅在本地支持不存在时更改这些脚本以添加支持(大多数人应该这样做),但是一旦它被覆盖,您就无法取回它。
  • @davin 不幸的是,我不拥有/控制页面或其脚本。
  • 就像我说的,我正在编写一个书签,而不是嵌入在页面中的脚本。所以我无法修改重新定义 getElementsByClassName 的函数,这就是为什么我正在寻找其他访问它的方式。

标签: javascript


【解决方案1】:

有一个解决方案,但它并不可靠。请参阅底部的免责声明。

如果您打开一个新窗口,您将可以访问其未修改的方法。我可以想到两种打开新窗口的方法:使用window.open 和在 iframe 中。 iframe 不那么突兀,因为它不会通过打开新的浏览器窗口或触发弹出窗口阻止程序来分散用户的注意力。

// Runs fn with a clean window reference
function getCleanReferences(fn) {
    var iframe = document.createElement('iframe');
    iframe.style.display = 'none';
    document.body.appendChild(iframe);
    var newWindow = iframe.contentWindow;
    iframe.parentNode.removeChild(iframe);
    return fn(newWindow);
}

有些浏览器不喜欢移除 iframe。如果您希望它在 Safari 或 Firefox 中工作,请删除 removeChild 行。接下来,您将要从新窗口中获取 getElementsByClassName 方法。

var nativeGetElementsByClassName = getCleanReferences(function(newWindow) {
    var getElementsByClassName = newWindow.document.getElementsByClassName;
    return function(className) {
        return getElementsByClassName.call(window.document, className);
    };
});

这几乎是跨浏览器的,除了 IE 不喜欢在 quirks 模式或版本 9 之前的 getElementsByClassName(您在书签中几乎无法控制)。这是一个 jsfiddle:http://jsfiddle.net/theazureshadow/vdqYG/

如果你对我导入其他方法的实验感到好奇,请查看这个 jsfiddle:http://jsfiddle.net/theazureshadow/ccFG3/

免责声明:在当前窗口的上下文中使用来自其他窗口的方法是危险的、未定义的领域。如果您选择使用这种脆弱的方法,请确保测试跨浏览器兼容性。我的测试显示了不同浏览器如何处理这种方法的主要差异。您不应该在生产站点上使用它,但对于个人书签可能没问题。仅包含您自己想要使用的任何方法的版本可能更安全。

【讨论】:

    【解决方案2】:

    我认为这是不可能的。这就是猴子补丁不好的原因。永远不应该重新定义宿主对象。

    【讨论】:

      【解决方案3】:

      我猜有些网站使用原型。 DOM 的 document.getElementsByClassName() 返回 NodeList 而 Prototype 的 document.getElementsByClassName() 返回 Array,因为 NodeLists 不能由 JS 脚本创建。

      在 Firefox 中你可以使用

      var node = document;
      Components.lookupMethod(node, 'getElementsByClassName').call(node, /* className */);
      

      获取原始方法。也许谷歌浏览器实现了类似的东西,否则你会不走运。我找不到任何东西(谷歌搜索 2 分钟)。

      在这种情况下,您可以使用以下内容:

      function getElementsByClassName(node, className) {
          var rv = new Array();
          var nodeList = node.getElementsByTagName('*');
          className = className.replace(/([\.\\\\\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:\-])/, '\\$1');
          var regex = new RegExp('(?:^|[\\n\\r ])' + className + '(?:[\\n\\r ]|$)');
          for(var length = nodeList.length, i = 0; i < length; ++i) {
              if(regex.test(nodeList[ i ].className)) {
                  rv.push(nodeList[ i ]);
              }
          }
          return rv;
      }
      

      如果 Google Chrome 实现了node.classList,您可以使用以下不使用令人毛骨悚然的正则表达式的函数:

      function getElementsByClassName(node, className) {
          var rv = new Array();
          var nodeList = node.getElementsByTagName('*');
          for(var length = nodeList.length, i = 0; i < length; ++i) {
              if(nodeList[ i ].classList.contains(className)) {
                  rv.push(nodeList[ i ]);
              }
          }
          return rv;
      }
      

      此函数遍历给定节点内的所有元素。它返回一个类似于 Prototype 的 Array 并具有相同的缺点:数组不像 NodeLists 那样“实时”。

      【讨论】:

      • 我最终做了类似于你的第二个解决方案的事情。第三种解决方案并不适合我,因为我需要一个 NodeList。目前正在尝试查找是否有 Firefox 的 lookupMethod 的 webkit 等效方法。谢谢!
      【解决方案4】:

      这取决于自定义方法是如何定义的。您可以以Element.prototype.getElementsByClassName 的身份访问该方法,但如果页面覆盖了此原型方法,我认为没有办法将其取回。

      【讨论】:

        猜你喜欢
        • 2018-08-14
        • 1970-01-01
        • 2019-10-06
        • 1970-01-01
        • 2020-09-16
        • 1970-01-01
        • 2011-02-08
        • 2023-03-12
        • 1970-01-01
        相关资源
        最近更新 更多