js更优雅的兼容_javascript技巧
问题种种做底层接口兼容,无非就是利用if,判断客户端支持哪个接口的问题。最著名的例子就是事件:
var addEvent = function(e, what, how) {
if (e.addEventListener) e.addEventListener(what, how, false)
else if (e.attachEvent) e.attachEvent('on' + what, how)
}
这里考虑了给元素绑定事件时可能遇到的两种状况——标准的W3C DOM接口以及DHTML提供的接口。当然这个例子还很粗糙,但足够说明问题了。
原先的方法是在兼容层调用有现场判断并进入相应的if分支。很显然,这种“现场判断”的方法效率并不高。后来,人们采用这样的办法:
if (MSIE) {
addEvent = function(e, what, how) {
e.attachEvent('on' + what, how);
}
} else {
addEvent = function(e, what, how) {
e.addEventListener(what, how);
}
}
在一次判断后给addEvent绑定不同的代码,从而免去了运行时的分支判断。
很可惜,这个问题也不小。首先把“采用attachEvent”和“客户端是MSIE”绑定在一起是个很过时的想法。假如微软哪天良心发现了怎么办?这事情现在就发生了——IE9明确支持了DOM接口,甚至DOM3都支持。结果,就这个“良心发现”的举动会毁掉许多前端库,他们必须被迫修改代码(如同IE8来时那样)。况且这种做法没有考虑“未知的客户端”——据我所知,Google发布Chrome后也导致不少类库重写代码。
特性检测那究竟该怎么做?特性检测就可以最大限度地避免“新客户端”带来的麻烦——通过一组在类库初始化时定义的代码来检测客户端拥有的特性,并利用这一组检测值绑定类库代码:
var supportsAddEventListener = !!(checkerElement.addEventListener);
if (supportsAddEventListener) {
addEvent = function(e, what, how) {
e.addEventListener(what, how);
}
} else if (supportsAttachEvent) {
addEvent = function(e, what, how) {
e.attachEvent('on' + what, how);
}
}
特性检测实际上是将“使用某个客户端”和“支持某个特性”进行解耦——让if分支直接针对“特性有无”(接口是否一致)判断,从而消除客户端制造商“良心发现”造成的“好心办坏事”。事实上这么做也是符合历史潮流之选——当标准接口逐渐普及,客户端之间渐渐“表征一致”时,为什么不做个一致的兼容层接口呢?
跌落让我们重新看看这些代码。通常,一条利用特性检测进行兼容的代码往往是这样:
if (new_interface_detected) {
comp = function() {uses_new_interface};
} else if (old_interface_detected) {
comp = function() {uses_old_interface};
} else {
throw new Error('Unadaptable!')
}
换言之,过程是:
如果客户端支持新接口,就将兼容层绑定到新接口上
否则,如果客户端支持老接口/不一致接口,就将兼容层绑定到老接口上
否则,如果可以的话,给出错误回馈
亦即,兼容层程序是从高空“掉”下来,如果客户端支持“高级”特性(新接口、标准接口)就将它“接住”——兼容层就有了归宿;否则继续向下掉——哦,老接口接住了,就用老接口;如果一直没人接住,于是——啪——摔倒了地上,并且用最后一口气喊一声:“你用的客户端太小众,我拿你没办法了!”
这和什么比较像?事实上,如果你了解JavaScript对象系统的机理,你就可以类比:这不就是原型嘛!原型系统就是利用了这种跌落——寻找某个成员,如果它在这个对象里定义了,就返回之;否则沿着原型链向上搜(没错,这次是向上的),如此重复,直到真的连原型链都到头的时候,返回个undefined。
说做就做!这里同样用addEvent为例。首先,我们定义一个空驱动,它里面什么都不包含:
var nullDriver = {}然后,就是创建个对象,并且把原型链指向它。在ECMA V5时代,我们可以用Object.create,可惜,现在还有N多老客户端(否则做什么兼容啊),所以自己craft个函数:
var derive = Object.create ? Object.create: function() {
var T = function() {};
return function(obj) {
T.prototype = obj;
return new T
}
}()
这个用法你可能会觉得很诡异,但它工作起来一点问题没有,速度也不慢——能达到Object.create的一半。我们就用这个derive开动:
var dhtmlDriver = derive(nullDriver);
var dhtmlDriverBugfix = derive(dhtmlDriver);这里的bugfix是针对一些“bug”和特殊情况定义的特别Driver。这里你可以忽略它。好了,DHTML里面addEvent是什么来着?
if (supportsAttachEvent) {
dhtmlDriver.addEvent = function(e, what, how) {
e.attachEvent('on' + what, how)
}
}
然后呢?位于原型链最前端的应该是W3C的标准驱动啊,写上!
var w3cDriver = derive(dhtmlDriverBugfix);
var w3cDriverBugfix = derive(w3cDriver);
if (supportsAddEventListener) {
w3cDriver.addEvent = function(e, what, how) {
e.addEventListener(what, how)
}
}
最后,我们就放个东西上去做最后调用的接口。(因为w3cDriverBugfix太难看……)
var driver = derive(w3cDriverBugfix);
然后就调用好了。看,这就让那些长得吓人的分支判断变得简单有效,但不失fallback本色:在支持addEventListener上调用addEvent等价于调用w3cDriver.addEvent,而在不支持addEventListener的客户端上就会跌落到底下,比如调用dhtmlDriver.addEvent。另外,进行bugfix也很容易——可以在专门的“bugfix”层进行hook,而原有层丝毫不受影响。
等等,继承这么多层会很慢么?诚然,那么深的原型链肯定会慢,不过我有办法。还记得给对象的属性写入时会发生什么事情吗?
var ego = function(x) {return x}
for (var each in driver) {
if (! (each in nullDriver)) {
driver[each] = ego(driver[each])
}
}
没错,原来高企在原型链上面的方法会“哗”的一下掉到最下面!这回不用沿着原型链向上搜了,直接从最底端获取属性即可。这里用ego函数的原因是防止一些浏览器“优化掉”这里的代码。
总结虽然这里谈兼容,可是,它的精华却在语言特性上——利用原型继承,我们可以很优雅地完成这个令人头疼的操作。是的,框架的美感不应该只在外表,其内部——即使是最最令人烦的内部——也同样要优雅。
这里的技术可以在dess中找到。

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

