现代化脚本有哪些可以用于新的ECMAScript 5函数?

16

ECMAScript 5增加了不少好的特性。 John Resig在这里有一个很好的概述。 这是一个ECMAScript 5兼容性表

对于那些还没有支持这些函数的浏览器,很多功能都可以“模拟”。你知道有哪些脚本可以实现这一点吗?我特别感兴趣的是Object.create。

例如,Douglas Crockford的JSON脚本会在创建函数之前检查JSON函数是否存在。

如果还有像JSON这样的东西,我们在需要使用新功能时就可以把它们包含进来。


请参见https://dev59.com/pUrSa4cB1Zd3GeqPZMiS#2916063。 - Sean McMillan
4个回答

40

Crockford推荐使用这种Object.create的垫片:

if (typeof Object.create != "function") {
  Object.create = function (o) {
    function F(){}
    F.prototype = o;
    return new F;
  };
}

但是请不要这样做

这种方法的问题在于ES5的Object.create有一个签名含有两个参数:第一个是继承自的对象,第二个(可选)是一个表示要添加到新创建的对象中的属性(或者说描述符)的对象。

Object.create(O[, Properties]); // see 15.2.3.5, ECMA-262 5th ed.

我们所拥有的是一个实现不一致且存在两种不同行为的问题。在拥有本地 Object.create 的环境中,该方法知道如何处理第二个参数;而在没有本地 Object.create 的环境中,它则无法处理。

这会有什么实际影响呢?

嗯,如果有一些代码(比如第三方脚本)想要使用 Object.create,那么代码通常会这样做:

if (Object.create) {
  var child = Object.create(parent, properties);
}

基本上假设Object.create存在,它必须符合规范 - 接受第二个参数并将相应的属性添加到对象中。

但是,使用上述补丁时,第二个参数被简单地忽略了。甚至没有任何指示表明出现了不同的情况。可以说是无声的失败 - 这种情况很难检测和修复。

我们能做得更好吗?

实际上,只使用(标准)ES3设施是不可能创建一个完全符合规范的Object.create补丁。最好的解决方案是创建一个自定义包装方法。

然而,还有一些其他的(次优的)方法可供尝试:

1)通知用户无法使用第二个参数工作

if (!Object.create) {
  Object.create = function (o) {
    if (arguments.length > 1) { 
      throw Error('second argument is not supported'); 
    }
    // ... proceed ...
  };
}

2) 尝试处理第二个参数:

if (!Object.create) {
  Object.create = function (parent, properties) {
    function F(){}
    F.prototype = parent;
    var obj = new F;
    if (properties) {
      // ... augment obj ...
    }
    return obj;
  }; 
}
请注意,“properties”是表示属性描述符的对象,而不仅仅是属性名称/值,并且这是一些不太容易支持的内容(有些事情甚至是不可能的,例如控制属性的可枚举性):
Object.create(parent, {
  foo: {
    value: 'bar',
    writable: true
  },
  baz: {
    get: function(){ return 'baz getter'; },
    set: function(value){ return 'baz setter'; },
    enumerable: true
  }
});

原始的垫片中存在另一个不一致之处,即它未考虑父级对象为null的情况。

var foo = Object.create(null);

这将创建一个 null 原型的对象;换句话说,该对象不继承任何东西,甚至不继承 Object.prototype(ECMAScript中所有原生对象的继承者)。

foo.toString; // undefined
foo.constructor; // undefined
// etc.

顺便说一下,这对于在ECMAScript中创建“proper”哈希表是有用的。

虽然可以使用非标准扩展(例如“magical”__proto__属性)来模拟此行为,但只能如此(因此实现可能不太可移植或健壮)。解决此问题的方法类似:要么完全模拟ES5实现,要么通知存在不一致/失败。


1
我的Xccessors JavaScript库提供了Object.definePropertyObject.defineProperties函数,您可以用于解决方案#2。 - Eli Grey
10
非常好的发现,你对 ES5 的了解显然很扎实,但我认为值得指出的是,Crockford 的代码比 ES5 早得多:第一个版本大约在2006年左右,最新版本早在2008年初。因此,暗示他推荐这种解决方案来模拟 ES5 有点不诚实。 - neonski
7
我上次看到Doug谈论这个垫片是3个月前,链接是http://www.slideshare.net/douglascrockford/javascript-the-good-parts-3292746/36。这大约是在ES5正式标准化之后4个月(即2009年12月),肯定晚于2008年。但我不想责备任何人(如果有这种感觉,很抱歉)。我们都会犯错误。重点是推荐不采用这种方法并解释原因。 - kangax
那么你推荐这里提到的解决方案,还是完全不同的其他东西? - Bob Spryn
@Bob Spryn,方案1或2都比原始方案更好。您还可以为方法使用不同的名称(例如SomeLibrary.object.create),只要在不同环境中不存在不一致的行为即可。 - kangax
显示剩余4条评论

8

3

ES5 - JavaScript/EcmaScript 5 in 3 是在BitBucket上分享的一个集合。特别是Object.create是一个容易“伪造”的方法,由Crockford等人广泛使用,但这里由Justin Love改进,专注于许多ES5部分。


0

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