持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
很多公司招聘要求上会写着 “熟悉各主流平台差异、兼容性优化解决方案等”,今天的这个惰性模式就是可以用来进行兼容性优化的。
什么是兼容性问题呢?通俗的说就是,某个方法A,在这个浏览器上存在,在另一个浏览器上就不存在或者说是不叫这个名字而是叫方法B,这样就会导致同样一段代码,在这个浏览器上可以运行,到另一个浏览器上就报错,这就是兼容性问题。
那我们再来思考一下,换做是你,你会如何解决兼容性问题呢?
假设现在有一个方法M,存在document对象上,然后有3种浏览器A、B、C,然后方法M在A上就叫M,在浏览器B中叫BM,在浏览器C中叫CM,接着让你写一段代码,保证能在3种浏览器上都实现方法M的功能。
按照平时的思路,大家首先想到的肯定是使用 if-else 来解决这个兼容性问题吧,就像下面的代码一样:
function comp() {
// 如果document对象上存在M方法,说明是在A浏览器中
if (document.M) {
document.M() // 直接执行对应方法
} else if (document.BM) {
document.BM() // B浏览器中
} else {
document.CM() // C浏览器中
}
}
上面这段代码确实实现了我们的需求,保证了ABC3种浏览器都可以实现方法M的功能,但是有什么不足的地方呢?
我们来分析一下,每次执行 comp 函数的时候,都会对当前浏览器环境进行判断,然后再根据判断结果去执行对应的方法,那么有没有可能,让 comp 函数记住当前浏览器环境,下次我们再执行 comp 函数的时候它无需再对浏览器环境进行判断,而是直接执行符合当前浏览器环境的方法呢?
这种方法就是 惰性模式,也叫机器学习。惰性模式的精髓在于下面这句话
惰性模式精髓
既然第一次执行的时候已经判断过了,而且以后再执行是不必要的,那么就在第一次执行后重新定义它吧。
惰性模式有 两种 实现方式
- 使用闭包,在文件加载进来 的时候通过闭包执行该方法,并对其重新定义;
- 在1的基础上,做一次 延迟执行,该为在函数第一次调用时对其重定义;
第一种方法的不足之处在于 会使得页面在加载的时候就占用一定的资源。 而第二种方式则是将这种资源消耗进行 惰性推移,在第一次执行函数的时候才进行,进而减少了文件加载时的资源消耗。
就像吃个云吞,要花7块钱,首先你要明确的一点就是,这7块钱你是无论如何都要给的了,然后现在有两种交易方式,一种是吃完就给钱,一种是下个月再给钱。 不要小看第二种方式,这种惰性推移有时候是很有必要的,当我们重定义会花费的资源比较多而又要不影响页面显示的时候,惰性推移的必要性就显示出来了。
加载时执行重定义
// 添加绑定事件on
A.on = function (dom, type, fn) {
// 如果支持addEventListener
if (document.addEventListener) {
// 返回重新定义的方法
return function (dom, type, fn) {
dom.addEventListener(type, fn, false)
}
}
// 如果支持attachEvent (IE浏览器)
else if (document.attachEvent) {
return function (dom, type, fn) {
dom.attachEvent('on' + type, fn)
}
} else {
// 返回新定义方法
return function (dom, type, fn) {
dom['on' + type] = fn
}
}
}(); // 加载时执行
假设上面的代码在一个 demo.js 中,然后将 A.on 方法重定义需要花费8秒钟时间,那么使用第一种方法带来的副作用就是:当页面加载到 demo.js 的时候,因为加载js文件是会阻塞页面的,而重定义 A.on 方法需要花费8秒钟,所以页面被阻塞了8秒,有可能会白屏8秒。但是好处是,加载完毕之后,你再执行A.on事件,将不再损失资源了。
惰性执行
// 添加绑定事件on
A.on = function (dom, type, fn) {
// 如果支持addEventListener
if (document.addEventListener) {
A.on = function (dom, type, fn) {
dom.addEventListener(type, fn)
}
}
// 如果支持attachEvent (IE浏览器)
else if (document.attachEvent) {
A.on = function (dom, type, fn) {
dom.attachEvent('on' + type, fn)
}
// 如果支持DOM0级的事件绑定
} else {
A.on = function (dom, type, fn) {
dom['on' + type] = fn
}
}
// 执行重定义on方法
A.on(dom, type, fn)
}
假设上面的代码在一个 demo.js 中,然后将 A.on 方法重定义需要花费8秒钟时间,那么使用第二种方法带来的副作用就是:当页面加载到 demo.js 的时候,页面不会阻塞,但是当我们第一次需要调用 A.on 方法的时候,会花费8秒时间对 A.on 方法进行重定义,然后再真正执行。也就是把资源的损失推迟到了第一次调用时。
以上就是两种实现惰性模式方法了,各有优劣,根据使用场景进行取舍。
会遇到兼容性问题的API有
document.addEventListener事件- 创建XHR对象
XMLHttpRequest - 获取区域元素的CSS样式 IE中是currentStyle,标准浏览器、国内浏览器则各有各的方法
上面是常见的兼容性问题,大家可以试试用惰性模式来优化一下解决方法。