扩展原生对象与 es5-safe 模块

标签: Programming | 发表时间:2011-08-10 15:59 | 作者:lifesinger cRabdanceR
出处:http://lifesinger.wordpress.com

扩展原生对象很邪恶吗

kangax 发表了一篇博文:Extending built-in native objects. Evil or not?, 很好地总结了扩展 JavaScript 内置原生对象的优劣:

首先要区分原生对象(Native Object, 比如 String/Array 等)和宿主对象(Host Object, 比如 DOM 等)。

扩展 Host Object 是邪恶的,很难实现,即便实现了也会有很多隐患,具体请参阅:What’s wrong with extending the DOM. 可以说对 DOM 对象进行扩展就注定了 Prototype.js 的没落(DOM extension is one of the biggest mistakes Prototype.js has ever done)。相比而言,jQuery 的作者 John Resig 没有选择 Protype.js 的老路,而是采用了 wrap 的方式,在 DOM 之上封装一套 API, 并且做到简洁易用,这是 jQuery 能兴起并流行至今的重要因素之一。

对 Native Object 进行扩展并不邪恶。kangax 的博文已详述,不多说。

扩展 Native Object 的难点在于,要实现与规范完全兼容的代码很难(writing proper, compliant shims is hard)。市面上流行的一些扩展方法,比如 Douglas Crockford 的 extend 方法,以及各个类库里的一些语言扩充,还有最近的一个项目 es5-shim 等,这些代码的实现都存在一些问题:Incorrent ES5 fallbacks.

如果要提供规范之外的一些方法,推荐采用 Underscore 的方式,以工具集的方式提供。这样能尽量避免冲突,并保持 API 的一致性。

es5-shim

很认同 hax 在 乱想 一文中关于 ES5 标准的一段话:

如果要产品化,特别是通用化,我认为考虑标准是及其重要的。这个标准,不仅是指现在已经有的纸面标准,而是要考虑标准的方向。

比方说过去大量的js库都是建立在一个小的方法集上的。但是新的库就应该注重base在ES5标准上。因为这是方向。不仅是纸面标准,而且也一定会是事实标准。在JS生存10年之后,我们必须认清楚,现在是一个跨越,就是开发者的baseline即将或已经提升到了ES5(比如nodejs上的开发社区),而不是之前残疾的ES3。

(此处省略 n 字,hax 知道…)

当然ES5这个baseline,是需要建立的,这就需要有库能把legacy浏览器弥补好。现在这个方向做的最好的是es5-shim。但是说实话,它真的还不够好。这块是有机会的,如果国内js高手们能联合起来,专注于这一个方向上做出世界级的库,绝不是天方夜谭。

es5-shim 开了一个很好的头,但就如 hax 所说,它真的不够好,目前的版本有以下不足:

  1. 有些方法,比如 Object.seal, 在老旧浏览器上很难甚至不可能实现。es5-shim 的策略是:fail silently. 就是说:让你调用,但不干活。这个策略在 es5-shim 的代码上随处可见,悲催呀。我期望的策略是:倘若无法实现某些特性,就爽快的抛出异常,让开发者自己去解决。
  2. 缺少测试。这两天作者补充了一些,但还非常欠缺。
  3. 代码实现上,太拘泥规范。比如 Function.prototype.bind 的实现,直接按照规范来一步步写代码,结果并不好,bugs 反而不少,还有些无效代码,比如给 bound.length 赋值。

总之,es5-shim 的理想是丰满的,但现实是骨感的。不少方法想完全按照规范来实现,但由于浏览器自身的限制,又无法完全实现,纠结中让使用者更纠结,隐患不少。

es5-safe

邮件给 es5-shim 的作者 kriskowal, 建议用 throw error 的策略来代替 fail silently. 但彼此很难说服,于是有了 es5-safe 项目:

https://github.com/seajs/dew/tree/master/src/es5-safe

es5-safe 模块里,仅扩展了可以较好实现的可以安全使用的部分方法,包括:

Function.prototype.bind
Object.create
Object.keys
Array.isArray
Array.prototype.forEach
Array.prototype.map
Array.prototype.filter
Array.prototype.every
Array.prototype.some
Array.prototype.reduce
Array.prototype.reduceRight
Array.prototype.indexOf
Array.prototype.lastIndexOf
String.prototype.trim
Date.now

大都是较好实现的“软柿子”,但也是最常用的方法。其中:

Function.prototype.bind
Object.create

是部分实现,比如 bind 返回的方法,会多出 prototype 属性,以及方法的 length 值不对。Object.create 方法不支持第二个参数,当你传入第二个参数时,会抛出错误。

采用 es5-safe 的好处是,可以放心大胆的使用,如果用错了,会收到 exception, 这样有助于将问题在开发或测试阶段快速解决掉。

此外,es5-safe 的代码质量很高。我这几天闲散时间全耗在这上面了,代码是我认为目前同类代码里质量最高的。单元测试直接来自 google V8 引擎,能有效保障代码功能的正确性。

目前在国内的主流浏览器(IE6-9, Chrome, Firefox, Safari, Opera)上均测试通过:

http://seajs.github.com/dew/src/es5-safe/test/runner.html

注意:并不完美

在三体世界里,完美的十维空间坍塌到现在的三维,整个世界就已经不完美了,连光速都是有限的-.- 我相信,绝大部分情况下,es5-safe.js 已够用,虽然离完美还有很远的距离。

这种不完美,主要体现在以下方面:

1. 规范本身的不完美。比如:

[].every(function(item) { return typeof item !== 'undefined'; } );