很多用户都会遇到在操作电脑的时候卡顿或者是蓝屏,这个时候我们就需要找一个最为稳定的win10版本来进行操作,整体都是非常的好用的,可以让你日常使用更为流畅。史上最稳定的win10版本1、win10正版原装系统在这里用户可以使用简单的操作,系统经过优化,具有很强的稳定性、安全性、兼容性,用户可以按照步骤实现完美机器2、俄罗斯大神精简版win10经过严格的精简操作,删除了很多不必要的功能和服务。精简后,系统的CPU和内存占用率更低,运行速度更快。3、win10精简版1909安装多台不同硬件型号的电脑

众所周知,win11一大特色就是自带安卓子系统,让我们不需要用模拟器就可以安装安卓软件,但也存在win11安卓应用卡顿的问题,这应该怎么解决呢。win11不兼容动态壁纸么:答:win11能兼容动态壁纸,如果用不了可能是软件或系统版本落后。如果是刚更新,可能是被系统壁纸覆盖了。1、如果是系统或软件版本落后,那么更新一下系统和动态壁纸软件即可。2、如果是被系统壁纸覆盖了,可以尝试打开“设置”3、接着进入“个性化”下的“背景”设置。4、然后将个性化设置背景改为“图片”5、修改完成后就能正常设置动态壁纸

Switch2是任天堂在2023年科隆游戏展公布的新机型,一些玩家担心新出来的机型与之前版本的机型卡带会不会存在兼容性问题,下面我们一起来看看吧。switch2兼容switch卡带吗答:switch2不兼容switch卡带。Switch2卡带的介绍根据任天堂的生产链公司的消息称,Switch2可能会使用64GB的卡带。它由于性能更好,支撑更多的3A游戏大作的原因,需要更大卡带容量。因为很多的游戏作品需要阉割和压缩,才能塞进一张游戏卡带里面。而且Switch的卡带容易被复制游戏内容,所以更换新卡带

近年来,Android系统在移动设备领域的普及率迅速增长,许多人开始关注在其他平台上是否也能运行安卓应用程序。Linux作为一种常见的操作系统,受到不少人的青睐,那么问题来了,Linux系统是否兼容安卓软件呢?首先要明确的是,Linux系统与Android系统在内核上有一定的相似性,都是基于Linux内核的操作系统,因此从理论上来讲,Linux系统是可以运行

d3dx9_43.dll不兼容怎么解决近年来,计算机和游戏的快速发展使得我们享受到了更多的娱乐和便利。然而,有时在安装或运行某些程序时,我们可能会遇到一些错误信息,例如“d3dx9_43.dll不兼容”。在这种情况下,我们应该如何解决这个问题呢?首先,让我们来了解一下这个错误信息的含义。d3dx9_43.dll是DirectX的一个系统文件,它是用于在操作系

有不少网站用户们在浏览的时候都会需要使用旧版本的浏览器才能够使用,而新版本的需要添加兼容网点才可以进行使用,所以今天就给你们带来了win11添加兼容网点详细教程,快来学习一下吧。win11如何添加兼容网点1、首先打开系统中的IE浏览器,点击浏览器右上角小齿轮进入“设置”。2、然后在打开的设置菜单中,打开“兼容性视图设置”。3、在添加此网站下的编辑框中输入要添加的网址,然后点击“添加”。4、最后在下方就可以看到添加网址的域名了,关闭窗口即可直接访问。

遇到Eclipse版本不兼容怎么办?【引言】Eclipse作为一款广受欢迎的开发工具,不仅提供了强大的功能和丰富的插件,还兼具跨平台的特性。然而,在使用Eclipse过程中,有时可能会遇到版本不兼容的问题,这可能导致程序无法正常运行或者编译错误。本文将介绍如何应对Eclipse版本不兼容的情况,并提供一些代码示例来解决常见的兼容性问题。【解决方案】更新Ecl

很多朋友目前还在选择win7系统而不是win10系统的原因就是害怕其兼容性不好。其实,现在win10系统已经可以设置win7的兼容模式了,只需要在属性中就可以更改设置,下面就一起来看一下吧。win10怎么兼容win71、首先右键点击我们需要在win7系统下运行的程序,然后打开“属性”2、然后点击上方的“兼容性”,进入兼容性选项卡。3、在兼容模式下勾选“以兼容模式运行这个程序”4、然后就可以在下方的下拉菜单中选择“windows7”5、完了之后只需要点击“应用”或者点击“确定”就可以了。
