资讯专栏INFORMATION COLUMN

惰性函数实现高性能能力检测

tianhang / 2217人阅读

摘要:在这种惰性判断里,我们利用了函数一等公民的身份和立即执行的便利,进行了初始判断,随后通过语句进行变量的重赋值,进而达到了性能上的提高。

在SPA、各种MV*框架如日中天,前后端分离、工程化大行其道的现如今来看,能力检测似乎已经不那么重要,因为大多产品已经不再需要去兼容老旧的IE,或者说是更多偏向移动端的开发,虽然一些兼容的坑依旧存在,但是通过层出不穷的自动化工具,我们已经不必事事躬亲了。然而今天要向大家分享的则是关于事件兼容性的能力检测,可能看上去没什么大的用场,却是我们理解惰性函数的好途径。

常规的检测方式

先看一则常见的事件监听封装

function addEvent(ele, type, cb, isCap) {
    
    if (window.addEventListener) {
        ele.addEventListener(type, cb, isCap);
    } else if (window.attachEvent) {
        ele.attachEvent(type, function () {
            var args            = [].prototype.slice(0);
            window.event[target] = window.event.srcElement;
            args.unshift(window.event);
            cb.apply(this, args);
        });
    } else {
        ele["on" + type] = cb;
    }

}

var body = document.body;

addEvent(body,"load",function () {
    console.log("onload") // onload
});

addEvent(body,"click",function () {
    console.log("onclick") // onclick
});

可以看的到,这是一个有着足够兼容性的的事件方法,能够应付常见的需求和场面。

只是细细想来,当我们将这个方法复用了很多次后,会发现每次复用都会重新进行一次能力检测,以便符合当前宿主环境。可能你会想,对于浏览器性能过剩的现如今来说,这点性能损耗并不算什么。

那么不妨做个假设,当我们在做一个高频操作,例如window.onresize的监听,鼠标随便轻轻一拖,就可能会触发无数次事件的重载。

面临这种状况,常规情况下,有经验的开发者大致会做两种处理来改善性能。

函数节流,将方法限频,过滤一些线性重复性的操作。这方面,在loadash和underscore都有相应的方法。日后我也会分享出原生的实现方式。

从源头找起,把不必要的能力检测去除,我们只在页面加载时检测一次不好吗?

惰性检测方式

函数节流我们日后再分享,那么,要如何把不必要的能力检测去除掉呢?下面有两种实现方式,代码如下:

//使用惰性函数


var addEvent = (function () {
    //立即执行    
    if (window.addEventListener) {
        return function (ele, type, cb, isCap) {
            ele.addEventListener(type, cb, isCap);
        }
    } else if (window.attachEvent) {
        return function (ele, type, cb) {
            ele.attachEvent(type, function () {
                var args             = [].prototype.slice(0);
                window.event[target] = window.event.srcElement;
                args.unshift(window.event);
                cb.apply(this, args);
            });
        }
    } else {
        return function (ele, type, cb) {
            ele["on" + type] = cb;
        }

    //初始判断过后,addEvent就无需再次判断,已经是正确的封装。
    }
}());

代码变化并不大,关键点在于,我们用到了立即执行函数,即方法在初始化完毕,便立即判断,判断后,返回适合当前场景的方法。

//常见的方法调用方式
function getName() {
    console.log("ives")
}

getName() // ives;
//立即执行 可以是声明函数 也可以是匿名函数 两种方式均可
(function getName() {
    console.log("ives") // ives
}());

在常见的高阶函数实践中,我们时常会接受函数,对函数的参数或者是上下文环境进行一些改造,并返回。在这种惰性判断里,我们利用了函数一等公民的身份和立即执行的便利,进行了初始判断,随后通过return语句进行变量的重赋值,进而达到了性能上的提高。

实际上,很多常用的检测,比如AJAX的封装、获取实际的CSS样式等,都可以使用惰性函数处理。

重载检测方式

然而,这种方法也并非是完美的,我们假设另外一种场景,即是在静态HTML中,我们引用的某个JS文件中有这个方法,但是这个HTML页面并没有事件性质的交互,那么我们可以基于吹毛求疵,鸡蛋里挑骨头的角度来断言,这次惰性加载是失败的,无用的。因为我们浪费了系统的资源,我们没有事件,你为什么要加载一次?

总而言之,惰性加载虽然性能有提升,却仍然不是最好的处理,只是有利于我们对于惰性函数的理解。