按照常理来看,应该返回 false. 但在当前所有浏览器下,均返回 true.

2. 浏览器对实现的限制。比如 Object.seal/frozen 的实现依赖浏览器自身的功能,在 Old IE 下,这是不可实现的任务。

3. 浏览器自身实现的差异性。比如 0 in [undefined]

  • 在原生 IE6-8 里,返回 false
  • 在原生 IE9 里,包括各种兼容模式下,返回 true
  • 非 IE 浏览器里,返回 true

这导致 Array.prototype.reduce 等方法,在操作对象为 [1,2,undefined,4] 这种含有 undefined 值的数组时,在原生 IE6-8 下的结果,与其他浏览器会有差异。

这种差异还有不少,大部分情况下不会遇到,但一旦遇到了,经常要找半天才能定位出原因。

小结

扩展 DOM 等 Host Object 是罪恶的,至少目前如此。

扩展原生 JavaScript 对象远没有想象中的糟糕。挑选一个合适的,比如 es5-safe.js, 让 es5 成为 baseline, 无论工作效率,还是心情,都是值当的。

面向未来开发!


相关 [扩展 对象 es5] 推荐:

扩展原生对象与 es5-safe 模块

- cRabdanceR - 岁月如歌
kangax 发表了一篇博文:Extending built-in native objects. Evil or not?, 很好地总结了扩展 JavaScript 内置原生对象的优劣:. 首先要区分原生对象(Native Object, 比如 String/Array 等)和宿主对象(Host Object, 比如 DOM 等).

数据库性能优化、统计信息与对象统计信息概述收集、扩展统计信息、dbms_stats.get_prefs

- - CSDN博客数据库推荐文章
      1.基于成本的Oracle优化法则.       2.Oracle性能诊断艺术.       3.基于Oracle的SQL优化.      基于cost 更大适应性/灵活性/10g开始.      基于规则 制定了15条/10g以前. 1.系统统计信息       . 2.数据库对象统计信息 .

Chrome Remote Desktop 扩展

- Larry Li - Wow! Ubuntu
Google 发布了一款重量级的 Chrome 扩展,名为 Chrome Remote Desktop,通过它你可直接在 Chrome/Chromium 浏览器上远程访问其它人的电脑,或共享自已的电脑让别人远程遥控. Chrome Remote Desktop 目前还处于 Beta 阶段,它是一款跨平台软件,完全支持 Windows, Linux, Mac 和 Chromebooks,使用条件是你必须具备一个 Google 帐号.

腾讯CMEM的PHP扩展

- duyue - 平凡的世界
最近公司在做相关的业务,由于Memcached协议缺少返回码,为了保证业务数据的安全性,不得已只好自己写个扩展来实现需求. 基于memcache扩展的2.2.6的稳定版开发而来. 代码已经开源,有需要的朋友请拿走,License是PHP License,请自觉遵守. 项目主页:http://code.google.com/p/cmem/.

ibus的GNOME Shell扩展

- 小汐 - 画猫不成反类虎
更新代码,支持横排模式(设置后需要重启gnome shell). 这个扩展使用了ibus的一些新API,该API还没有发布,需要自行编译ibus的git仓库代码. 以前说过GNOME3的一个缺点,在通知栏聊天时,无法看到ibus的候选词界面,确切地说是候选词界面被通知栏聊天界面挡住了. 这是GNOME Shell的架构所决定的,目前没有方法可以绕过(除非修改GNOME Shell)本身.

Chrome+Firefox 扩展推荐

- - 笨兔兔
Adblock – 阻击网页广告. Google Mail Checker – Gmail 收件箱提示. Turn Off The Lights –观看网页视频时候”关灯”. Google Chrome to Phone –将链接或信息直接发送到Android 手机上. IE Tab –Chrome 下使用IE风格显示网页.

javascript对象转json

- - JavaScript - Web前端 - ITeye博客
把javascript对象转成json. 已有 0 人发表留言,猛击->> 这里<<-参与讨论. —软件人才免语言低担保 赴美带薪读研.

Chrome迷介绍扩展汇总,2010年的108枚扩展

- yifan - Chrome迷
感谢 Android 的帮忙整理. Chrome迷曾在2011年6月19日就整理过《2011年1月-6月的Chrome扩展大汇总》. 特别感谢 Android 的 Washington 同学,后面的68个由 Washington 同学贡献整理(忙到22点才吃晚饭). Chrome迷致力于向广大Chrome用户提供好玩、有趣、实用、效率的Chrome扩展.

快速禁用某个或全部 Chrome 扩展的 Chrome 扩展

- wyman - 谷奥——探寻谷歌的奥秘
也许你已经安装了太多太多的扩展了,而有些扩展可能临时才会用一下,其实不用的时候不如把他们禁用掉,省点内存,也省点屏幕空间. 禁用的好处就是你可以随时在需要的时候启用它,比直接卸载强. Disable All Extensions Plus这枚Chrome扩展就是干这个的,点击它的图标可以看到所有已安装的扩展,想禁用哪个直接点它名字就好了,或者直接禁用全部.

Context扩展:神奇切换扩展组合

- guoan - Chrome迷
打网游的同学一定熟悉这个:物品快捷操作栏位置有限,但是东西又太多,所以一定会出现类似这样的分组:. 按一下按钮,就可以快速切换,让有限的空间承载数倍的功能性. 对于 Chrome 的某些死变态用户来说,扩展栏里有十多二十个扩展一点都不稀奇(我朋友就是. 那这类用户有没有类似网游快捷物品栏的需求呢. Context 就是这么一个扩展,让你设置好扩展的组合,然后快速一键切换:.