绕过IE8损坏的Object.defineProperty实现方式

26

考虑以下代码,使用ECMAScript5的Object.defineProperty特性:

var sayHi = function(){ alert('hi'); };
var defineProperty = (typeof Object.defineProperty == 'function');
if (defineProperty) Object.defineProperty(Array.prototype,'sayHi',{value:sayHi});
else Array.prototype.sayHi = sayHi;
var a = [];
a.sayHi();

这适用于Chrome和Firefox 4(其中defineProperty存在),也适用于Firefox 3.6(其中defineProperty不存在)。然而,IE8 仅部分支持defineProperty。结果,它尝试运行Object.defineProperty方法,但会失败(浏览器中不显示任何错误),并停止运行页面上的所有其他JavaScript代码。

是否有更好的方法来检测和避免IE8的破损实现,而不是:

if (defineProperty){
  try{ Object.defineProperty(Array.prototype,'sayHi',{value:sayHi}); }catch(e){};
}
if (!Array.prototype.sayHi) Array.prototype.sayHi = sayHi;

对于好奇的人,我在我的ArraySetMath库中使用它来定义非枚举数组方法,以便在支持这一特性的浏览器中使用,对于旧浏览器则回退到枚举方法。

5个回答

23

我认为直接使用try/catch进行特征测试是没有比这更好的方法了。实际上,这正是IE团队在最近发布的有关转换到ES5 API的文章中推荐的方法。

你可以缩短测试的代码,只需像这样使用Object.defineProperty({}, 'x', {})(而不是使用Array.prototype),但这只是一个小问题;使用你的示例测试确切的功能(因此误检的可能性更小)。


7

0
Array.prototype.sayHi = function(){ alert('hi'); };

try {
  Object.defineProperty(Array.prototype, 'sayHi', {
    value: Array.prototype.sayHi
  });
}
catch(e){};

0

我遇到了同样的问题(即IE 8中的Object.defineProperty仅为DOM,而不是其他浏览器的完整实现),但这是用于polyfill的。

无论如何,最终我使用了一个“特性”检查来查看是否在使用IE,虽然它并不完美,但它在我能做的所有测试中都有效:

if (Object.defineProperty && !document.all && document.addEventListener) {
    Object.defineProperty(Array.prototype,'sayHi',{value:sayHi});
} else {
    Array.prototype.sayHi = sayHi;
}

由于IE <= 8没有document.addEventListener,而document.all是微软对W3C标准的专有扩展。这两个检查等同于检查IE是否为8或以下版本。


0

我之前也遇到过这个问题。在我看来,使用try...catch语句太过激烈。
更高效的方法是使用条件编译:

/*@cc_on@if(@_jscript_version>5.8)if(document.documentMode>8)@*/
Object.defineProperty && Object.defineProperty(Array.prototype,'sayHi',{value:sayHi});
/*@end@*/ 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接