什么情景才是最完美的呢?即是初次调用时才做第一次判断,随后,无需判断,方法也很简单,只要简单的改动几行代码。

依旧是以事件为例,代码如下:

var addEvent = function (ele,type,cb,isCap) {
    //取消了立即执行 初次调用时进行方法重载
    if (window.addEventListener) {
        addEvent = function (ele, type, cb, isCap) {
            ele.addEventListener(type, cb, isCap);
        }
    } else if (window.attachEvent) {
        addEvent = function (ele, type, cb) {
            ele.attachEvent(type, function () {
                var args             = [].prototype.slice(0);
                window.event[target] = window.event.srcElement;
                args.unshift(window.event);
                cb.apply(this, args);
            });
        }
    } else {
        addEvent = function (ele, type, cb) {
            ele["on" + type] = cb;
        }
    }
    //初次调用时,手动执行重载的方法
    addEvent(ele,type,cb,isCap);
};

可以看到实现的方式异常简单,通过对方法的重载,并在初始执行时手动执行重载后的方法,满足了我们上述的条件。

结束语

可能大家会说,你个标题党,最后一种重载的方式才是相对完美的方式,今天又为什么要讲惰性函数呢?无外,抛砖引玉罢了,在钻研设计模式时,是绝对离不开闭包和惰性函数的,今天则算是小小的热身。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/89398.html

相关文章

  • 谈谈JS中的高级函数

    摘要:作用域安全的构造函数会首先确认对象是正确类型的实例,然后再进行更改,如下这样就避免了在全局对象上意外更改或设置属性。在多人协作的项目中,为了避免他们误改了全局对象,也应使用作用域安全的构造函数。 博客原文地址:Claiyre的个人博客如需转载,请在文章开头注明原文地址 在JavaScript中,函数的功能十分强大。它们是第一类对象,也可以作为另一个对象的方法,还可以作为参数传入另一个函...

    raoyi 评论0 收藏0
  • 精读《V8 引擎 Lazy Parsing》

    摘要:在执行函数时,通过保存堆栈状态,再保存堆栈跳出后返回位置的指针,最后对变量赋值。这看上去没有问题,只要将值存在堆栈就搞定了。 1. 引言 本周精读的文章是 V8 引擎 Lazy Parsing,看看 V8 引擎为了优化性能,做了怎样的尝试吧! 这篇文章介绍的优化技术叫 preparser,是通过跳过不必要函数编译的方式优化性能。 2. 概述 & 精读 解析 Js 发生在网页运行的关键路...

    罗志环 评论0 收藏0
  • JavaScript 函数式编程到底是个啥

    摘要:函数是一等公民。其实闭包本身也是函数式编程的一个应用。劣势不能算是严格意义上的函数式语言,很多函数式编程的特性并没有。 随着大前端时代的到来,在产品开发过程中,前端所占业务比重越来越大、交互越来越重。传统的老夫拿起JQuery就是一把梭应付当下重交互页面已经十分乏力。于是乎有了Angular,React,Vue这些现代框架。 但随之而来的还有大量的新知识新名词,如MVC,MVVM,Fl...

    denson 评论0 收藏0
  • JavaScript 函数式编程到底是个啥

    摘要:函数是一等公民。其实闭包本身也是函数式编程的一个应用。劣势不能算是严格意义上的函数式语言,很多函数式编程的特性并没有。 随着大前端时代的到来,在产品开发过程中,前端所占业务比重越来越大、交互越来越重。传统的老夫拿起JQuery就是一把梭应付当下重交互页面已经十分乏力。于是乎有了Angular,React,Vue这些现代框架。 但随之而来的还有大量的新知识新名词,如MVC,MVVM,Fl...

    Aomine 评论0 收藏0
  • 让Python中类的属性具有惰性求值的能力

    摘要:更多描述可见文档这种惰性求值的方法在很多模块中都会使用,比如中的使用上与例子一致,如表单中的讨论在大部分情况下,让属性具有惰性求值能力的全部意义就在于提升程序性能。当不需要这个属性时就能避免进行无意义的计算,同时又能阻止该属性重复进行计算。 起步 我们希望将一个只读的属性定义为 property 属性方法,只有在访问它时才进行计算,但是,又希望把计算出的值缓存起来,不要每次访问它时都重...

    NervosNetwork 评论0 收藏0

发表评论

0条评论

阅读需要支付1元查看
<